You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: harden avatar fetching, output contract, and API handling
Review of the v0.1.0 surface turned up two ways an action run could stall or
be subverted, plus a set of behaviours that contradicted the documented ones.
- Avatar workers no longer die on exceptions outside AvatarError (malformed
Content-Type, TLS failures, unreadable files): those killed the fiber and
left the collector blocked until the job timed out.
- Release tags are validated and passed through env, so a crafted tag cannot
reach a shell in the publishing job.
- Local avatars are resolved with realpath and size-capped; redirects must
stay on public https, and only image content types reach the data URI.
- Nothing is written when a render would be empty or a later output fails,
so a good wall is never replaced by a blank one.
- Sources compose: members/stargazers/sponsors work without a users list, a
person found in several sources appears once with the highest weight, and
config entries keep their own group.
- API errors are typed: 5xx retries, non-JSON bodies and hidden sponsors
become ApiError, and a stalled GraphQL cursor ends pagination.
- Labels widen the canvas instead of being clipped, truncate: 0 disables
truncation everywhere, and honeycomb/mosaic shrink to their content.
- Outputs always report changed/paths; png.scale accepts integers; enum and
no_commit mistakes fail with the accepted values.
*(Curated samples from [`examples/showcase.yml`](examples/showcase.yml). This repository also runs the action on itself every week — the live result lands in [`docs/`](docs/).)*
10
+
9
11
-**Three styles** — classic grid (circle/rounded/square), honeycomb hexagons, and a weight-tiered mosaic where your top contributors literally loom larger.
10
12
-**Many sources, one wall** — your curated `users` list, repository contributors, org members, stargazers, and GitHub Sponsors (tier amounts become weights). Mix freely; your YAML entries always win.
11
13
-**Sections & roles** — split the wall into titled groups (say, *Contributors* and *Special Thanks*) and tag people with a role line (*Creator*, *Design*, *Docs*) — made for honoring the folks the contributors API can't see.
| `config` | `.github/hall-of-fame.yml` | Path to the config YAML, relative to the repository root |
70
+
| `token` | `${{ github.token }}` | GitHub API token. Required for `sponsors`; also lifts rate limits and reaches private repos for the other API sources |
71
+
| `no_commit` | `false` | Generate files but skip commit/push (must be `true` or `false`) |
70
72
| `commit_message` | `chore: update hall of fame` | Commit message |
71
73
72
-
Outputs: `svg_path`(comma-separated generated paths), `user_count`, `changed` (whether a commit was pushed).
74
+
Outputs: `paths`(comma-separated generated files, SVG and PNG), `user_count`, and `changed` (whether a commit was pushed; `false` when `no_commit` is set). `svg_path` still works as an alias for `paths`.
73
75
74
76
## Configuration
75
77
@@ -162,11 +164,16 @@ png:
162
164
scale: 2# rasterization zoom for .png outputs
163
165
```
164
166
165
-
When `source: both`, your `users` entries win over API data field by field — set a custom `name` or `weight` while the contribution count fills everyone else's. Users without a `group` render first in an untitled section; groups follow the `groups` order (or first mention in the config). This is the recipe for honoring people the API misses — unlinked commit emails, design or docs work: add them to `users` with a `role` and their own section.
167
+
When `source: both`, your `users` entries win over API data field by field — set a custom `name` or `weight` while the contribution count fills everyone else's. Placement is always yours: an entry without `group` renders in the untitled leading section even if the API put that person in one, so add `group:` when you want them filed under a heading. Someone returned by more than one API source (a contributor who also sponsors) appears once, keeping the highest weight and the first source's group.
168
+
169
+
This is the recipe for honoring people the API misses — unlinked commit emails, design or docs work: add them to `users` with a `role` and their own section.
166
170
167
171
## Notes
168
172
173
+
- A config file is required; the action fails if `.github/hall-of-fame.yml` (or the path you pass as `config`) does not exist.
169
174
- The workflow needs `permissions: contents: write` to push the generated file, and a `concurrency` group avoids racing pushes on busy repositories.
175
+
- Avatars link to profiles and carry name/role tooltips, but a README embed (``) renders as an `<img>`, where neither is active. Open the SVG directly — or inline it — to get links.
176
+
- `members` returns public organization members only; a token with `read:org` is needed for the rest. Stargazers arrive oldest-first with no weight, so under the default `sort: weight` they trail contributors — use `sort: none` to keep the API order.
170
177
- On `pull_request` events the checkout is a detached HEAD, so pushes fail — use push/schedule/dispatch triggers, or set `no_commit: true` and handle the file yourself.
171
178
- SVG size grows with user count (roughly 5–15 KB per avatar). Use `limit` and moderate avatar sizes for large walls.
172
179
- With `mode: auto` (the default) the SVG contains both palettes and a `prefers-color-scheme` media query, so it follows GitHub's light/dark theme. PNGs can't adapt, so `.png` outputs pin `auto` to the light palette — add a second output with `mode: dark` for a pair.
@@ -179,10 +186,12 @@ The action binary is also a local CLI:
bin/hall-of-fame --config examples/showcase.yml # regenerates the committed examples/*.svg
183
190
bin/hall-of-fame -c my.yml --commit # opt in to commit/push locally
184
191
```
185
192
193
+
`--config`is resolved against the current directory, while output paths and local `avatar_url` files are resolved against `--workspace` (the current directory by default; `GITHUB_WORKSPACE` inside the action). Committing happens automatically when `GITHUB_ACTIONS=true` — including on runners that emulate it, such as act or Forgejo — and otherwise only with `--commit`.
194
+
186
195
## Development
187
196
188
197
```bash
@@ -193,7 +202,9 @@ crystal tool format
193
202
bin/ameba src spec
194
203
```
195
204
196
-
Release flow: pushing a `v*` tag builds a multi-arch image to `ghcr.io/crystal-actions/hall-of-fame` and force-moves the major tag (`v0`, `v1`, …). The image is pushed before the git tag moves, so the moving tag always references an existing image.
205
+
Release flow: pushing a `vX.Y.Z` tag builds a multi-arch image to `ghcr.io/crystal-actions/hall-of-fame` and force-moves the major tag (`v0`, `v1`, …). The image is pushed before the git tag moves, so the moving tag always references an existing image.
206
+
207
+
While the repository is private, `action.yml` still uses `image: Dockerfile`, so consumers build the image on their runner (roughly a minute on a cold cache) and the published GHCR image is not used yet. Switching `action.yml` to `docker://ghcr.io/crystal-actions/hall-of-fame:v0` is part of going public — runners pull GHCR anonymously, which only works once the package is public.
0 commit comments