Skip to content

chore: use changesets to support an automatic release process - #3445

Draft
zbynekstara wants to merge 3 commits into
clientIO:masterfrom
zbynekstara:changesets
Draft

chore: use changesets to support an automatic release process#3445
zbynekstara wants to merge 3 commits into
clientIO:masterfrom
zbynekstara:changesets

Conversation

@zbynekstara

@zbynekstara zbynekstara commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Description

Alternative to #3436.

Use changesets for deferred versioning and changelog creation.

Motivation and Context

This will allow us to streamline our release process by asking PR contributors to make versioning and changelog decisions at PR time (when they have the most context about their changes) instead of at release time.

A changeset is a file inside .changesets/ folder (one per PR), which records two things:

  • Which packages need a version bump due to this change, and if it is a patch/minor/major bump.
  • What the changelog line(s) for this change should say.

Setup TODO

Before the Changesets release pipeline can run, these steps need to be completed:

  • Add npm token — create an npm automation token and add it as repo secret NPM_TOKEN
    (org 2FA must permit automation tokens; there's no interactive OTP in CI).
  • Create the release GitHub App — an App with contents: write + pull-requests: write,
    installed on clientIO/joint. Add its credentials as repo secrets RELEASE_APP_ID and
    RELEASE_APP_PRIVATE_KEY. (Used instead of GITHUB_TOKEN so the release PR triggers CI, and to
    push tags + force-update prod.)
  • Enable auto-merge — repo Settings → General → "Allow auto-merge", so the release/next PR
    self-merges on green.
  • Branch protection — allow the release App to (a) merge the release PR into master, (b) push
    vX.Y.Z tags, and (c) force-update prod (git push --force origin master:prod). Add a
    bypass/allowance for the App on any rules that would block these, and make sure a prod branch
    exists (or can be created by the first push).
  • Create the release PR label — the workflow adds a release label to the PR.
  • Sync the Yarn ignore reference list — apply the same test-glob fix there:
    **/*.test.{js,ts,mjs}**/*.test.{js,jsx,ts,tsx,mjs}, so it stays the exact inverse of
    .changeset/config.json's changedFilePatterns.
  • Fix local commit signinggit commit currently hangs (GPG passphrase prompt in
    non-interactive shells). Resolve (agent/pinentry, or commit.gpgsign=false) before committing
    this branch.
  • Commit & open the PR for this changesets branch once the above are ready.

First release should be a dry run: dispatch Release (prepare) with dry_run: true and review the
rendered CHANGELOG / RELEASE_NOTES.md in the job summary before doing a real run (optionally a
next/beta channel end-to-end first).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a Changesets-based deferred versioning workflow to support an automated two-phase release process (prepare + publish) for the JointJS monorepo, including custom root changelog/release-notes rendering and CI enforcement that releasable PRs include a changeset.

Changes:

  • Add Changesets CLI integration + CI enforcement (changeset status) for releasable PRs.
  • Add custom scripts to derive release metadata and to render the root CHANGELOG / RELEASE_NOTES.md from pending changesets.
  • Add GitHub Actions workflows to prepare a release PR and to publish/tag/create a GitHub Release + refresh prod.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
yarn.lock Adds Changesets and related dependency graph tooling to the lockfile.
scripts/release-info.mjs Computes release metadata (versions/title) from pending changesets for workflow use.
scripts/changelog-from-changesets.mjs Generates JointJS-formatted root CHANGELOG and RELEASE_NOTES.md from pending changesets.
package.json Adds Changesets CLI + release helper scripts (status/changelog/version/publish).
CONTRIBUTING.md Documents changeset requirement and the new maintainer release workflow.
.github/workflows/test-pr.yml Enforces “releasable change ⇒ changeset exists” in PR CI.
.github/workflows/release.yml Adds “Release (prepare)” workflow to render changelog, version, and open a release PR.
.github/workflows/publish.yml Adds “Release (publish)” workflow to publish packages, tag/release, and force-update prod.
.github/pull_request_template.md Updates contributor checklist to reference yarn test + changesets.
.changeset/README.md Adds repo-specific Changesets conventions (body format, ordering, documented packages).
.changeset/config.json Adds Changesets configuration, including changedFilePatterns for bump enforcement.
Suppressed comments (1)

.github/workflows/release.yml:143

  • The dry-run summary prints inputs.dist_tag, but dist_tag is not actually used by the workflow/pipeline. If you want to show the channel, derive it from the computed core version (prerelease suffix) to match publish.yml.
          echo "## Dry run — release NOT pushed" >> "$GITHUB_STEP_SUMMARY"
          echo '```' >> "$GITHUB_STEP_SUMMARY"
          echo "channel: ${{ inputs.dist_tag }}" >> "$GITHUB_STEP_SUMMARY"
          echo "${{ steps.info.outputs.release_title }}" >> "$GITHUB_STEP_SUMMARY"
          echo "released: ${{ steps.info.outputs.released }}" >> "$GITHUB_STEP_SUMMARY"

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/release.yml
Comment thread package.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.

Suppressed comments (6)

package.json:32

  • release:publish runs npm publish but passes Yarn-specific flags (e.g. --tolerate-republish) and the workflow expects Yarn to handle workspace: ranges. This will fail with the npm CLI and may publish incorrect dependency ranges. Use yarn npm publish inside workspaces foreach instead (so --tag passed by publish.yml is forwarded to the right command).
    "release:publish": "yarn workspaces foreach --all --no-private -t npm publish --access public --tolerate-republish"

packages/joint-shapes-general/package.json:5

  • Marking this workspace as private prevents it from being versioned/published by Changesets (and it will be skipped by --no-private publishing). That contradicts .changeset/README.md which explicitly calls @joint/shapes-general a publishable package that is still versioned and published. Either keep it non-private, or update the release docs/pipeline to match the new intent.
  "name": "@joint/shapes-general",
  "private": true,
  "title": "JointJS General Shapes",
  "version": "4.3.1",

packages/joint-shapes-general-tools/package.json:5

  • Marking this workspace as private prevents it from being versioned/published by Changesets (and it will be skipped by --no-private publishing). That contradicts .changeset/README.md which explicitly calls @joint/shapes-general-tools a publishable package that is still versioned and published. Either keep it non-private, or update the release docs/pipeline to match the new intent.
  "name": "@joint/shapes-general-tools",
  "private": true,
  "title": "JointJS General Shapes Tools",
  "version": "4.3.1",

scripts/changelog-from-changesets.mjs:367

  • Prerelease sorting is currently lexicographic (pa.pre < pb.pre), which misorders numeric identifiers (e.g. beta.10 will sort before beta.2). This can produce incorrectly ordered CHANGELOG blocks when using changeset pre (as described in CONTRIBUTING). Implement semver-consistent prerelease comparison (numeric identifiers compared numerically, and shorter prerelease sets sort lower).
    // A version WITH a prerelease tag is lower than one without.
    if (pa.pre && !pb.pre) return 1;
    if (!pa.pre && pb.pre) return -1;
    if (pa.pre && pb.pre) return pa.pre < pb.pre ? 1 : pa.pre > pb.pre ? -1 : 0;
    return 0;

scripts/release-info.mjs:39

  • If @joint/core is missing from the workspace list for any reason, this will throw a cryptic Cannot read properties of undefined when accessing .packageJson. Since this script is intended for CI automation, it should fail with a clear error message.
const core = versionByPkg.get('@joint/core');
const coreVersion = core ?? packages.packages.find((p) => p.packageJson.name === '@joint/core').packageJson.version;
const coreChanged = versionByPkg.has('@joint/core');

.github/workflows/release.yml:122

  • The PR description references a release/next PR/branch, but the workflow hard-codes release/pending. Please align the branch naming between the workflow and the documented release process to avoid operational confusion.
          BRANCH="release/pending"

Comment thread packages/joint-layout-directed-graph/package.json
Comment thread packages/joint-layout-msagl/package.json
Comment thread packages/joint-eslint-config/package.json
Comment thread packages/joint-decorators/package.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants