Design System Releases
The Observatory design system lives in its own repository,
MishraShardendu22/observatory-ui,
and reaches this repository as two npm packages, @mishrashardendu22/observatory-tokens
and @mishrashardendu22/observatory-ui, always on the same version. Nothing in the
release path is manual: a merge there becomes a pull request here. This page is the
runbook: how the pipeline works, the one-time setup with every token and permission,
how the tarball dependencies were migrated, and how to roll back.
The pipeline
observatory-ui github-backup-automation-system
────────────── ───────────────────────────────
push to main ──► release.yml design-system-update.yml
├ pnpm verify ├ repository_dispatch ◄── (1)
├ changeset (hand-written, or ├ schedule 06:23 UTC daily (backstop)
│ written from the commits) └ workflow_dispatch (version input)
├ changeset version → commit │
│ "chore(release): version …" ├ wait until npm serves the version
├ changeset publish → npm ├ pnpm add --save-exact in
│ (provenance) + tags │ src/apps/github-backup-dashboard
├ GitHub release per tag │ src/apps/github-backup-design
│ │ src/apps/github-backup-docs (tokens)
└ (1) POST /repos/…/dispatches └ one pull request against main
event: observatory-ui-released chore(deps): observatory design system X.Y.Z
Library side (observatory-ui/.github/workflows/release.yml, documented in its
RELEASING.md):
- Runs on every push to
mainand onworkflow_dispatch, under areleaseconcurrency group. - When no changeset is pending,
scripts/auto-changeset.mjswrites one from the commits since the last release tag that touchedpackages/:featis minor, a!after the type or aBREAKING CHANGE:footer is breaking, anything else is patch. While the packages are 0.x a breaking change bumps the minor version; 1.0.0 is a hand-written changeset. Both packages are a Changesetsfixedgroup, so they always share a version. changeset versionwrites the versions and changelogs; the workflow commits them tomainwithGITHUB_TOKEN(such pushes start no workflow, so there is no loop).changeset publishpublishes each package whose version is not on npm yet, with provenance, and tags@mishrashardendu22/observatory-ui@X.Y.Z(and tokens).changesets/actioncreates a GitHub release per tag and exposespublishedandpublishedPackagesas outputs; the job exposesversiontoo.actions/github-scriptcallsPOST /repos/{owner}/{repo}/dispatcheson this repository withevent_type: observatory-ui-releasedandclient_payload: {version, packages, tag, release_url, sha, run_url}.
Consumer side (.github/workflows/design-system-update.yml):
- Triggered by that event, by a daily schedule, or by hand with an optional version.
Both
repository_dispatchandschedulerun the workflow file onmain, so the automation is live once the workflow is onmain. - Resolves the version: the payload's (polling npm for up to five minutes until the registry serves it) or the latest on npm. If the dashboard already pins it, the run ends with nothing to do.
- Runs
pnpm add --save-exact <tokens>@X.Y.Z <ui>@X.Y.Zin every app that uses the packages (APPSin the workflow; the docs site takes the tokens only), which rewritespackage.jsonand regenerates eachpnpm-lock.yaml. - Writes a pull request body with the release link and the release notes, then
peter-evans/create-pull-requestcommits (signoff, signed through the GitHub API) togithub-actions/main/observatory-ui-X.Y.Zand opens or updates the pull request againstmain. Re-running for the same version updates the same pull request; merging it deletes the branch.
The apps pin exact versions. Dependabot ignores @mishrashardendu22/* in both apps so
the two mechanisms never open competing pull requests; everything else stays with
Dependabot.
One-time setup
Nothing here holds a token in a file. Every token is a GitHub Actions secret, and each one is scoped to the single thing it does.
| Repository | Setting | Value | Why |
|---|---|---|---|
| npmjs.com | Account | One that owns the @mishrashardendu22 scope (the account name, or an organisation with that name). | The packages are scoped. |
observatory-ui | Secret NPM_TOKEN | npm granular access token: packages and scopes, read and write on @mishrashardendu22/*, bypass 2FA on, an expiry you note. | changeset publish runs npm publish. Needed for the first publish. |
observatory-ui | Variable NPM_TRUSTED_PUBLISHING = true (optional, after the first publish) | On npmjs.com, each package → Settings → Trusted publisher → GitHub Actions, repository MishraShardendu22/observatory-ui, workflow release.yml. Then set the variable and delete NPM_TOKEN. | Publishing through OIDC (id-token: write) with no long-lived secret. |
observatory-ui | Secret CONSUMER_DISPATCH_TOKEN | Fine-grained personal access token, resource owner MishraShardendu22, repository access: only github-backup-automation-system, permissions: Contents: read and write, nothing else. | POST /repos/{owner}/{repo}/dispatches is a write on another repository; GITHUB_TOKEN is scoped to the repository that runs the workflow. |
observatory-ui | Variable CONSUMER_REPOSITORY (optional) | owner/name | Only if this repository moves. |
| this repository | Settings → Actions → General → Workflow permissions | Tick Allow GitHub Actions to create and approve pull requests | Without it, GITHUB_TOKEN cannot open the upgrade pull request. Not needed when the secret below exists. |
| this repository | Secret DESIGN_SYSTEM_BOT_TOKEN (recommended) | Fine-grained personal access token, resource owner MishraShardendu22, repository access: only github-backup-automation-system, permissions: Contents: read and write, Pull requests: read and write. | A pull request opened with GITHUB_TOKEN starts no pull_request workflow, so CI, the labeler and the Jules review would not run on it. With this token they do, and the commit is signed as its owner. |
Where GITHUB_TOKEN is enough and used: verifying, versioning and committing to
observatory-ui/main, pushing its tags, creating its GitHub releases, reading the
release notes from here, and (with the setting above) opening the pull request here.
Where a fine-grained token is required: the cross-repository dispatch, and CI on the
bot's pull request.
Until NPM_TOKEN or NPM_TRUSTED_PUBLISHING exists, the library's workflow ends green
with the warning "Release skipped", so the pipeline can be merged before the tokens are
created.
Migration from the release tarballs
Until this change the apps depended on tarball URLs
(https://github.com/MishraShardendu22/observatory-ui/releases/download/vX.Y.Z/….tgz).
The pipeline migrates them on its first run, because pnpm add replaces whatever
specifier is in package.json:
- Complete the
observatory-uisetup above (at leastNPM_TOKEN). - Run its
Releaseworkflow once by hand (Actions → Release → Run workflow) or merge any change. The pending changeset for the publishing setup makes this0.3.1, the first version on npm (0.3.0exists only as release tarballs). - The dispatch opens the pull request here that turns both apps' tarball URLs into
"0.3.1"and regenerates the lockfiles. Merge it.
Or by hand, once the packages are on npm:
for app in src/apps/github-backup-dashboard src/apps/github-backup-design; do
(cd "$app" && pnpm add --save-exact @mishrashardendu22/observatory-tokens@0.3.1 @mishrashardendu22/observatory-ui@0.3.1)
done
(cd src/apps/github-backup-docs && pnpm add --save-exact @mishrashardendu22/observatory-tokens@0.3.1)
The existing v0.1.0–v0.3.0 releases keep their tarballs; nothing installed today
breaks. The design site reads the version it documents from the installed
@mishrashardendu22/observatory-ui/package.json, so it needs no edit on an upgrade.
Rollback
- A bad design system version reached the apps: revert the upgrade pull request (or close it before merging). The apps pin exact versions, so nothing else moves. Vercel redeploys the reverted commit.
- A bad version is on npm: never unpublish. In
observatory-ui, runnpm deprecate @mishrashardendu22/observatory-ui@X.Y.Z "reason"(and tokens), revert or fix onmain, and the next merge publishes the fixed version and opens a new pull request here. - The library versioned but did not publish (outage, expired token): re-run its
Releasejob;changeset publishpublishes the missing version without a second version commit. - The dispatch was lost (missing or expired
CONSUMER_DISPATCH_TOKEN): the library run is red at its last step. Fix the token and re-run it, or runDesign system updatehere with the version; the daily schedule catches up anyway. - An expired
DESIGN_SYSTEM_BOT_TOKEN: the pull request step fails with a 401. Rotate the token; without it the workflow falls back toGITHUB_TOKENonly if the secret is deleted (an empty secret is not the same as a missing one).
Day to day
- A change to a component or a token: pull request in
observatory-ui, merge. Within a few minutes a pull request titledchore(deps): observatory design system X.Y.Zappears here; review the rendered pages in the Vercel preview and merge. - To choose the bump or the changelog wording, add a changeset in the library pull
request with
pnpm changeset. Otherwise the commit subjects decide. - The upgrade pull request is exempt from the repository's single-open-PR rule, like a Dependabot pull request: it is a dependency bump, not a feature branch.