Repository navigation
Expand file tree
/
Copy pathaction.yml
More file actions
205 lines (187 loc) · 7.33 KB
/
Copy pathaction.yml
File metadata and controls
205 lines (187 loc) · 7.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
name: 'Letterboxd Contribution Graph'
description: 'Generate a GitHub-style contribution graph (SVG + JSON) from a Letterboxd film diary'
author: 'nichtlegacy'
branding:
icon: 'film'
color: 'green'
inputs:
username:
description: 'Letterboxd username to generate the graph for'
required: true
years:
description: >-
Years to draw. A list, e.g. "2026,2025", or a span counted back from the
current year, e.g. "last 2" for this year and last. Empty means the
current year. Prefer the span: a pinned list keeps drawing the same two
years after New Year, and nothing about the run looks wrong.
required: false
default: ''
review-years:
description: >-
Years for review cards. "all" creates one card for every year present in
the fetched diary; a list or "last N" selects specific years.
required: false
default: 'all'
week-start:
description: 'First day of the week: "sunday" or "monday"'
required: false
default: 'sunday'
mode:
description: 'Cell coloring: "count" or "rating"'
required: false
default: 'count'
gradient:
description: 'Gradient text: "true" (name and year), "false", "name" or "year"'
required: false
default: 'true'
animate:
description: 'Reveal grid cells with a CSS animation when the SVG loads'
required: false
default: 'true'
scope:
description: >-
Diary scope. "all" fetches the complete diary, which costs one request per
50 entries — a few seconds for a few hundred films, several minutes for a
few thousand. "years" fetches only the years in `years`.
required: false
default: 'all'
month-cards:
description: 'Recent months to also make review cards for, "0" to skip'
required: false
default: '2'
top-films:
description: >-
What the card's film list ranks: "watched" for everything seen in the
period, "released" for only the films released in its year.
required: false
default: 'watched'
export-png:
description: 'Also write PNG versions next to the SVGs'
required: false
default: 'false'
badge-style:
description: 'Badge style: pill, card, dot, flat, flat-square, for-the-badge, plastic'
required: false
default: 'dot'
badge-stats:
description: 'Comma-separated stats for badges: films, rating, streak, days, liked, rewatches'
required: false
default: 'films,rating,streak,days'
output:
description: 'Output path without extension, relative to the repository root'
required: false
default: 'images/github-letterboxd'
node-version:
description: 'Node.js version used to run the generator'
required: false
default: '22.12'
install-browser-deps:
description: 'Install the system libraries the Puppeteer fallback needs (Ubuntu runners only)'
required: false
default: 'true'
commit:
description: 'Commit and push the generated files back to the repository'
required: false
default: 'true'
commit-message:
description: 'Commit message. The branch name and a UTC timestamp are appended.'
required: false
default: 'Update Letterboxd graph'
outputs:
svg-dark:
description: 'Path to the generated dark theme SVG'
value: '${{ inputs.output }}-dark.svg'
svg-light:
description: 'Path to the generated light theme SVG'
value: '${{ inputs.output }}-light.svg'
data-json:
description: 'Path to the generated JSON export'
value: '${{ steps.generate.outputs.data-json }}'
runs:
using: 'composite'
steps:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
# Deliberately no `cache: npm`. Its cache-dependency-path has to point at
# the lockfile inside the action, and `github.action_path` resolves to
# "<workspace>/." for a local `uses: ./`, which setup-node rejects as
# relative pathing. Caching the action's own dependencies is a minor win
# next to shipping a path that breaks for some callers.
- name: Install Puppeteer system dependencies
if: ${{ inputs.install-browser-deps == 'true' && runner.os == 'Linux' }}
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y \
libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2t64 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 \
libxrandr2 libgbm1 libasound2t64 libpango-1.0-0 libcairo2
- name: Install generator dependencies
shell: bash
working-directory: ${{ github.action_path }}
run: npm ci
- name: Install curl_cffi fetcher
shell: bash
run: |
python3 -m pip install --upgrade pip
python3 -m pip install curl_cffi==0.14.0
- name: Generate graph
id: generate
shell: bash
env:
LB_USERNAME: ${{ inputs.username }}
LB_YEARS: ${{ inputs.years }}
LB_REVIEW_YEARS: ${{ inputs.review-years }}
LB_WEEK_START: ${{ inputs.week-start }}
LB_MODE: ${{ inputs.mode }}
LB_GRADIENT: ${{ inputs.gradient }}
LB_ANIMATE: ${{ inputs.animate }}
LB_SCOPE: ${{ inputs.scope }}
LB_MONTH_CARDS: ${{ inputs.month-cards }}
LB_TOP_FILMS: ${{ inputs.top-films }}
LB_EXPORT_PNG: ${{ inputs.export-png }}
LB_BADGE_STYLE: ${{ inputs.badge-style }}
LB_BADGE_STATS: ${{ inputs.badge-stats }}
LB_OUTPUT: ${{ inputs.output }}
run: |
set -euo pipefail
args=("$LB_USERNAME" -o "$LB_OUTPUT" -w "$LB_WEEK_START" -m "$LB_MODE" -s "$LB_SCOPE" -c "$LB_MONTH_CARDS" -r "$LB_TOP_FILMS" --review-years "$LB_REVIEW_YEARS" --badge-style "$LB_BADGE_STYLE" --badge-stats "$LB_BADGE_STATS")
if [ -n "$LB_YEARS" ]; then args+=(-y "$LB_YEARS"); fi
if [ -n "$LB_GRADIENT" ] && [ "$LB_GRADIENT" != "true" ]; then args+=(-g "$LB_GRADIENT"); fi
if [ "$LB_ANIMATE" = "false" ]; then args+=(-a false); fi
if [ "$LB_EXPORT_PNG" = "true" ]; then args+=(-p); fi
node "$GITHUB_ACTION_PATH/src/cli.js" "${args[@]}"
echo "data-json=$(dirname "$LB_OUTPUT")/letterboxd-data.json" >> "$GITHUB_OUTPUT"
- name: Commit and push
if: ${{ inputs.commit == 'true' }}
shell: bash
env:
LB_OUTPUT: ${{ inputs.output }}
LB_COMMIT_MESSAGE: ${{ inputs.commit-message }}
run: |
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
git add "$(dirname "$LB_OUTPUT")"
if git diff --staged --quiet; then
echo "No changes to commit"
exit 0
fi
BRANCH="${GITHUB_REF_NAME}"
TIMESTAMP=$(date -u +"%Y-%m-%d %H:%M UTC")
git commit -m "$LB_COMMIT_MESSAGE [$BRANCH] - $TIMESTAMP"
if ! git push origin "HEAD:$BRANCH"; then
echo "Initial push failed, retrying after rebase..."
git fetch origin "$BRANCH"
# The branch usually moved because a concurrent run rewrote the same
# generated files, so a plain rebase always conflicts. These are build
# outputs, so the freshly generated version wins.
if ! git rebase -X theirs "origin/$BRANCH"; then
git rebase --abort
echo "Rebase onto origin/$BRANCH failed, giving up." >&2
exit 1
fi
git push origin "HEAD:$BRANCH"
fi