Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 39 additions & 20 deletions .github/jump-links.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@
# (packages/github-actions-grafana-jump). Gives every contributor to this
# repo the same jump targets without each of them configuring the userscript
# by hand. A contributor's own personal config (set via the userscript's own
# "Set up Grafana jump" panel) always takes priority over this file for any
# page it covers - this is only a fallback for pages nobody has personally
# "Set up links" panel) always takes priority over this file for any page it
# covers - this is only a fallback for pages nobody has personally
# configured. Bootstrap one of these for your own repo via the userscript's
# config panel -> "Create repo config template" / "Export my config to repo".
#
# This repo doesn't actually run a Grafana instance - the values below are
# illustrative placeholders, kept here purely to document and exercise the
# file format the userscript reads.
#
# baseUrl: your Grafana instance's base URL (no trailing slash).
# dashboards: one entry per jump target. Each entry is either a Grafana
# dashboard link (type: dashboard) or a Tempo trace search (type: trace). A
# target only shows up as a jump target on pages that provide every field
# it's configured to use - not all fields are available on every page (a
# branch's Actions page has no workflow run ID, for example), so different
# targets naturally show up on different pages.
# baseUrl: your Grafana instance's base URL (no trailing slash). Only used by
# type: dashboard and type: trace entries - a type: link entry's URL is
# already complete on its own.
# dashboards: one entry per jump target. Each entry is a Grafana dashboard
# link (type: dashboard), a Tempo trace search (type: trace), or a link to
# any other URL (type: link). A target only shows up as a jump target on
# pages that provide every field it's configured to use - not all fields
# are available on every page (a branch's Actions page has no workflow run
# ID, for example), so different targets naturally show up on different
# pages.
#
# Available fields (named after GitHub Actions' own `github` context, as
# far as a page's URL actually provides an equivalent value): repo, org,
# repoFullName (org/repo), branch, prNumber, workflowName, runnerName,
# runnerGroupName, runId, jobId, serverUrl, apiUrl.
#
# type: dashboard
# name - display label for the jump button/menu.
Expand All @@ -26,24 +34,31 @@
# URL).
# slug - the URL slug right after the uid in the dashboard's URL.
# varNames - which of this dashboard's template variables (if any) to
# preset from the current GitHub page. Leave a field out if
# the dashboard doesn't use that kind of filter. Available
# fields: repo, branch, prNumber, workflowName, runnerName,
# runnerGroupName, runId, jobId.
# preset from the current GitHub page, keyed by one of the
# fields above. Leave a field out if the dashboard doesn't use
# that kind of filter.
#
# type: trace
# name - display label for the jump button/menu.
# id - any string unique among your trace targets; used only
# to dedupe when exporting/merging this file, not shown
# anywhere.
# id - any string unique among your trace/link targets; used
# only to dedupe when exporting/merging this file, not
# shown anywhere.
# datasourceUid - the Tempo datasource's UID in Grafana (Connections ->
# Data sources -> your Tempo source -> the "uid" in its
# URL or Settings JSON).
# query - a TraceQL query with `{{fieldKey}}` placeholders (the
# same field names as varNames above) filled in from the
# current GitHub page - adjust the attribute names below
# (e.g. resource.github.run_id) to match however your own
# query - a TraceQL query with `{{fieldKey}}` placeholders (any
# field above) filled in from the current GitHub page -
# adjust the attribute names below (e.g.
# resource.github.run_id) to match however your own
# traces are tagged.
#
# type: link
# name - display label for the jump button/menu.
# id - any string unique among your trace/link targets; same
# role as a trace target's id.
# urlTemplate - a full URL with `{{fieldKey}}` placeholders (any field
# above) filled in from the current GitHub page - for
# linking anywhere, not just Grafana.
baseUrl: https://grafana.example.com
dashboards:
- type: dashboard
Expand Down Expand Up @@ -75,3 +90,7 @@ dashboards:
id: workflow-job-span
datasourceUid: tempo-datasource-uid
query: '{resource.github.run_id="{{runId}}" && resource.github.job_id="{{jobId}}"}'
- type: link
name: Runbook for this repo
id: repo-runbook
urlTemplate: 'https://runbooks.example.com/{{repoFullName}}'
103 changes: 31 additions & 72 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@ name: Release
# lint/format commit that would trigger another bump.
#
# A second commit, stacked on top of the bump commit but never merged into
# main, adds the built .user.js files to git. GreasyFork's "release published"
# webhook event resolves a sync URL of the form
# .../releases/latest/download/<pkg>.user.js to a *repo-root-relative* git
# path of exactly <pkg>.user.js at the release's own tag (see
# lib/github.rb#file_from_root_for_url in GreasyFork's source - it strips the
# releases/.../download/ prefix and treats what's left as the whole path, so
# packages/<pkg>/dist/ nesting is not preserved), then reads it with
# `git show <tag>:<path>`. That's why this commit mirrors each package's
# built file at repo root under that exact flat name, in addition to its
# normal packages/<pkg>/dist/ location. Tagging that commit (as the release
# tag, and as the floating `latest` tag) gives GreasyFork a git ref it can
# read both forms from, without ever putting build artifacts in main's
# history.
# main, adds the built .user.js files to git at their normal
# packages/<pkg>/dist/ path (no flat root-level mirror needed - see below).
# Tagging that commit as the release tag, and moving the floating `latest`
# tag onto it, gives GreasyFork a git ref it can read the real nested path
# from, without ever putting build artifacts in main's history.
#
# Each package's @downloadURL/@updateURL points at
# https://raw.githubusercontent.com/<org>/<repo>/latest/packages/<pkg>/dist/script.user.js.
# On GreasyFork's "release published" webhook, it strips the domain+org+repo
# prefix from that URL, then discards exactly one more path segment as the
# ref (see lib/github.rb#file_from_root_for_url in GreasyFork's source) -
# here that's the `latest` segment - leaving `packages/<pkg>/dist/script.user.js`
# as the real git path, which it then reads with `git show <release-tag>:<path>`
# (a server-side git operation, not an HTTP fetch of the stored URL - the
# `latest` segment is never itself dereferenced by that fetch, only by other
# tools like Tampermonkey polling @updateURL directly). Since the dist commit
# already places the file at that exact nested path, no flat mirror or
# release-asset upload is needed.

