Skip to content

Distribute the activity script as a GitHub Action - #4

Merged
kasperg merged 11 commits into
mainfrom
distribute-action
Sep 15, 2026
Merged

kasperg merged 11 commits into
mainfrom
distribute-action

Conversation

@kasperg

@kasperg kasperg commented Sep 15, 2026

Copy link
Copy Markdown
Member

Distributes the activity script to Upsun projects through a composite GitHub Action instead of hand installs with the CLI.

Consumer repositories copy examples/upsun-github-env-sync.yml. It runs the action weekly, on manual dispatch and when the workflow file changes. The action creates or updates a script integration in the Upsun project and sets GH_TOKEN, GH_REPO and UPSUN_GITHUB_ENV_SYNC_VERSION on it. Ownership is decided by that version variable alone: integrations without it are never read or changed, and hand installs are not adopted. Creation and update go through upsun api:curl, so the new integration ID comes back from the response.

Releases are cut by semantic-release on push to main from conventional commits. A feat or fix commit creates a tag and a GitHub release and moves the floating major tag, so consumers on @v1 pick up each release on their next nightly run. Nothing is published to a registry. CI lints every commit in a pull request, and npm run lint:commits does the same locally.

Development moves to Node 24. Upsun documents ES2021 for activity scripts and no Node version, and the ES2021 target in the type check is what guards compatibility. Node 20 is end of life and the release tooling needs Node 22 or newer.

Verified locally: npm run check on Node 24, commitlint over all commits, actionlint over the three workflows, and a dry run of semantic-release in a throwaway clone. With the v0.0.0 tag on main, the first release from this branch is 0.1.0. The documentation already refers to @v1, which exists once 1.0.0 is released.

Before merging:

  1. Make the repository public so uses: reload/upsun-github-env-sync@v1 resolves from other repositories. The history contains no secrets.
  2. Create the organization secret UPSUN_API_TOKEN from the shared Reload Upsun API user.

After merging, bupl is the first consumer: delete its hand-installed integration, add the example workflow, and run it once by hand. That run is the first real test of the create payload.

Assisted-by: Claude <noreply@anthropic.com>
@kasperg
kasperg force-pushed the distribute-action branch 18 times, most recently from 8f88a91 to b5a0a3c Compare September 15, 2026 12:19
kasperg and others added 10 commits September 15, 2026 15:01
Consumer repositories run the action to create or update the script
integration in their Upsun project and set GH_TOKEN and GH_REPO on it.

Ownership is decided by a single rule: a script integration is ours
only if it carries the UPSUN_GITHUB_ENV_SYNC_VERSION variable. The action
never reads or changes integrations without it, so projects with other
script integrations keep working and hand installs are never adopted.
The variable doubles as a record of the installed release, read from
package.json in the action checkout. On create it is written first so
a failure later in the run leaves an integration the next run finds.

All API calls go through `upsun api:curl`, which handles
authentication. Creating through the API returns the new ID directly,
so no list diffing is needed. The Upsun CLI is installed from the
upsun/cli installer into a runner temp directory so the step does not
depend on sudo.

Assisted-by: Claude <noreply@anthropic.com>
The installer reads VERSION and falls back to the latest release when it
is empty, so consumers can pin only when a CLI release breaks them.

Assisted-by: Claude <noreply@anthropic.com>
Every push to main with a feat or fix commit creates a GitHub release
and tag, updates CHANGELOG.md and package.json, and moves the floating
major tag so consumers can pin either an exact tag or vN.

Nothing is published to a registry: npmPublish is off and the package
stays private. The npm plugin is kept only for the version bump, which
the action reads back as the installed version.

The release commit is pushed with a token that has bypass rights on
the main ruleset, since the workflow token cannot be added to a bypass
list. The version starts at 0.0.0 so the first release is 0.1.0. semantic-
release derives versions from tags, so a v0.0.0 tag on main is needed
before the first run, otherwise it starts at 1.0.0.

Assisted-by: Claude <noreply@anthropic.com>
Assisted-by: Claude <noreply@anthropic.com>
Node 20 was chosen as the oldest LTS supporting ES2021, the level Upsun
documents for activity scripts. Upsun does not document a Node version,
and the script runs in a sandbox that is not Node: fetch is synchronous
and the storage module exists only there. ES2021 compatibility is
guarded by the es2021 target in the type check, not by the local Node
version.

Node 20 is end of life, and the release and commit lint tooling needs
Node 22 or newer.

Assisted-by: Claude <noreply@anthropic.com>
npm run lint:commits checks every commit between origin/main and HEAD
with the same config as the pull request check, so a rejected message
is found before the push.

Assisted-by: Claude <noreply@anthropic.com>
semantic-release reads every commit that lands on main, so each pull
request runs the same commitlint script that developers run locally.
The commitlint version is pinned in package.json, so Dependabot keeps
it current.

Assisted-by: Claude <noreply@anthropic.com>
Nightly schedule so releases of the action roll out without a consumer
commit, manual dispatch for first install and debugging, and push on the
workflow path so a new consumer is installed on merge.

Assisted-by: Claude <noreply@anthropic.com>
Replaces the manual CLI install instructions with the action-based
install, documents the ownership rule, the version tags, the release
process and the token expiry symptom.

Assisted-by: Claude <noreply@anthropic.com>
Since activities lost their default commits, this test took the
environment-name fallback for the ref. Use the push helper like the
other push tests so it covers a failing POST after a successful
head-commit lookup.

Assisted-by: Claude <noreply@anthropic.com>
@kasperg
kasperg merged commit 101ab72 into main Sep 15, 2026
2 checks passed
@reload-upsun

Copy link
Copy Markdown

🎉 This PR is included in version 0.1.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants