Skip to content

Commit e75af39

Browse files
committed
Accumulated updates for Github Action / python package usage
1 parent 44d4c07 commit e75af39

File tree

7 files changed

+182
-65
lines changed

7 files changed

+182
-65
lines changed

README.md

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,128 @@
1-
# yaml to ics calendar
1+
# YAML to ics calendar
22

3-
This repository turns yaml files into icl calendars (importable into
3+
This repository turns yaml files into .ics calendars (importable into
44
different programs) using
55
[yaml2ics](https://github.com/scientific-python/yaml2ics) and GitHub
6-
actions.
6+
actions. Or, you can use git-calendar directly.
77

88
This repository provides a working template for the action to build +
9-
publish to Github Pages, and some minimal HTML index pages. All of
10-
the important logic is in
9+
publish to Github Pages, including files into other files, and some
10+
minimal HTML index pages. All of the important logic is in
1111
[yaml2ics](https://github.com/scientific-python/yaml2ics), so if you
1212
want to roll your own site and build system (which isn't hard) for the
1313
.ics files, consider using yaml2ics directly.
1414

15-
This is sort of alpha: it works and is used, but code editing or
16-
asking for clarifications is probably needed. Documentation should be
17-
improved.
1815

1916

20-
21-
## Example
17+
## Example deployments
2218

2319
The CodeRefinery calendar
2420
* [Built calendar site](https://coderefinery.github.io/calendar/)
25-
(main landing page for most projects)
21+
(the auto-generated landing page)
2622
* [Source repository](https://github.com/coderefinery/calendar)
2723
* Optional: [Calendars inserted into
2824
website](https://coderefinery.org/calendars/) ([source](https://github.com/coderefinery/coderefinery.org/blob/main/content/calendars.md))
2925

3026

3127

32-
## Usage
28+
## General principles
3329

34-
To use, look at
35-
[git-calendar-template](https://github.com/coderefinery/git-calendar-template)
36-
for a sample repository that uses this. Generate your own repository
37-
from that template and go from there.
30+
One defines events in YAML, and they get built to `.ics` files which
31+
can be served as a static site. These can then be imported to various
32+
calendar clients.
3833

39-
This repository can also be installed as a Python pip package.
34+
There's a command line program, `git-calendar`, which takes YAML files
35+
as input (one of which can be named `_config.yaml`). The last
36+
argument is the output directory to which to write.
4037

41-
Usage in short:
38+
A typical repository layout:
4239

4340
- `calendars/*.yaml` contains the input calendars.
44-
- `./build.sh` builds the outputs. Edit this script as
45-
needed, or copy the command to your own build script.
46-
- `out/index.html` gets build and serves as a landing page
47-
- Calendars get build to `out/*.ics`
48-
- The Github Actions workflow file deploys `out/` to the `gh-pages`
49-
branch.
50-
51-
- **First time deployment note:** you have to go to settings and
52-
toggle pages off and on again the first time to enable Github Pages,
53-
after that Github Actions will automatically deploy.
41+
- `output/` is the web root for web deployment
42+
- `output/*.ics` is the build calendars
43+
- `output/index.html` gets built and serves as a landing page with
44+
links to the .ics files
5445

5546
For now, see `calendars/example.yaml` for an example, or any of the
5647
test calendars in yaml2ics. This documentation should be improved.
5748

5849

5950

51+
## Usage - Github action
52+
53+
This can be used as Github action (see EXAMPLE REPO for the full file).
54+
55+
```yaml
56+
- uses: actions/checkout@v4
57+
- name: git-calendar action
58+
uses: coderefinery/git-calendar@action-test
59+
with:
60+
input_dir: calendars # the default, not required
61+
output_dir: output # the default, not required
62+
index_file: index.html # the default, set to '' to not
63+
create
64+
htmlbody_file: body.html # Only the part inside <body> for inclusion in other files
65+
```
66+
67+
This action will automatically deploy to Github Pages. You can set
68+
`with: pages_deploy: false` to disable this. The action will also
69+
setup Python and install this Python library. If you already set up
70+
Python, it may be easier to code up your own action than use this.
71+
72+
73+
74+
## Usage - Raw Github action
75+
76+
If you want to program the action yourself, below is the base. You
77+
probably can probably figure out how to integrate this to a Github
78+
Pages deployment and all.
79+
```yaml
80+
- name: build
81+
shell: bash
82+
run: |
83+
git-calendar calendars/*.yaml output/ --index=index.html --edit-link=https://github.com/$GITHUB_REPOSITORY
84+
```
85+
86+
87+
88+
## Usage - command line
89+
90+
This is a Python package, install as normal (yes, in a virtual environment, etc...):
91+
```console
92+
$ pip install git-calendar
93+
```
94+
95+
Then run:
96+
97+
```console
98+
$ git-calendar calendars/*.yaml output/ --index=index.html --edit-link=https://github.com/$GITHUB_REPOSITORY
99+
```
100+
101+
There are more options, not currently documented.
102+
103+
104+
105+
## Usage - via raw yaml2ics
106+
107+
Don't forget this is just a wrapper around
108+
[yaml2ics](https://github.com/scientific-python/yaml2ics) - if you
109+
only need a .ics file and not index.html, including calendars in
110+
others, etc., then maybe that is your starting point:
111+
112+
```console
113+
$ python yaml2ics.py example/test_calendar.yaml example/another_calendar.yaml > out.ics
114+
```
115+
116+
117+
60118
## Status
61119

62-
Alpha, it works but may require code changes still to get around
63-
corner cases.
120+
Beta as of 2025, it works but may require code changes still to get
121+
around corner cases. As of 2025 issues will probably be responded to.
64122

65123

66124

67-
## See Also
125+
## See also
68126

69127
* This directly uses https://github.com/scientific-python/yaml2ics as
70128
the yaml to ical generator - if you want to create your own build

action.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# CC-0 (public domain)
2+
# but please link to the source so that people can more easily reuse
3+
# (if relevant)
4+
5+
name: git-calendar
6+
description: Manage multiple branches in gh-pages
7+
8+
inputs:
9+
calendars:
10+
description: 'Input calendar directory (default calendars, globs *.yaml from there)'
11+
default: 'calendars/*.yaml'
12+
output_dir:
13+
description: 'Output directory (default output/)'
14+
default: 'output/'
15+
extra_options:
16+
description: 'extra options to git-calendar build. Example: --timezone=Europe/Helsinki --timezone=Europe/Stockholm'
17+
default: ''
18+
index_file:
19+
default: index.html
20+
html_body_file:
21+
default: body.html
22+
pages_deploy:
23+
description: "Deploy to GitHub pages. Use only if you don't have another pages deployment. Default: true."
24+
default: true
25+
26+
runs:
27+
using: "composite"
28+
steps:
29+
- name: Set up Python
30+
uses: actions/setup-python@v5
31+
with:
32+
python-version: '3.x'
33+
- name: Install dependencies
34+
shell: bash
35+
run: |
36+
#pip install https://github.com/coderefinery/git-calendar/archives/main.zip
37+
pip install ${{ github.action_path }}
38+
- name: build
39+
shell: bash
40+
run: |
41+
git-calendar\
42+
${{ inputs.calendars }} \
43+
${{ inputs.output_dir}} \
44+
--edit-link=https://github.com/$GITHUB_REPOSITORY \
45+
${{ inputs.index_file && format('--index={0}', inputs.index_file) }} \
46+
${{ inputs.html_body_file && format('--html-body={0}', inputs.html_body_file) }} \
47+
${{ inputs.extra_options }}
48+
- name: List outputs
49+
shell: bash
50+
run: |
51+
find ${{ inputs.output_dir }} | sort
52+
- name: Upload artifact
53+
uses: actions/upload-pages-artifact@v4
54+
with:
55+
path: ${{ inputs.output_dir }}
56+
- name: Deploy to GitHub Pages
57+
uses: actions/deploy-pages@v4
58+
if: ${{ inputs.pages_deploy }}
59+

build.sh

Lines changed: 0 additions & 4 deletions
This file was deleted.

git_calendar/build.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@
1919
def main(argv=sys.argv[1:]):
2020
parser = argparse.ArgumentParser()
2121
parser.add_argument('inputs', nargs='+', help="input files", type=Path)
22-
parser.add_argument('--output', '-o', help="output directory", type=str)
22+
parser.add_argument('output', help="output directory", type=str)
2323
parser.add_argument('--index', '-i', help="output HTML index file", type=str)
2424
parser.add_argument('--html-body', '-b', help="output HTML body to be included in other pages", type=str)
2525
parser.add_argument('--timezone', action='append', help="zoneinfo timezone names", type=str, default=[])
2626
parser.add_argument('--edit-link', help='Link to edit, will be added to the generated page.')
2727
parser.add_argument('--base-url', help='Base url to append in front of all .ics files '
2828
'(include trailing slash).',
2929
default='')
30+
parser.add_argument('--jinja-template-dir', help='Jinja template dir. See source for a starting point',
31+
action='append', default=[])
3032
args = parser.parse_args(argv)
3133

3234
calendars = [ ]
@@ -53,22 +55,27 @@ def main(argv=sys.argv[1:]):
5355
output = join(args.output, fics)
5456

5557
calendar = yaml2ics.files_to_calendar([f])
58+
print(f"Writing {f} --> {output}")
5659
open(output, 'w').write(calendar.serialize())
5760

5861
# Generate the rendered views in different timezones
5962
for tzdata in timezones:
6063
# text file dump:, may be useful for some people.
6164
# This is clearly a hack, calling the shell commands. This should
6265
# be improved later.
66+
print(f"Writing {f} (in {tzdata['tz']}) --> out/{fics}.{tzdata['tzslug']}.txt")
6367
subprocess.check_output(
64-
fr"TZ={tzdata['tz']} mutt-ics out/{fics} | sed 's/^Subject:/\n\n----------\nSubject:/' > out/{fics}.{tzdata['tzslug']}.txt",
68+
fr"TZ={tzdata['tz']} mutt-ics {args.output}/{fics} | sed 's/^Subject:/\n\n----------\nSubject:/' > {args.output}/{fics}.{tzdata['tzslug']}.txt",
6569
shell=True)
6670
# Convert to an .ics file in different timezones. This shouldn't
6771
# be needed, but it seems that Thunderbird doesn't convert
6872
# timezones so this is useful for it.
6973
calendarTZ = calendar.clone()
7074
calendarTZ.normalize(gettz(tzdata['tz']))
71-
open(join(args.output, fbase+'.'+tzdata['tzslug']+'.ics'), 'w').write(calendarTZ.serialize())
75+
76+
output_tz_txt = join(args.output, fbase+'.'+tzdata['tzslug']+'.ics')
77+
print(f"Writing {f}[{tzdata['tz']}] --> {output_tz_txt}")
78+
open(output_tz_txt, 'w').write(calendarTZ.serialize())
7279

7380

7481

@@ -78,37 +85,34 @@ def main(argv=sys.argv[1:]):
7885
git_hash = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"], encoding='utf8').strip()
7986

8087
env = jinja2.Environment(
81-
loader=jinja2.FileSystemLoader([TEMPLATE_DIR]),
88+
loader=jinja2.FileSystemLoader(args.jinja_template_dir + [TEMPLATE_DIR]),
8289
autoescape=jinja2.select_autoescape(['html', 'xml', '.j2.html',]),
8390
)
8491
env.filters['markdown'] = markdown_it.MarkdownIt().render
85-
86-
if args.index:
87-
template = env.get_template('index.j2.html')
88-
print(f'Writing index to {args.index}', file=sys.stderr)
89-
index = template.render(
92+
template_context = dict(
9093
calendars=calendars,
9194
timezones=timezones,
9295
timestamp=timestamp,
9396
git_hash=git_hash,
9497
edit_link=args.edit_link,
9598
config=config,
9699
)
97-
shutil.copy(TEMPLATE_DIR/'style.css', Path(args.index).parent/'style.css')
98-
open(args.index, 'w').write(index)
100+
101+
if args.index:
102+
index_file = join(args.output, args.index)
103+
print(f'Writing index to {index_file}', file=sys.stderr)
104+
index = env.get_template('index.j2.html').render(template_context)
105+
stylecss = env.get_template('style.css').render(template_context)
106+
with open(index_file, 'w') as f_index, open(join(args.output, 'style.css'), 'w') as f_style:
107+
f_style.write(stylecss)
108+
f_index.write(index)
99109

100110
if args.html_body:
111+
htmlbody_file = join(args.output, args.html_body)
101112
template = env.get_template('body.j2.html')
102-
print(f'Writing HTML to {args.html_body}', file=sys.stderr)
103-
with open(args.html_body, 'w') as f:
104-
html = template.render(
105-
calendars=calendars,
106-
timezones=timezones,
107-
timestamp=timestamp,
108-
git_hash=git_hash,
109-
edit_link=args.edit_link,
110-
config=config,
111-
)
113+
print(f'Writing HTML to {htmlbody_file}', file=sys.stderr)
114+
with open(htmlbody_file, 'w') as f:
115+
html = template.render(template_context)
112116
f.write(html)
113117

114118

git_calendar/templates/body.j2.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ <h2>Available calendars</h2>
2525
)
2626
{%- endif %}
2727
{% if c.data.links -%}
28-
( {%- for href, text in c.data.links %}<a href="{{href}}">{{text}}</a>{% endfor %}{%if not loop.last%}, {% endif -%}
28+
( {%- for href, text in c.data.links %}<a href="{{href}}">{{text}}</a>{%if not loop.last%}, {% endif %}{% endfor -%}
2929
)
3030
{%- endif %}
3131
</li>

git_calendar/yaml2ics.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,9 @@ def files_to_events(files: list, dirname: str = "") -> (ics.Calendar, str):
224224
all_events = []
225225
name = None
226226
processed_files, temporary_files = gather_files(files, dirname=dirname)
227-
print(processed_files)
228-
print(files)
229-
print(dirname)
227+
#print(processed_files)
228+
#print(files)
229+
#print(dirname)
230230
for f in processed_files:
231231
print(f"Processing {f}")
232232
# If it is a raw ICS file

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ license = {file = "LICENSE"}
1010
classifiers = ["License :: OSI Approved :: MIT License"]
1111
dynamic = ["version", "description"]
1212
dependencies = [
13-
#"yaml2ics",
14-
"yaml2ics @ https://github.com/scientific-python/yaml2ics/archive/refs/heads/main.zip",
13+
#"yaml2ics", # now vendored
14+
#"yaml2ics @ https://github.com/scientific-python/yaml2ics/archive/refs/heads/main.zip",
1515
"python-dateutil",
1616
"jinja2",
1717
"mutt-ics",
1818
"pyyaml",
1919
"markdown-it-py",
20-
# TODO: how to install from github here? Then remove this
21-
# from requirements.txt
22-
"ics @ https://github.com/ics-py/ics-py/archive/refs/heads/main.zip",
20+
# There is some new unreleased ics-py feature needed. As of 2025,
21+
# last release was in 2022.
22+
"ics @ https://github.com/ics-py/ics-py/archive/3b7d072.zip", # main probably works too
2323
]
2424

2525
[project.scripts]

0 commit comments

Comments
 (0)