on:
push:
Expand Down Expand Up @@ -217,14 +222,6 @@ jobs:
exit 1
fi
git add -f "$src"

# Flat root-level mirror, named to match each package's
# @downloadURL/@updateURL asset name - see the comment above this
# step for why GreasyFork's release-webhook sync needs this exact
# path instead of the nested packages/<pkg>/dist/ one.
root_copy="${pkg_name}.user.js"
cp "$src" "$root_copy"
git add -f "$root_copy"
done
git commit -m "chore(release): include built userscripts for GreasyFork sync [skip ci]"
echo "dist-sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
Expand All @@ -245,20 +242,21 @@ jobs:
#
# JUDGEMENT CALL: one shared release per run, not one per package.
# Every published script's @downloadURL points at the stable
# .../releases/latest/download/<name>.user.js path. Per-package tags
# would make "latest" ambiguous - whichever package released most
# recently would win, and the other scripts' download URLs would
# resolve to a release that has no asset for them. That's also why
# the upload step below re-uploads every greasyforkPublish package's
# asset on every run, not just the ones bumped this run: "latest" is
# one shared pointer, so leaving an unchanged package out would make
# its download URL 404 the instant any other package's release moved
# past it. Per-package provenance is not lost: each package keeps
# its own version in its package.json and its own CHANGELOG.md.
# .../latest/packages/<pkg>/dist/script.user.js path. Per-package
# tags would make "latest" ambiguous - whichever package released
# most recently would win, and the other scripts' download URLs
# would resolve to a release commit that predates their own most
# recent build. Moving "latest" onto every dist commit (not just
# ones with bumps for that package - the dist commit above rebuilds
# every greasyforkPublish package) keeps every script's download URL
# resolvable. Per-package provenance is not lost: each package
# keeps its own version in its package.json and its own
# CHANGELOG.md.
RELEASE_TAG="release/$(date -u +%Y%m%d-%H%M%S)"
git tag -a "$RELEASE_TAG" -m "Release $RELEASE_TAG"
# Floating tag GreasyFork sync identifiers point at (raw.githubusercontent.com
# .../refs/tags/latest/...) so each script's sync URL never has to change again.
# Floating tag every package's @downloadURL/@updateURL resolves
# through (raw.githubusercontent.com/<org>/<repo>/latest/...) so
# each script's URL never has to change again.
git tag -f "latest" HEAD
# release/last-run stays on the bump commit (not the dist commit) -
# it's the base the NEXT run diffs source changes against.
Expand Down Expand Up @@ -292,42 +290,3 @@ jobs:
--title "$RELEASE_TAG" \
--notes-file /tmp/release-notes.md \
--latest

- name: Upload userscripts as release assets
if: steps.bump.outputs.has-bumps == 'true'
env:
GH_TOKEN: ${{ steps.auth.outputs.token }}
RELEASE_TAG: ${{ steps.tag.outputs.release-tag }}
run: |
set -euo pipefail
UPLOAD_DIR="$(mktemp -d)"

# Every greasyforkPublish package gets its asset re-uploaded on
# every release, not just the ones bumped this run. "latest" is one
# shared pointer across all published scripts' @downloadURL and
# GreasyFork sync identifiers - a script left out here would vanish
# from releases/latest/download/<name>.user.js the moment any OTHER
# script's release moves "latest" past it, even though its own code
# never changed. `yarn run build` above already built every
# package, bumped or not.
for pkg_dir in packages/*/; do
pkg_name="$(basename "$pkg_dir")"
pjson="${pkg_dir}package.json"
[ -f "$pjson" ] || continue
opted_in="$(node -pe "require('./$pjson').greasyforkPublish === true" 2>/dev/null || echo false)"
[ "$opted_in" = "true" ] || continue

# Rename to <package-dir-name>.user.js so several packages' assets
# coexist on one release and each matches the fixed
# .../releases/latest/download/<name>.user.js URL baked into that
# package's own @downloadURL/@updateURL.
src="packages/${pkg_name}/dist/script.user.js"
if [ ! -f "$src" ]; then
echo "::error::greasyforkPublish is set for $pkg_name but $src is missing after build"
exit 1
fi
dest="${UPLOAD_DIR}/${pkg_name}.user.js"
cp "$src" "$dest"
echo "Uploading $dest to $RELEASE_TAG"
gh release upload "$RELEASE_TAG" "$dest" --repo "${{ github.repository }}" --clobber
done
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ This repository uses a monorepo structure with the following setup:
- A patch bump is applied to each package whose files changed since the last
release. Bumping a version by hand in a PR (e.g. for a minor or major
release) is respected and not bumped again on top.
- Each release run publishes one GitHub Release carrying every bumped script's
compiled `<package-name>.user.js` as an asset. Published scripts point their
`@downloadURL`/`@updateURL` at that release's stable `latest/download` URL,
- Each release run also commits every published package's built
`dist/script.user.js`, tags it, and moves a floating `latest` tag onto that
commit. Published scripts point their `@downloadURL`/`@updateURL` at
`https://raw.githubusercontent.com/nsheaps/greasemonkey-scripts/latest/packages/<package-name>/dist/script.user.js`,
so a userscript manager installed directly from GitHub auto-updates from
there. [GreasyFork](https://greasyfork.org/en/scripts?by=1372068) forcibly
rewrites those same fields for any script actually listed on its site, so
Expand Down
Loading
Loading