Skip to main content

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):

  1. Runs on every push to main and on workflow_dispatch, under a release concurrency group.
  2. When no changeset is pending, scripts/auto-changeset.mjs writes one from the commits since the last release tag that touched packages/: feat is minor, a ! after the type or a BREAKING 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 Changesets fixed group, so they always share a version.
  3. changeset version writes the versions and changelogs; the workflow commits them to main with GITHUB_TOKEN (such pushes start no workflow, so there is no loop).
  4. changeset publish publishes each package whose version is not on npm yet, with provenance, and tags @mishrashardendu22/observatory-ui@X.Y.Z (and tokens). changesets/action creates a GitHub release per tag and exposes published and publishedPackages as outputs; the job exposes version too.
  5. actions/github-script calls POST /repos/{owner}/{repo}/dispatches on this repository with event_type: observatory-ui-released and client_payload: {version, packages, tag, release_url, sha, run_url}.

Consumer side (.github/workflows/design-system-update.yml):

  1. Triggered by that event, by a daily schedule, or by hand with an optional version. Both repository_dispatch and schedule run the workflow file on main, so the automation is live once the workflow is on main.
  2. 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.
  3. Runs pnpm add --save-exact <tokens>@X.Y.Z <ui>@X.Y.Z in every app that uses the packages (APPS in the workflow; the docs site takes the tokens only), which rewrites package.json and regenerates each pnpm-lock.yaml.
  4. Writes a pull request body with the release link and the release notes, then peter-evans/create-pull-request commits (signoff, signed through the GitHub API) to github-actions/main/observatory-ui-X.Y.Z and opens or updates the pull request against main. 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.

RepositorySettingValueWhy
npmjs.comAccountOne that owns the @mishrashardendu22 scope (the account name, or an organisation with that name).The packages are scoped.
observatory-uiSecret NPM_TOKENnpm 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-uiVariable 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-uiSecret CONSUMER_DISPATCH_TOKENFine-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-uiVariable CONSUMER_REPOSITORY (optional)owner/nameOnly if this repository moves.
this repositorySettings → Actions → General → Workflow permissionsTick Allow GitHub Actions to create and approve pull requestsWithout it, GITHUB_TOKEN cannot open the upgrade pull request. Not needed when the secret below exists.
this repositorySecret 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:

  1. Complete the observatory-ui setup above (at least NPM_TOKEN).
  2. Run its Release workflow once by hand (Actions → Release → Run workflow) or merge any change. The pending changeset for the publishing setup makes this 0.3.1, the first version on npm (0.3.0 exists only as release tarballs).
  3. 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, run npm deprecate @mishrashardendu22/observatory-ui@X.Y.Z "reason" (and tokens), revert or fix on main, 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 Release job; changeset publish publishes 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 run Design system update here 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 to GITHUB_TOKEN only 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 titled chore(deps): observatory design system X.Y.Z appears 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.