chore: replace changesets with bespoke stateless release system (#3769)

Remove the entire changesets infrastructure (config, patches, custom
GitHub Action, pre.json mode switching) and replace it with a simple,
stateless release system designed around two workflows:

Stable release:
  1. Run "create release PR" workflow — pick patch/minor/major
2. Workflow bumps all package.json versions, generates AI release notes,
     opens a PR
  3. Full CI test suite runs on the PR (lint, types, build, tests)
  4. Merge the PR → "publish release" workflow triggers automatically
  5. Publishes all @copilotkit/* packages to npm, creates git tag +
     GitHub Release with AI-generated notes

Pre-release:
  1. Run "publish prerelease" workflow — pick patch/minor/major
  2. Computes stateless version (e.g. 1.56.0-next.<timestamp>)
  3. Publishes to npm with "next" tag. No commits, no state files.

Key improvements over changesets:
- No pre.json mode switching or state corruption
- No patches to upstream packages
- No enter/exit prerelease ceremonies
- Version management is just "pick patch/minor/major"
- AI-generated release notes via Anthropic API
- SOC2 compliant — nothing auto-merges, release PR is the review gate
- Private packages automatically excluded via allowlist in config
This commit is contained in:
Tyler Slaton
2026-04-10 22:35:08 -07:00
committed by GitHub
57 changed files with 1347 additions and 18956 deletions
-8
View File
@@ -1,8 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
-51
View File
@@ -1,51 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"privatePackages": {
"tag": false,
"version": false
},
"access": "public",
"baseBranch": "origin/main",
"fixed": [
[
"@copilotkit/a2ui-renderer",
"@copilotkit/core",
"@copilotkit/react-core",
"@copilotkit/react-textarea",
"@copilotkit/react-ui",
"@copilotkit/runtime",
"@copilotkit/runtime-client-gql",
"@copilotkit/sdk-js",
"@copilotkit/shared",
"@copilotkit/sqlite-runner",
"@copilotkit/voice",
"@copilotkit/web-inspector"
]
],
"ignore": [
"@copilotkit-examples/*",
"@copilotkit-storybook/*",
"@copilotkit/angular-demo",
"@copilotkit/angular-demo-server",
"@copilotkit/demo-agents",
"@copilotkit/docs",
"@copilotkit/example-runtime-*",
"@copilotkit/next-pages-router",
"@copilotkit/node-express",
"@apps/node",
"agent-langraph-interrupt",
"demo",
"langgraph-js-starter",
"next-openai",
"next-pages-router",
"node-express",
"node-http",
"react-router-example",
"ui-protocols-demo",
"web-langraph-interrupt"
],
"linked": [],
"updateInternalDependencies": "patch"
}
@@ -1,6 +0,0 @@
{
"presets": [
["@babel/preset-env", { "targets": { "node": 10 } }],
"@babel/preset-typescript"
]
}
@@ -1,8 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -1,13 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@1.6.2/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "changesets/action" }
],
"commit": false,
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
@@ -1,24 +0,0 @@
name: Node CI
on: [pull_request]
jobs:
build:
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Install Dependencies
run: yarn --frozen-lockfile
- name: Test
run: yarn test
@@ -1,35 +0,0 @@
name: Version or Publish
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
changesets:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Install Dependencies
run: yarn --frozen-lockfile
- name: Build
run: yarn build
- name: Create Release Pull Request or Publish
id: changesets
uses: ./
with:
version: yarn bump
publish: yarn release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,4 +0,0 @@
node_modules
.parcel-cache
.cache
*.log
@@ -1,118 +0,0 @@
# @changesets/action
## 1.4.9
### Patch Changes
- [#415](https://github.com/changesets/action/pull/415) [`57ab80c`](https://github.com/changesets/action/commit/57ab80c61104c270bebc125910ae32da3a5aca46) Thanks [@benmccann](https://github.com/benmccann)! - Improve error message when attempting to publish without publish script defined
## 1.4.8
### Patch Changes
- [#393](https://github.com/changesets/action/pull/393) [`48ab0d2`](https://github.com/changesets/action/commit/48ab0d2f2e77ae169182d022591ef5c18c931ff2) Thanks [@s0](https://github.com/s0)! - Ensure the PR remains open when updated
- [#393](https://github.com/changesets/action/pull/393) [`48ab0d2`](https://github.com/changesets/action/commit/48ab0d2f2e77ae169182d022591ef5c18c931ff2) Thanks [@s0](https://github.com/s0)! - Switch to cheaper API for querying existing PRs
## 1.4.7
### Patch Changes
- [#255](https://github.com/changesets/action/pull/255) [`f2660aa`](https://github.com/changesets/action/commit/f2660aa7e78365f53dbeb4cfa774c1499ec6483a) Thanks [@ernestognw](https://github.com/ernestognw)! - Allow customize PR `branch` field
## 1.4.6
### Patch Changes
- [#350](https://github.com/changesets/action/pull/350) [`9385be9`](https://github.com/changesets/action/commit/9385be9e757839189ea5ee63ec4e3caa8a6ca71b) Thanks [@m-shaka](https://github.com/m-shaka)! - Bump the used node.js from 16 to 20
## 1.4.5
### Patch Changes
- [#282](https://github.com/changesets/action/pull/282) [`eb19e25`](https://github.com/changesets/action/commit/eb19e25e7797cf33dc2de4caa071e85a8057a0f0) Thanks [@mark-omarov](https://github.com/mark-omarov)! - Updated a few dependencies to patch the security vulnerabilities that were reported for their older versions.
## 1.4.4
### Patch Changes
- [#291](https://github.com/changesets/action/pull/291) [`db8a109`](https://github.com/changesets/action/commit/db8a1099bc0ba1dd6f46a5b9df4212e4f69e78c9) Thanks [@varl](https://github.com/varl)! - Wire up [`@octokit/plugin-throttling`](https://github.com/octokit/plugin-throttling.js) with all GitHub Octokit instances
## 1.4.3
### Patch Changes
- [#289](https://github.com/changesets/action/pull/289) [`8b28186`](https://github.com/changesets/action/commit/8b2818674de86a7fc69aebb9ed6b486ee32eb96e) Thanks [@varl](https://github.com/varl)! - Use logging provided by `@actions/core`
## 1.4.2
### Patch Changes
- [#286](https://github.com/changesets/action/pull/286) [`225a1e8`](https://github.com/changesets/action/commit/225a1e8cbcabb7b585174ba0ad806549db40d4cd) Thanks [@varl](https://github.com/varl)! - This patch implements the [`@octokit/plugin-throttling`](https://github.com/octokit/plugin-throttling.js) plugin and [wires
it up with the internal GitHub Octokit instance](https://github.com/actions/toolkit/tree/457303960f03375db6f033e214b9f90d79c3fe5c/packages/github#extending-the-octokit-instance).
This plugin is recommended by [the Octokit docs](://octokit.github.io/rest.js/v19#throttling) as it implements all the GitHub [best practices for integrators](https://docs.github.com/en/rest/guides/best-practices-for-integrators?apiVersion=2022-11-28).
This should help with `changesets/action` gitting spurious secondary rate limits and failing CI jobs, for which the only known workaround is to simply re-run the job.
## 1.4.1
### Patch Changes
- [#123](https://github.com/changesets/action/pull/123) [`b78f480`](https://github.com/changesets/action/commit/b78f48099899f0a853c5d9cd3feb21a5440babbd) Thanks [@Andarist](https://github.com/Andarist)! - Updated `@actions/*` dependencies to avoid using deprecated features of the runner.
## 1.4.0
### Minor Changes
- [#216](https://github.com/changesets/action/pull/216) [`398d7ed`](https://github.com/changesets/action/commit/398d7ed) Thanks [@quinnjn](https://github.com/quinnjn)! - Execute action with node16 instead of node12.
### Patch Changes
- [#228](https://github.com/changesets/action/pull/228) [`bff53cc`](https://github.com/changesets/action/commit/bff53cc50c1ebb33f8f558f9de2e0eb9a99230c6) Thanks [@iansan5653](https://github.com/iansan5653)! - Add `is:pull-request` to search query when looking for existing PR. This fixes an issue with user-owned PATs.
* [#206](https://github.com/changesets/action/pull/206) [`8c3f5f5`](https://github.com/changesets/action/commit/8c3f5f5637a95a2327e78d5dabcf357978aedcbb) Thanks [@glasser](https://github.com/glasser)! - Skip creating a PR when all existing changesets are empty.
## 1.3.0
### Minor Changes
- [#167](https://github.com/changesets/action/pull/167) [`993a0a0`](https://github.com/changesets/action/commit/993a0a090df78cee07481d3886dcd8b29deb9567) Thanks [@dmregister](https://github.com/dmregister)! - Added `pullRequestNumber` to the action's outputs
### Patch Changes
- [#157](https://github.com/changesets/action/pull/157) [`521c27b`](https://github.com/changesets/action/commit/521c27bf86ec53547d6a350d208fbbbc9d576fbc) Thanks [@emmenko](https://github.com/emmenko)! - Automatically adjust GitHub PR message if it exceeds a size limit of 60k characters by omitting some of the changelog information.
## 1.2.2
### Patch Changes
- [#161](https://github.com/changesets/action/pull/161) [`52c9ce7`](https://github.com/changesets/action/commit/52c9ce75d9d8a14ea2d75e4157b0c15b7a4ac313) Thanks [@bicknellr](https://github.com/bicknellr)! - Change directory to `cwd` before running git user setup. This fixes an issue when the action starts its execution not in a git repository.
## 1.2.1
### Patch Changes
- [#144](https://github.com/changesets/action/pull/144) [`898d125`](https://github.com/changesets/action/commit/898d125cee6ba00c6a11b6cadca512752c6c910c) Thanks [@Andarist](https://github.com/Andarist)! - Updated all Changesets dependencies. This should fix parsing issues for completely empty summaries that has been fixed in `@changesets/parse@0.3.11`.
## 1.2.0
### Minor Changes
- [#130](https://github.com/changesets/action/pull/130) [`5c0997b`](https://github.com/changesets/action/commit/5c0997b25e175ecf5e1723ba07210bbcea5d92fb) Thanks [@akphi](https://github.com/akphi)! - Added `createGithubReleases` input option (defaults to `true`) to control whether to create Github releases during publish or not.
* [#134](https://github.com/changesets/action/pull/134) [`1ed9bc2`](https://github.com/changesets/action/commit/1ed9bc24b7a56462c183eb815c8f4bdf0e2e5785) Thanks [@dmregister](https://github.com/dmregister)! - Added `cwd` input option that can be used in projects that are not in the root directory.
## 1.1.0
### Minor Changes
- [#128](https://github.com/changesets/action/pull/128) [`1937303`](https://github.com/changesets/action/commit/19373036c4bad4b0183344b6f2623a3b0e42da6c) Thanks [@dhruvdutt](https://github.com/dhruvdutt)! - Setup the git user in the local config instead of the global one.
* [#131](https://github.com/changesets/action/pull/131) [`d3db9ec`](https://github.com/changesets/action/commit/d3db9eceaf41d42c56d5370d504c86851627188f) Thanks [@jacklesliewise](https://github.com/jacklesliewise)! - Added `setupGitUser` option to enable or disable setting up a default git user
## 1.0.0
### Major Changes
- [#118](https://github.com/changesets/action/pull/118) [`05c863d`](https://github.com/changesets/action/commit/05c863d3f980125585016a593b5cb45b27d19c2c) Thanks [@Andarist](https://github.com/Andarist)! - From now on this action will be released using the Changesets-based workflow (using itself). Thanks to that we'll have a good release history. The users will be able to find specific versions of the action and will be able to track changes over time. It also improves the security as the build artifact will always get built in the CI environment, using a frozen lockfile.
-217
View File
@@ -1,217 +0,0 @@
# Changesets Release Action
This action for [Changesets](https://github.com/atlassian/changesets) creates a pull request with all of the package versions updated and changelogs updated and when there are new changesets on [your configured `baseBranch`](https://github.com/changesets/changesets/blob/main/docs/config-file-options.md#basebranch-git-branch-name), the PR will be updated. When you're ready, you can merge the pull request and you can either publish the packages to npm manually or setup the action to do it for you.
## Usage
### Inputs
- publish - The command to use to build and publish packages
- version - The command to update version, edit CHANGELOG, read and delete changesets. Default to `changeset version` if not provided
- commit - The commit message to use. Default to `Version Packages`
- title - The pull request title. Default to `Version Packages`
- setupGitUser - Sets up the git user for commits as `"github-actions[bot]"`. Default to `true`
- createGithubReleases - A boolean value to indicate whether to create Github releases after `publish` or not. Default to `true`
- cwd - Changes node's `process.cwd()` if the project is not located on the root. Default to `process.cwd()`
### Outputs
- published - A boolean value to indicate whether a publishing has happened or not
- publishedPackages - A JSON array to present the published packages. The format is `[{"name": "@xx/xx", "version": "1.2.0"}, {"name": "@xx/xy", "version": "0.8.9"}]`
### Example workflow:
#### Without Publishing
Create a file at `.github/workflows/release.yml` with the following content.
```yml
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup Node.js 20
uses: actions/setup-node@v3
with:
node-version: 20
- name: Install Dependencies
run: yarn
- name: Create Release Pull Request
uses: changesets/action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
#### With Publishing
Before you can setup this action with publishing, you'll need to have an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) that can publish the packages in the repo you're setting up the action for and doesn't have 2FA on publish enabled ([2FA on auth can be enabled](https://docs.npmjs.com/about-two-factor-authentication)). You'll also need to [add it as a secret on your GitHub repo](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) with the name `NPM_TOKEN`. Once you've done that, you can create a file at `.github/workflows/release.yml` with the following content.
```yml
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x
- name: Install Dependencies
run: yarn
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: yarn release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Send a Slack notification if a publish happens
if: steps.changesets.outputs.published == 'true'
# You can do something when a publish happens.
run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!"
```
By default the GitHub Action creates a `.npmrc` file with the following content:
```
//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}
```
However, if a `.npmrc` file is found, the GitHub Action does not recreate the file. This is useful if you need to configure the `.npmrc` file on your own.
For example, you can add a step before running the Changesets GitHub Action:
```yml
- name: Creating .npmrc
run: |
cat << EOF > "$HOME/.npmrc"
//registry.npmjs.org/:_authToken=$NPM_TOKEN
EOF
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
```
#### Custom Publishing
If you want to hook into when publishing should occur but have your own publishing functionality, you can utilize the `hasChangesets` output.
Note that you might need to account for things already being published in your script because a commit without any new changesets can always land on your base branch after a successful publish. In such a case you need to figure out on your own how to skip over the actual publishing logic or handle errors gracefully as most package registries won't allow you to publish over already published version.
```yml
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x
- name: Install Dependencies
run: yarn
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish
if: steps.changesets.outputs.hasChangesets == 'false'
# You can do something when a publish should happen.
run: yarn publish
```
#### With version script
If you need to add additional logic to the version command, you can do so by using a version script.
If the version script is present, this action will run that script instead of `changeset version`, so please make sure that your script calls `changeset version` at some point. All the changes made by the script will be included in the PR.
```yml
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x
- name: Install Dependencies
run: yarn
- name: Create Release Pull Request
uses: changesets/action@v1
with:
# this expects you to have a npm script called version that runs some logic and then calls `changeset version`.
version: yarn version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
#### With Yarn 2 / Plug'n'Play
If you are using [Yarn Plug'n'Play](https://yarnpkg.com/features/pnp), you should use a custom `version` command so that the action can resolve the `changeset` CLI:
```yaml
- uses: changesets/action@v1
with:
version: yarn changeset version
...
```
@@ -1,4 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@1.3.0/schema.json",
"ignore": ["ignored-package-pkg-a"]
}
@@ -1,8 +0,0 @@
{
"name": "ignored-package",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
]
}
@@ -1,7 +0,0 @@
{
"name": "ignored-package-pkg-a",
"version": "1.0.0",
"dependencies": {
"ignored-package-pkg-b": "1.0.0"
}
}
@@ -1,4 +0,0 @@
{
"name": "ignored-package-pkg-b",
"version": "1.0.0"
}
@@ -1,3 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@1.3.0/schema.json"
}
@@ -1,8 +0,0 @@
{
"name": "simple-project",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
]
}
@@ -1,7 +0,0 @@
{
"name": "simple-project-pkg-a",
"version": "1.0.0",
"dependencies": {
"simple-project-pkg-b": "1.0.0"
}
}
@@ -1,4 +0,0 @@
{
"name": "simple-project-pkg-b",
"version": "1.0.0"
}
@@ -1,46 +0,0 @@
name: Changesets
description: A GitHub action to automate releases with Changesets
runs:
using: "node20"
main: "dist/index.js"
inputs:
publish:
description: "The command to use to build and publish packages"
required: false
version:
description: "The command to update version, edit CHANGELOG, read and delete changesets. Default to `changeset version` if not provided"
required: false
cwd:
description: Sets the cwd for the node process. Default to `process.cwd()`
required: false
commit:
description: |
The commit message. Default to `Version Packages`
required: false
title:
description: The pull request title. Default to `Version Packages`
required: false
setupGitUser:
description: Sets up the git user for commits as `"github-actions[bot]"`. Default to `true`
required: false
default: true
createGithubReleases:
description: "A boolean value to indicate whether to create Github releases after `publish` or not"
required: false
default: true
branch:
description: Sets the branch in which the action will run. Default to `github.ref_name` if not provided
required: false
outputs:
published:
description: A boolean value to indicate whether a publishing is happened or not
publishedPackages:
description: >
A JSON array to present the published packages. The format is `[{"name": "@xx/xx", "version": "1.2.0"}, {"name": "@xx/xy", "version": "0.8.9"}]`
hasChangesets:
description: A boolean about whether there were changesets. Useful if you want to create your own publishing functionality.
pullRequestNumber:
description: The pull request number that was created or updated
branding:
icon: "package"
color: "blue"
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -1,61 +0,0 @@
{
"name": "@changesets/action",
"version": "1.4.9",
"license": "MIT",
"main": "dist/index.js",
"scripts": {
"build": "ncc build src/index.ts -o dist --transpile-only --minify",
"test": "jest",
"test:watch": "yarn test --watch",
"changeset": "changeset",
"bump": "node ./scripts/bump.js",
"release": "node ./scripts/release.js"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1",
"@changesets/pre": "^1.0.9",
"@changesets/read": "^0.5.3",
"@manypkg/get-packages": "^1.1.3",
"@octokit/plugin-throttling": "^5.2.1",
"fs-extra": "^8.1.0",
"mdast-util-to-string": "^1.0.6",
"remark-parse": "^7.0.1",
"remark-stringify": "^7.0.3",
"resolve-from": "^5.0.0",
"semver": "^7.5.3",
"unified": "^8.3.2"
},
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/preset-env": "^7.13.10",
"@babel/preset-typescript": "^7.13.0",
"@changesets/changelog-github": "^0.4.2",
"@changesets/cli": "^2.20.0",
"@changesets/write": "^0.1.6",
"@types/fs-extra": "^8.0.0",
"@types/jest": "^29.5.1",
"@types/node": "^20.11.17",
"@types/semver": "^7.5.0",
"@vercel/ncc": "^0.36.1",
"babel-jest": "^29.5.0",
"fixturez": "^1.1.0",
"husky": "^3.0.3",
"jest": "^29.5.0",
"prettier": "^2.0.5",
"typescript": "^5.0.4"
},
"resolutions": {
"**/@octokit/core": "4.2.0",
"trim": "^0.0.3",
"y18n": "^4.0.1"
},
"husky": {
"hooks": {}
},
"prettier": {},
"engines": {
"node": ">= 20"
}
}
@@ -1,19 +0,0 @@
const fs = require("fs");
const path = require("path");
const { exec } = require("@actions/exec");
process.chdir(path.join(__dirname, ".."));
(async () => {
await exec("changeset", ["version"]);
const releaseLine = `v${require("../package.json").version.split(".")[0]}`;
const readmePath = path.join(__dirname, "..", "README.md");
const content = fs.readFileSync(readmePath, "utf8");
const updatedContent = content.replace(
/changesets\/action@[^\s]+/g,
`changesets/action@${releaseLine}`,
);
fs.writeFileSync(readmePath, updatedContent);
})();
@@ -1,41 +0,0 @@
const path = require("path");
const { exec, getExecOutput } = require("@actions/exec");
const { version } = require("../package.json");
const tag = `v${version}`;
const releaseLine = `v${version.split(".")[0]}`;
process.chdir(path.join(__dirname, ".."));
(async () => {
const { exitCode, stderr } = await getExecOutput(
`git`,
["ls-remote", "--exit-code", "origin", "--tags", `refs/tags/${tag}`],
{
ignoreReturnCode: true,
},
);
if (exitCode === 0) {
console.log(
`Action is not being published because version ${tag} is already published`,
);
return;
}
if (exitCode !== 2) {
throw new Error(`git ls-remote exited with ${exitCode}:\n${stderr}`);
}
await exec("git", ["checkout", "--detach"]);
await exec("git", ["add", "--force", "dist"]);
await exec("git", ["commit", "-m", tag]);
await exec("changeset", ["tag"]);
await exec("git", [
"push",
"--force",
"--follow-tags",
"origin",
`HEAD:refs/heads/${releaseLine}`,
]);
})();
@@ -1,59 +0,0 @@
import { exec, getExecOutput } from "@actions/exec";
export const setupUser = async () => {
await exec("git", ["config", "user.name", `"github-actions[bot]"`]);
await exec("git", [
"config",
"user.email",
`"github-actions[bot]@users.noreply.github.com"`,
]);
};
export const pullBranch = async (branch: string) => {
await exec("git", ["pull", "origin", branch]);
};
export const push = async (
branch: string,
{ force }: { force?: boolean } = {},
) => {
await exec(
"git",
["push", "origin", `HEAD:${branch}`, force && "--force"].filter<string>(
Boolean as any,
),
);
};
export const pushTags = async () => {
await exec("git", ["push", "origin", "--tags"]);
};
export const switchToMaybeExistingBranch = async (branch: string) => {
let { stderr } = await getExecOutput("git", ["checkout", branch], {
ignoreReturnCode: true,
});
let isCreatingBranch = !stderr
.toString()
.includes(`Switched to a new branch '${branch}'`);
if (isCreatingBranch) {
await exec("git", ["checkout", "-b", branch]);
}
};
export const reset = async (
pathSpec: string,
mode: "hard" | "soft" | "mixed" = "hard",
) => {
await exec("git", ["reset", `--${mode}`, pathSpec]);
};
export const commitAll = async (message: string) => {
await exec("git", ["add", "."]);
await exec("git", ["commit", "-m", message]);
};
export const checkIfClean = async (): Promise<boolean> => {
const { stdout } = await getExecOutput("git", ["status", "--porcelain"]);
return !stdout.length;
};
@@ -1,128 +0,0 @@
import * as core from "@actions/core";
import fs from "fs-extra";
import * as gitUtils from "./gitUtils";
import { runPublish, runVersion } from "./run";
import readChangesetState from "./readChangesetState";
const getOptionalInput = (name: string) => core.getInput(name) || undefined;
(async () => {
let githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
core.setFailed("Please add the GITHUB_TOKEN to the changesets action");
return;
}
const inputCwd = core.getInput("cwd");
if (inputCwd) {
core.info("changing directory to the one given as the input");
process.chdir(inputCwd);
}
let setupGitUser = core.getBooleanInput("setupGitUser");
if (setupGitUser) {
core.info("setting git user");
await gitUtils.setupUser();
}
core.info("setting GitHub credentials");
await fs.writeFile(
`${process.env.HOME}/.netrc`,
`machine github.com\nlogin github-actions[bot]\npassword ${githubToken}`,
);
let { changesets } = await readChangesetState();
let publishScript = core.getInput("publish");
let hasChangesets = changesets.length !== 0;
const hasNonEmptyChangesets = changesets.some(
(changeset) => changeset.releases.length > 0,
);
let hasPublishScript = !!publishScript;
core.setOutput("published", "false");
core.setOutput("publishedPackages", "[]");
core.setOutput("hasChangesets", String(hasChangesets));
switch (true) {
case !hasChangesets && !hasPublishScript:
core.info(
"No changesets present or were removed by merging release PR. Not publishing because no publish script found.",
);
return;
case !hasChangesets && hasPublishScript: {
core.info(
"No changesets found. Attempting to publish any unpublished packages to npm",
);
let userNpmrcPath = `${process.env.HOME}/.npmrc`;
if (fs.existsSync(userNpmrcPath)) {
core.info("Found existing user .npmrc file");
const userNpmrcContent = await fs.readFile(userNpmrcPath, "utf8");
const authLine = userNpmrcContent.split("\n").find((line) => {
// check based on https://github.com/npm/cli/blob/8f8f71e4dd5ee66b3b17888faad5a7bf6c657eed/test/lib/adduser.js#L103-L105
return /^\s*\/\/registry\.npmjs\.org\/:[_-]authToken=/i.test(line);
});
if (authLine) {
core.info(
"Found existing auth token for the npm registry in the user .npmrc file",
);
} else {
core.info(
"Didn't find existing auth token for the npm registry in the user .npmrc file, creating one",
);
fs.appendFileSync(
userNpmrcPath,
`\n//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`,
);
}
} else {
core.info("No user .npmrc file found, creating one");
fs.writeFileSync(
userNpmrcPath,
`//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`,
);
}
const result = await runPublish({
script: publishScript,
githubToken,
createGithubReleases: core.getBooleanInput("createGithubReleases"),
});
if (result.published) {
core.setOutput("published", "true");
core.setOutput(
"publishedPackages",
JSON.stringify(result.publishedPackages),
);
}
return;
}
case hasChangesets && !hasNonEmptyChangesets:
core.info("All changesets are empty; not creating PR");
return;
case hasChangesets:
const { pullRequestNumber } = await runVersion({
script: getOptionalInput("version"),
githubToken,
prTitle: getOptionalInput("title"),
commitMessage: getOptionalInput("commit"),
hasPublishScript,
branch: getOptionalInput("branch"),
});
if (pullRequestNumber !== -1) {
core.setOutput("pullRequestNumber", String(pullRequestNumber));
} else {
core.setOutput("isNextAutomerge", String(true));
}
return;
}
})().catch((err) => {
core.error(err);
core.setFailed(err.message);
});
@@ -1,29 +0,0 @@
import { PreState, NewChangeset } from "@changesets/types";
import { readPreState } from "@changesets/pre";
import readChangesets from "@changesets/read";
export type ChangesetState = {
preState: PreState | undefined;
changesets: NewChangeset[];
};
export default async function readChangesetState(
cwd: string = process.cwd(),
): Promise<ChangesetState> {
let preState = await readPreState(cwd);
let changesets = await readChangesets(cwd);
if (preState !== undefined && preState.mode === "pre") {
let changesetsToFilter = new Set(preState.changesets);
return {
preState,
changesets: changesets.filter((x) => !changesetsToFilter.has(x.id)),
};
}
return {
preState: undefined,
changesets,
};
}
@@ -1,278 +0,0 @@
import fixturez from "fixturez";
import * as github from "@actions/github";
import * as githubUtils from "@actions/github/lib/utils";
import fs from "fs-extra";
import path from "path";
import writeChangeset from "@changesets/write";
import { Changeset } from "@changesets/types";
import { runVersion } from "./run";
jest.mock("@actions/github", () => ({
context: {
repo: {
owner: "changesets",
repo: "action",
},
ref: "refs/heads/some-branch",
sha: "xeac7",
},
}));
jest.mock("@actions/github/lib/utils", () => ({
GitHub: {
plugin: () => {
// function necessary to be used as constructor
return function () {
return {
rest: mockedGithubMethods,
};
};
},
},
getOctokitOptions: jest.fn(),
}));
jest.mock("./gitUtils");
let mockedGithubMethods = {
pulls: {
create: jest.fn(),
list: jest.fn(),
},
repos: {
createRelease: jest.fn(),
},
};
let f = fixturez(__dirname);
const linkNodeModules = async (cwd: string) => {
await fs.symlink(
path.join(__dirname, "..", "node_modules"),
path.join(cwd, "node_modules"),
);
};
const writeChangesets = (changesets: Changeset[], cwd: string) => {
return Promise.all(changesets.map((commit) => writeChangeset(commit, cwd)));
};
beforeEach(() => {
jest.clearAllMocks();
});
describe("version", () => {
it("creates simple PR", async () => {
let cwd = f.copy("simple-project");
linkNodeModules(cwd);
mockedGithubMethods.pulls.list.mockImplementationOnce(() => ({ data: [] }));
mockedGithubMethods.pulls.create.mockImplementationOnce(() => ({
data: { number: 123 },
}));
await writeChangesets(
[
{
releases: [
{
name: "simple-project-pkg-a",
type: "minor",
},
{
name: "simple-project-pkg-b",
type: "minor",
},
],
summary: "Awesome feature",
},
],
cwd,
);
await runVersion({
githubToken: "@@GITHUB_TOKEN",
cwd,
});
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
});
it("only includes bumped packages in the PR body", async () => {
let cwd = f.copy("simple-project");
linkNodeModules(cwd);
mockedGithubMethods.pulls.list.mockImplementationOnce(() => ({ data: [] }));
mockedGithubMethods.pulls.create.mockImplementationOnce(() => ({
data: { number: 123 },
}));
await writeChangesets(
[
{
releases: [
{
name: "simple-project-pkg-a",
type: "minor",
},
],
summary: "Awesome feature",
},
],
cwd,
);
await runVersion({
githubToken: "@@GITHUB_TOKEN",
cwd,
});
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
});
it("doesn't include ignored package that got a dependency update in the PR body", async () => {
let cwd = f.copy("ignored-package");
linkNodeModules(cwd);
mockedGithubMethods.pulls.list.mockImplementationOnce(() => ({ data: [] }));
mockedGithubMethods.pulls.create.mockImplementationOnce(() => ({
data: { number: 123 },
}));
await writeChangesets(
[
{
releases: [
{
name: "ignored-package-pkg-b",
type: "minor",
},
],
summary: "Awesome feature",
},
],
cwd,
);
await runVersion({
githubToken: "@@GITHUB_TOKEN",
cwd,
});
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
});
it("does not include changelog entries if full message exceeds size limit", async () => {
let cwd = f.copy("simple-project");
linkNodeModules(cwd);
mockedGithubMethods.pulls.list.mockImplementationOnce(() => ({ data: [] }));
mockedGithubMethods.pulls.create.mockImplementationOnce(() => ({
data: { number: 123 },
}));
await writeChangesets(
[
{
releases: [
{
name: "simple-project-pkg-a",
type: "minor",
},
],
summary: `# Non manus superum
## Nec cornibus aequa numinis multo onerosior adde
Lorem markdownum undas consumpserat malas, nec est lupus; memorant gentisque ab
limine auctore. Eatque et promptu deficit, quam videtur aequa est **faciat**,
locus. Potentia deus habebat pia quam qui coniuge frater, tibi habent fertque
viribus. E et cognoscere arcus, lacus aut sic pro crimina fuit tum **auxilium**
dictis, qua, in.
In modo. Nomen illa membra.
> Corpora gratissima parens montibus tum coeperat qua remulus caelum Helenamque?
> Non poenae modulatur Amathunta in concita superi, procerum pariter rapto cornu
> munera. Perrhaebum parvo manus contingere, morari, spes per totiens ut
> dividite proculcat facit, visa.
Adspicit sequitur diffamatamque superi Phoebo qua quin lammina utque: per? Exit
decus aut hac inpia, seducta mirantia extremo. Vidi pedes vetus. Saturnius
fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.
`,
},
],
cwd,
);
await runVersion({
githubToken: "@@GITHUB_TOKEN",
cwd,
prBodyMaxCharacters: 1000,
});
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
expect(mockedGithubMethods.pulls.create.mock.calls[0][0].body).toMatch(
/The changelog information of each package has been omitted from this message/,
);
});
it("does not include any release information if a message with simplified release info exceeds size limit", async () => {
let cwd = f.copy("simple-project");
linkNodeModules(cwd);
mockedGithubMethods.pulls.list.mockImplementationOnce(() => ({ data: [] }));
mockedGithubMethods.pulls.create.mockImplementationOnce(() => ({
data: { number: 123 },
}));
await writeChangesets(
[
{
releases: [
{
name: "simple-project-pkg-a",
type: "minor",
},
],
summary: `# Non manus superum
## Nec cornibus aequa numinis multo onerosior adde
Lorem markdownum undas consumpserat malas, nec est lupus; memorant gentisque ab
limine auctore. Eatque et promptu deficit, quam videtur aequa est **faciat**,
locus. Potentia deus habebat pia quam qui coniuge frater, tibi habent fertque
viribus. E et cognoscere arcus, lacus aut sic pro crimina fuit tum **auxilium**
dictis, qua, in.
In modo. Nomen illa membra.
> Corpora gratissima parens montibus tum coeperat qua remulus caelum Helenamque?
> Non poenae modulatur Amathunta in concita superi, procerum pariter rapto cornu
> munera. Perrhaebum parvo manus contingere, morari, spes per totiens ut
> dividite proculcat facit, visa.
Adspicit sequitur diffamatamque superi Phoebo qua quin lammina utque: per? Exit
decus aut hac inpia, seducta mirantia extremo. Vidi pedes vetus. Saturnius
fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.
`,
},
],
cwd,
);
await runVersion({
githubToken: "@@GITHUB_TOKEN",
cwd,
prBodyMaxCharacters: 500,
});
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
expect(mockedGithubMethods.pulls.create.mock.calls[0][0].body).toMatch(
/All release information have been omitted from this message, as the content exceeds the size limit/,
);
});
});
@@ -1,463 +0,0 @@
import { exec, getExecOutput } from "@actions/exec";
import { GitHub, getOctokitOptions } from "@actions/github/lib/utils";
import * as github from "@actions/github";
import * as core from "@actions/core";
import fs from "fs-extra";
import { getPackages, Package } from "@manypkg/get-packages";
import path from "path";
import * as semver from "semver";
import { PreState } from "@changesets/types";
import {
getChangelogEntry,
getChangedPackages,
sortTheThings,
getVersionsByDirectory,
} from "./utils";
import * as gitUtils from "./gitUtils";
import readChangesetState from "./readChangesetState";
import resolveFrom from "resolve-from";
import { throttling } from "@octokit/plugin-throttling";
// GitHub Issues/PRs messages have a max size limit on the
// message body payload.
// `body is too long (maximum is 65536 characters)`.
// To avoid that, we ensure to cap the message to 60k chars.
const MAX_CHARACTERS_PER_MESSAGE = 60000;
const setupOctokit = (githubToken: string) => {
return new (GitHub.plugin(throttling))(
getOctokitOptions(githubToken, {
throttle: {
onRateLimit: (retryAfter, options: any, octokit, retryCount) => {
core.warning(
`Request quota exhausted for request ${options.method} ${options.url}`,
);
if (retryCount <= 2) {
core.info(`Retrying after ${retryAfter} seconds!`);
return true;
}
},
onSecondaryRateLimit: (
retryAfter,
options: any,
octokit,
retryCount,
) => {
core.warning(
`SecondaryRateLimit detected for request ${options.method} ${options.url}`,
);
if (retryCount <= 2) {
core.info(`Retrying after ${retryAfter} seconds!`);
return true;
}
},
},
}),
);
};
const createRelease = async (
octokit: ReturnType<typeof setupOctokit>,
{ pkg, tagName }: { pkg: Package; tagName: string },
) => {
try {
let changelogFileName = path.join(pkg.dir, "CHANGELOG.md");
let changelog = await fs.readFile(changelogFileName, "utf8");
let changelogEntry = getChangelogEntry(changelog, pkg.packageJson.version);
if (!changelogEntry) {
// we can find a changelog but not the entry for this version
// if this is true, something has probably gone wrong
throw new Error(
`Could not find changelog entry for ${pkg.packageJson.name}@${pkg.packageJson.version}`,
);
}
await octokit.rest.repos.createRelease({
name: tagName,
tag_name: tagName,
body: changelogEntry.content,
prerelease: pkg.packageJson.version.includes("-"),
...github.context.repo,
});
} catch (err) {
// if we can't find a changelog, the user has probably disabled changelogs
if (
err &&
typeof err === "object" &&
"code" in err &&
err.code !== "ENOENT"
) {
throw err;
}
}
};
type PublishOptions = {
script: string;
githubToken: string;
createGithubReleases: boolean;
cwd?: string;
};
type PublishedPackage = { name: string; version: string };
type PublishResult =
| {
published: true;
publishedPackages: PublishedPackage[];
}
| {
published: false;
};
export async function runPublish({
script,
githubToken,
createGithubReleases,
cwd = process.cwd(),
}: PublishOptions): Promise<PublishResult> {
const octokit = setupOctokit(githubToken);
let [publishCommand, ...publishArgs] = script.split(/\s+/);
let changesetPublishOutput = await getExecOutput(
publishCommand,
publishArgs,
{ cwd },
);
await gitUtils.pushTags();
let { packages, tool } = await getPackages(cwd);
let releasedPackages: Package[] = [];
if (tool !== "root") {
let newTagRegex = /New tag:\s+(@[^/]+\/[^@]+|[^/]+)@([^\s]+)/;
let packagesByName = new Map(packages.map((x) => [x.packageJson.name, x]));
for (let line of changesetPublishOutput.stdout.split("\n")) {
let match = line.match(newTagRegex);
if (match === null) {
continue;
}
let pkgName = match[1];
let pkg = packagesByName.get(pkgName);
if (pkg === undefined) {
throw new Error(
`Package "${pkgName}" not found.` +
"This is probably a bug in the action, please open an issue",
);
}
releasedPackages.push(pkg);
}
if (createGithubReleases) {
await Promise.all(
releasedPackages.map((pkg) =>
createRelease(octokit, {
pkg,
tagName: `${pkg.packageJson.name}@${pkg.packageJson.version}`,
}),
),
);
}
} else {
if (packages.length === 0) {
throw new Error(
`No package found.` +
"This is probably a bug in the action, please open an issue",
);
}
let pkg = packages[0];
let newTagRegex = /New tag:/;
for (let line of changesetPublishOutput.stdout.split("\n")) {
let match = line.match(newTagRegex);
if (match) {
releasedPackages.push(pkg);
if (createGithubReleases) {
await createRelease(octokit, {
pkg,
tagName: `v${pkg.packageJson.version}`,
});
}
break;
}
}
}
if (releasedPackages.length) {
return {
published: true,
publishedPackages: releasedPackages.map((pkg) => ({
name: pkg.packageJson.name,
version: pkg.packageJson.version,
})),
};
}
return { published: false };
}
const requireChangesetsCliPkgJson = (cwd: string) => {
try {
return require(resolveFrom(cwd, "@changesets/cli/package.json"));
} catch (err) {
if (
err &&
typeof err === "object" &&
"code" in err &&
err.code === "MODULE_NOT_FOUND"
) {
throw new Error(
`Have you forgotten to install \`@changesets/cli\` in "${cwd}"?`, { cause: err },
);
}
throw err;
}
};
type GetMessageOptions = {
hasPublishScript: boolean;
branch: string;
changedPackagesInfo: {
highestLevel: number;
private: boolean;
content: string;
header: string;
}[];
prBodyMaxCharacters: number;
preState?: PreState;
};
export async function getVersionPrBody({
hasPublishScript,
preState,
changedPackagesInfo,
prBodyMaxCharacters,
branch,
}: GetMessageOptions) {
let messageHeader = `This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and ${
hasPublishScript
? `the packages will be published to npm automatically`
: `publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing)`
}. If you're not ready to do a release yet, that's fine, whenever you add more changesets to ${branch}, this PR will be updated.
`;
let messagePrestate = !!preState
? `⚠️⚠️⚠️⚠️⚠️⚠️
\`${branch}\` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run \`changeset pre exit\` on \`${branch}\`.
⚠️⚠️⚠️⚠️⚠️⚠️
`
: "";
let messageReleasesHeading = `# Releases`;
let fullMessage = [
messageHeader,
messagePrestate,
messageReleasesHeading,
...changedPackagesInfo.map((info) => `${info.header}\n\n${info.content}`),
].join("\n");
// Check that the message does not exceed the size limit.
// If not, omit the changelog entries of each package.
if (fullMessage.length > prBodyMaxCharacters) {
fullMessage = [
messageHeader,
messagePrestate,
messageReleasesHeading,
`\n> The changelog information of each package has been omitted from this message, as the content exceeds the size limit.\n`,
...changedPackagesInfo.map((info) => `${info.header}\n\n`),
].join("\n");
}
// Check (again) that the message is within the size limit.
// If not, omit all release content this time.
if (fullMessage.length > prBodyMaxCharacters) {
fullMessage = [
messageHeader,
messagePrestate,
messageReleasesHeading,
`\n> All release information have been omitted from this message, as the content exceeds the size limit.`,
].join("\n");
}
return fullMessage;
}
type VersionOptions = {
script?: string;
githubToken: string;
cwd?: string;
prTitle?: string;
commitMessage?: string;
hasPublishScript?: boolean;
prBodyMaxCharacters?: number;
branch?: string;
};
type RunVersionResult = {
pullRequestNumber: number;
};
export async function runVersion({
script,
githubToken,
cwd = process.cwd(),
prTitle = "Version Packages",
commitMessage = "Version Packages",
hasPublishScript = false,
prBodyMaxCharacters = MAX_CHARACTERS_PER_MESSAGE,
branch,
}: VersionOptions): Promise<RunVersionResult> {
const octokit = setupOctokit(githubToken);
let repo = `${github.context.repo.owner}/${github.context.repo.repo}`;
branch = branch ?? github.context.ref.replace("refs/heads/", "");
let versionBranch = `changeset-release/${branch}`;
let { preState } = await readChangesetState(cwd);
await gitUtils.switchToMaybeExistingBranch(versionBranch);
await gitUtils.reset(github.context.sha);
let versionsByDirectory = await getVersionsByDirectory(cwd);
if (script) {
let [versionCommand, ...versionArgs] = script.split(/\s+/);
await exec(versionCommand, versionArgs, { cwd });
} else {
let changesetsCliPkgJson = requireChangesetsCliPkgJson(cwd);
let cmd = semver.lt(changesetsCliPkgJson.version, "2.0.0")
? "bump"
: "version";
await exec("node", [resolveFrom(cwd, "@changesets/cli/bin.js"), cmd], {
cwd,
});
}
const existingPullRequestsPromise = octokit.rest.pulls.list({
...github.context.repo,
state: "open",
head: `${github.context.repo.owner}:${versionBranch}`,
base: branch,
});
let changedPackages = await getChangedPackages(cwd, versionsByDirectory);
let changedPackagesInfoPromises = Promise.all(
changedPackages.map(async (pkg) => {
let changelogContents = await fs.readFile(
path.join(pkg.dir, "CHANGELOG.md"),
"utf8",
);
let entry = getChangelogEntry(changelogContents, pkg.packageJson.version);
return {
highestLevel: entry.highestLevel,
private: !!pkg.packageJson.private,
content: entry.content,
header: `## ${pkg.packageJson.name}@${pkg.packageJson.version}`,
};
}),
);
const finalPrTitle = `${prTitle}${!!preState ? ` (${preState.tag})` : ""}`;
const isNext = preState?.tag === "next";
// project with `commit: true` setting could have already committed files
if (!(await gitUtils.checkIfClean())) {
const finalCommitMessage = `${commitMessage}${
!!preState ? ` (${preState.tag})` : ""
}`;
await gitUtils.commitAll(finalCommitMessage);
}
await gitUtils.push(versionBranch, { force: true });
let existingPullRequests = await existingPullRequestsPromise;
core.info(JSON.stringify(existingPullRequests.data, null, 2));
const changedPackagesInfo = (await changedPackagesInfoPromises)
.filter((x) => x)
.sort(sortTheThings);
let prBody = await getVersionPrBody({
hasPublishScript,
preState,
branch,
changedPackagesInfo,
prBodyMaxCharacters,
});
if (isNext) {
core.info("creating pull request for next release");
const { data: newPullRequest } = await octokit.rest.pulls.create({
base: branch,
head: versionBranch,
title: finalPrTitle,
body: prBody,
...github.context.repo,
});
// Add a special label
await octokit.rest.issues.addLabels({
...github.context.repo,
issue_number: newPullRequest.number,
labels: ["automated-version-pr"],
});
// Run Release workflow
await octokit.rest.actions.createWorkflowDispatch({
...github.context.repo,
workflow_id: "release.yml",
ref: versionBranch,
});
// Immediately merge it
await octokit.rest.pulls.merge({
...github.context.repo,
pull_number: newPullRequest.number,
merge_method: "squash",
commit_title: finalPrTitle,
});
return {
pullRequestNumber: newPullRequest.number,
};
} else {
if (existingPullRequests.data.length === 0) {
core.info("creating pull request");
const { data: newPullRequest } = await octokit.rest.pulls.create({
base: branch,
head: versionBranch,
title: finalPrTitle,
body: prBody,
...github.context.repo,
});
return {
pullRequestNumber: newPullRequest.number,
};
} else {
const [pullRequest] = existingPullRequests.data;
core.info(`updating found pull request #${pullRequest.number}`);
await octokit.rest.pulls.update({
pull_number: pullRequest.number,
title: finalPrTitle,
body: prBody,
...github.context.repo,
state: "open",
});
return {
pullRequestNumber: pullRequest.number,
};
}
}
}
@@ -1,101 +0,0 @@
import { getChangelogEntry, BumpLevels, sortTheThings } from "./utils";
let changelog = `# @keystone-alpha/email
## 3.0.1
### Patch Changes
- [19fe6c1b](https://github.com/keystonejs/keystone-5/commit/19fe6c1b):
Move frontmatter in docs into comments
## 3.0.0
### Major Changes
- [2164a779](https://github.com/keystonejs/keystone-5/commit/2164a779):
- Replace jade with pug because Jade was renamed to Pug, and \`jade\` package is outdated
### Patch Changes
- [81dc0be5](https://github.com/keystonejs/keystone-5/commit/81dc0be5):
- Update dependencies
## 2.0.0
- [patch][b69fb9b7](https://github.com/keystonejs/keystone-5/commit/b69fb9b7):
- Update dev devependencies
- [major][f97e4ecf](https://github.com/keystonejs/keystone-5/commit/f97e4ecf):
- Export { emailSender } as the API, rather than a default export
## 1.0.2
- [patch][7417ea3a](https://github.com/keystonejs/keystone-5/commit/7417ea3a):
- Update patch-level dependencies
## 1.0.1
- [patch][1f0bc236](https://github.com/keystonejs/keystone-5/commit/1f0bc236):
- Update the package.json author field to "The Keystone Development Team"
## 1.0.0
- [major] 8b6734ae:
- This is the first release of keystone-alpha (previously voussoir).
All packages in the \`@voussoir\` namespace are now available in the \`@keystone-alpha\` namespace, starting at version \`1.0.0\`.
To upgrade your project you must update any \`@voussoir/<foo>\` dependencies in \`package.json\` to point to \`@keystone-alpha/<foo>: "^1.0.0"\` and update any \`require\`/\`import\` statements in your code.
# @voussoir/email
## 0.0.2
- [patch] 113e16d4:
- Remove unused dependencies
- [patch] 625c1a6d:
- Update mjml-dependency
`;
test("it works", () => {
let entry = getChangelogEntry(changelog, "3.0.0");
expect(entry.content).toMatchSnapshot();
expect(entry.highestLevel).toBe(BumpLevels.major);
});
test("it works", () => {
let entry = getChangelogEntry(changelog, "3.0.1");
expect(entry.content).toMatchSnapshot();
expect(entry.highestLevel).toBe(BumpLevels.patch);
});
test("it sorts the things right", () => {
let things = [
{
name: "a",
highestLevel: BumpLevels.major,
private: true,
},
{
name: "b",
highestLevel: BumpLevels.patch,
private: false,
},
{
name: "c",
highestLevel: BumpLevels.major,
private: false,
},
];
expect(things.sort(sortTheThings)).toMatchSnapshot();
});
@@ -1,100 +0,0 @@
import unified from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
// @ts-ignore
import mdastToString from "mdast-util-to-string";
import { getPackages, Package } from "@manypkg/get-packages";
export const BumpLevels = {
dep: 0,
patch: 1,
minor: 2,
major: 3,
} as const;
export async function getVersionsByDirectory(cwd: string) {
let { packages } = await getPackages(cwd);
return new Map(packages.map((x) => [x.dir, x.packageJson.version]));
}
export async function getChangedPackages(
cwd: string,
previousVersions: Map<string, string>,
) {
let { packages } = await getPackages(cwd);
let changedPackages = new Set<Package>();
for (let pkg of packages) {
const previousVersion = previousVersions.get(pkg.dir);
if (previousVersion !== pkg.packageJson.version) {
changedPackages.add(pkg);
}
}
return [...changedPackages];
}
export function getChangelogEntry(changelog: string, version: string) {
let ast = unified().use(remarkParse).parse(changelog);
let highestLevel: number = BumpLevels.dep;
let nodes = ast.children as Array<any>;
let headingStartInfo:
| {
index: number;
depth: number;
}
| undefined;
let endIndex: number | undefined;
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node.type === "heading") {
let stringified: string = mdastToString(node);
let match = stringified.toLowerCase().match(/(major|minor|patch)/);
if (match !== null) {
let level = BumpLevels[match[0] as "major" | "minor" | "patch"];
highestLevel = Math.max(level, highestLevel);
}
if (headingStartInfo === undefined && stringified === version) {
headingStartInfo = {
index: i,
depth: node.depth,
};
continue;
}
if (
endIndex === undefined &&
headingStartInfo !== undefined &&
headingStartInfo.depth === node.depth
) {
endIndex = i;
break;
}
}
}
if (headingStartInfo) {
ast.children = (ast.children as any).slice(
headingStartInfo.index + 1,
endIndex,
);
}
return {
content: unified().use(remarkStringify).stringify(ast),
highestLevel: highestLevel,
};
}
export function sortTheThings(
a: { private: boolean; highestLevel: number },
b: { private: boolean; highestLevel: number },
) {
if (a.private === b.private) {
return b.highestLevel - a.highestLevel;
}
if (a.private) {
return 1;
}
return -1;
}
@@ -1,63 +0,0 @@
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true /* Do not emit outputs. */,
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
@@ -1,17 +0,0 @@
type Opts = {
glob?: string | Array<string>;
root?: string;
cleanup?: boolean;
};
declare module "fixturez" {
export default function (
cwd: string,
opts?: Opts,
): {
find: (a: string) => string;
temp: () => string;
copy: (a: string) => string;
cleanup: () => any;
};
}
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
name: publish / prerelease
on:
workflow_dispatch:
inputs:
suffix:
description: "Version suffix (e.g. 'fix-user-issue'). Leave blank for timestamp."
required: false
type: string
dry_run:
description: "Dry run (don't actually publish)"
required: false
default: false
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
env:
NX_VERBOSE_LOGGING: true
jobs:
prerelease:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.13.1"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: https://registry.npmjs.org
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Configure npm auth
run: |
npm config set "//registry.npmjs.org/:_authToken" "${NPM_TOKEN}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Publish prerelease
run: |
ARGS=""
if [ -n "${{ inputs.suffix }}" ]; then
ARGS="--suffix ${{ inputs.suffix }}"
fi
if [ "${{ inputs.dry_run }}" == "true" ]; then
ARGS="$ARGS --dry-run"
fi
pnpm tsx scripts/release/prerelease.ts $ARGS
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+117
View File
@@ -0,0 +1,117 @@
name: publish release
on:
pull_request:
types: [closed]
branches: [main]
concurrency:
group: publish-release
cancel-in-progress: false
permissions:
contents: write
env:
NX_VERBOSE_LOGGING: true
jobs:
publish:
# Only run when a release PR is merged (not just closed)
if: >
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.head.ref, 'release/publish/v')
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.13.1"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: https://registry.npmjs.org
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Configure npm auth
run: |
npm config set "//registry.npmjs.org/:_authToken" "${NPM_TOKEN}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Publish to npm
id: publish
run: pnpm tsx scripts/release/publish-release.ts
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
- name: Configure git user
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Create and push git tag
run: |
TAG="v${{ steps.publish.outputs.version }}"
git tag -a "$TAG" -m "Release ${{ steps.publish.outputs.version }}"
git push origin "$TAG"
- name: Create GitHub Release
uses: actions/github-script@v7
env:
RELEASE_TAG: v${{ steps.publish.outputs.version }}
RELEASE_NAME: v${{ steps.publish.outputs.version }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require("fs");
const { owner, repo } = context.repo;
const tag = process.env.RELEASE_TAG;
const name = process.env.RELEASE_NAME;
let body = "";
try {
body = fs.readFileSync("./release-notes.md", "utf8");
} catch {
body = `Release ${name}`;
}
try {
const existing = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
await github.rest.repos.updateRelease({
owner, repo,
release_id: existing.data.id,
tag_name: tag, name, body,
draft: false, prerelease: false,
});
} catch (error) {
if (error.status !== 404) throw error;
await github.rest.repos.createRelease({
owner, repo,
tag_name: tag, name, body,
draft: false, prerelease: false,
});
}
- name: Release summary
run: |
echo "## Release Published" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.publish.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Tag:** v${{ steps.publish.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**npm:** https://www.npmjs.com/package/@copilotkit/react-core/v/${{ steps.publish.outputs.version }}" >> $GITHUB_STEP_SUMMARY
-50
View File
@@ -1,50 +0,0 @@
name: publish / custom
on:
workflow_dispatch:
concurrency: ${{ github.workflow }}-${{ github.ref }}
env:
NX_VERBOSE_LOGGING: true
jobs:
custom-pre:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.13.1"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: "pnpm"
cache-dependency-path: "**/pnpm-lock.yaml"
registry-url: https://registry.npmjs.org
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Configure Nx Cloud environment
run: |
echo "NX_CI_EXECUTION_ID=${{ github.run_id }}-${{ github.run_attempt }}-publish-custom" >> $GITHUB_ENV
echo "NX_CLOUD_NO_TIMEOUTS=true" >> $GITHUB_ENV
echo "NX_CLOUD_DISTRIBUTED_EXECUTION=false" >> $GITHUB_ENV
echo "NX_NO_CLOUD=true" >> $GITHUB_ENV
- name: Build
run: pnpm run build
- name: Publish snapshot
run: ./scripts/release/publish-snapshot-release.sh
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
-176
View File
@@ -1,176 +0,0 @@
name: publish / release
on:
workflow_dispatch:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: write
pull-requests: write
env:
NX_VERBOSE_LOGGING: true
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.13.1"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: https://registry.npmjs.org
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Configure npm auth
run: |
npm config set "//registry.npmjs.org/:_authToken" "${NPM_TOKEN}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Validate release package allowlist
run: pnpm ci:check-release-plan
- name: Create Release PR or Publish
id: changesets
uses: changesets/action@v1
with:
version: pnpm changeset version
publish: pnpm ci:publish-release
commit: "chore: version packages"
title: "chore: version packages"
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Get published version
if: steps.changesets.outputs.published == 'true'
id: release-version
run: |
VERSION=$(node -p "require('./packages/react-core/package.json').version")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if [[ "$VERSION" == *-* ]]; then
echo "is_stable=false" >> "$GITHUB_OUTPUT"
else
echo "is_stable=true" >> "$GITHUB_OUTPUT"
fi
- name: Configure git user for tagging
if: steps.changesets.outputs.published == 'true' && steps.release-version.outputs.is_stable == 'true'
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Generate release changelog
if: steps.changesets.outputs.published == 'true' && steps.release-version.outputs.is_stable == 'true'
run: |
node -p "require('./scripts/release/generate-changelog')('${{ steps.release-version.outputs.version }}')" > changelog.txt
- name: Ensure stable release tag
if: steps.changesets.outputs.published == 'true' && steps.release-version.outputs.is_stable == 'true'
id: release-tag
run: |
TAG="v${{ steps.release-version.outputs.version }}"
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
echo "created=false" >> "$GITHUB_OUTPUT"
else
git tag -a "$TAG" -m "Release ${{ steps.release-version.outputs.version }}"
git push origin "$TAG"
echo "created=true" >> "$GITHUB_OUTPUT"
fi
- name: Create or update GitHub release
if: steps.changesets.outputs.published == 'true' && steps.release-version.outputs.is_stable == 'true'
uses: actions/github-script@v7
env:
RELEASE_TAG: v${{ steps.release-version.outputs.version }}
RELEASE_NAME: v${{ steps.release-version.outputs.version }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require("fs");
const { owner, repo } = context.repo;
const tag = process.env.RELEASE_TAG;
const name = process.env.RELEASE_NAME;
const body = fs.readFileSync("./changelog.txt", "utf8");
try {
const existing = await github.rest.repos.getReleaseByTag({
owner,
repo,
tag,
});
await github.rest.repos.updateRelease({
owner,
repo,
release_id: existing.data.id,
tag_name: tag,
name,
body,
draft: false,
prerelease: false,
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
await github.rest.repos.createRelease({
owner,
repo,
tag_name: tag,
name,
body,
draft: false,
prerelease: false,
});
}
- name: Re-enter next prerelease mode
if: steps.changesets.outputs.published == 'true' && steps.release-version.outputs.is_stable == 'true'
run: |
if [[ -f .changeset/pre.json ]]; then
echo "Changesets prerelease mode is already enabled."
exit 0
fi
pnpm changeset pre enter next
- name: Create or update prerelease mode PR
if: steps.changesets.outputs.published == 'true' && steps.release-version.outputs.is_stable == 'true'
id: prerelease-pr
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: changeset-release/re-enter-next
delete-branch: true
add-paths: .changeset/pre.json
commit-message: "chore: re-enter next prerelease mode"
title: "chore: re-enter next prerelease mode"
body: |
This PR was created automatically after publishing a stable release.
It re-enters Changesets prerelease mode for `next` so future release PRs continue on the `next` channel.
+160
View File
@@ -0,0 +1,160 @@
name: create release PR
on:
workflow_dispatch:
inputs:
bump:
description: "Version bump level"
required: true
type: choice
options:
- patch
- minor
- major
dry_run:
description: "Dry run (preview without creating PR)"
required: false
default: false
type: boolean
concurrency:
group: release-pr
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
env:
NX_VERBOSE_LOGGING: true
jobs:
create-release-pr:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check for existing release PR
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
const { data: prs } = await github.rest.pulls.list({
owner,
repo,
state: "open",
head_prefix: `${owner}:release/publish/v`,
});
const releasePRs = prs.filter(pr => pr.head.ref.startsWith("release/publish/v"));
if (releasePRs.length > 0) {
const existing = releasePRs.map(pr => ` - #${pr.number}: ${pr.title} (${pr.html_url})`).join("\n");
core.setFailed(
`An open release PR already exists. Close or merge it before creating a new one:\n${existing}`
);
}
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.13.1"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Prepare release
id: prepare
run: |
if [ "${{ inputs.dry_run }}" == "true" ]; then
pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }} --dry-run
else
pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }}
fi
- name: Generate AI release notes
if: inputs.dry_run != true
id: ai_notes
run: pnpm tsx scripts/release/generate-ai-release-notes.ts "${{ steps.prepare.outputs.version }}"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
NOTION_RELEASE_NOTES_PAGE: ${{ secrets.NOTION_RELEASE_NOTES_PAGE }}
- name: Create release PR
if: inputs.dry_run != true
id: create_pr
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: release/publish/v${{ steps.prepare.outputs.version }}
delete-branch: true
commit-message: "chore: release v${{ steps.prepare.outputs.version }}"
title: "chore: release v${{ steps.prepare.outputs.version }}"
body: |
## Release v${{ steps.prepare.outputs.version }}
**Bump:** `${{ inputs.bump }}`
---
### How this release process works
1. **This PR was created automatically** by the "Create Release PR" workflow.
It bumped all `@copilotkit/*` package versions to `${{ steps.prepare.outputs.version }}`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there before merging.
4. **When this PR is merged**, the `publish-release` workflow automatically:
- Builds all packages
- Publishes all `@copilotkit/*` packages to npm at version `${{ steps.prepare.outputs.version }}`
- Creates git tag `v${{ steps.prepare.outputs.version }}`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs automatically on this PR.
labels: release
- name: Comment Notion link on PR
if: inputs.dry_run != true && steps.ai_notes.outputs.notion_url
uses: actions/github-script@v7
env:
NOTION_URL: ${{ steps.ai_notes.outputs.notion_url }}
PR_NUMBER: ${{ steps.create_pr.outputs.pull-request-number }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
const prNumber = parseInt(process.env.PR_NUMBER, 10);
const notionUrl = process.env.NOTION_URL;
if (prNumber && notionUrl) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: `📝 **Release notes draft:** ${notionUrl}\n\nYou can edit the release notes in Notion before merging. The final content will be used for the GitHub Release.`,
});
}
-6
View File
@@ -7,18 +7,12 @@ on:
- "docs/**"
- "README.md"
- "examples/**"
- ".github/workflows/demos_preview.yml"
- ".github/workflows/release.yml"
- "packages/**/package.json"
- "packages/**/CHANGELOG.md"
- ".changeset/**"
pull_request:
branches: [main]
paths-ignore:
- "docs/**"
- "README.md"
- "examples/**"
- ".changeset/**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
-5
View File
@@ -7,11 +7,6 @@ on:
- "docs/**"
- "README.md"
- "examples/**"
- ".github/workflows/demos_preview.yml"
- ".github/workflows/release.yml"
- "packages/**/package.json"
- "packages/**/CHANGELOG.md"
- ".changeset/**"
pull_request:
branches: [main]
paths-ignore:
+4
View File
@@ -53,6 +53,10 @@ lefthook-local.yml
# Private agent instructions (not shared with the team)
private-agents.md
# Release artifacts
release-notes.md
release-notes-notion.json
# Binary artifacts
*.dSYM/
*.exe
+2 -7
View File
@@ -21,9 +21,8 @@
"docs": "pnpm -C examples/v2/docs dev",
"demo:angular": "nx run-many -t dev --projects=examples/v2/angular/demo,examples/v2/angular/demo-server",
"demo:react": "pnpm -C examples/v2/react/demo dev",
"changeset": "changeset",
"ci:check-release-plan": "node ./scripts/release/check-allowed-release-packages.js",
"ci:publish-release": "pnpm run build && pnpm changeset publish --no-git-tag",
"release:prepare:dry": "tsx scripts/release/prepare-release.ts --dry-run",
"release:prerelease:dry": "tsx scripts/release/prerelease.ts --dry-run",
"prepare": "lefthook install",
"graph": "nx graph",
"publint": "nx run-many -t publint --projects=packages/**",
@@ -33,7 +32,6 @@
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.18.2",
"@changesets/cli": "^2.29.8",
"@commitlint/cli": "^20.3.0",
"@commitlint/config-conventional": "^20.3.0",
"@storybook/addon-docs": "^10.1.10",
@@ -92,9 +90,6 @@
"prismjs@<=1.30.0": "1.30.0",
"pino@<=10.1.1": "10.1.1",
"@copilotkit/license-verifier": "0.0.1-a1"
},
"patchedDependencies": {
"@changesets/assemble-release-plan@6.0.9": "patches/@changesets__assemble-release-plan@6.0.9.patch"
}
}
}
@@ -1,37 +0,0 @@
diff --git a/dist/changesets-assemble-release-plan.cjs.js b/dist/changesets-assemble-release-plan.cjs.js
@@ -317,6 +317,9 @@
preInfo,
onlyUpdatePeerDependentsWhenOutOfRange
}) {
+ if(depType === "peerDependencies" ) {
+ return false;
+ }
// we check if it is a peerDependency because if it is, our dependent bump type might need to be major.
return depType === "peerDependencies" && nextRelease.type !== "none" && nextRelease.type !== "patch" && (
// 1. If onlyUpdatePeerDependentsWhenOutOfRange set to true, bump major if the version is leaving the range.
@@ -523,17 +526,21 @@
// Detailed explanation at https://github.com/changesets/changesets/pull/382#discussion_r434434182
if (preInfo.preVersions.get(pkg.packageJson.name) !== 0) {
const existingRelease = releases.get(pkg.packageJson.name);
+ const shouldSkip = shouldSkipPackage.shouldSkipPackage(pkg, {
+ ignore: refinedConfig.ignore,
+ allowPrivatePackages: refinedConfig.privatePackages.version
+ });
if (!existingRelease) {
+ if (shouldSkip) {
+ continue;
+ }
releases.set(pkg.packageJson.name, {
name: pkg.packageJson.name,
type: "patch",
oldVersion: pkg.packageJson.version,
changesets: []
});
- } else if (existingRelease.type === "none" && !shouldSkipPackage.shouldSkipPackage(pkg, {
- ignore: refinedConfig.ignore,
- allowPrivatePackages: refinedConfig.privatePackages.version
- })) {
+ } else if (existingRelease.type === "none" && !shouldSkip) {
existingRelease.type = "patch";
}
}
+46 -379
View File
@@ -20,11 +20,6 @@ overrides:
pnpmfileChecksum: sha256-Yeprb3L5TyOmoppd3E/Q880zwJi1e3M2rgrViqRvicc=
patchedDependencies:
'@changesets/assemble-release-plan@6.0.9':
hash: 1288f70af3f58bbc5f2f72e85c45225162d663de7867bfc05d0e489a4579863d
path: patches/@changesets__assemble-release-plan@6.0.9.patch
importers:
.:
@@ -32,9 +27,6 @@ importers:
'@arethetypeswrong/cli':
specifier: ^0.18.2
version: 0.18.2
'@changesets/cli':
specifier: ^2.29.8
version: 2.29.8(@types/node@18.19.130)
'@commitlint/cli':
specifier: ^20.3.0
version: 20.3.1(@types/node@18.19.130)(typescript@5.9.2)
@@ -412,7 +404,7 @@ importers:
version: 0.1.13
eslint-config-next:
specifier: ^15.0.2
version: 15.4.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
version: 15.4.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
flowtoken:
specifier: ^1.0.40
version: 1.0.40(@types/react@19.2.7)(react@19.2.3)
@@ -4455,61 +4447,6 @@ packages:
'@cfworker/json-schema@4.1.1':
resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
'@changesets/apply-release-plan@7.0.14':
resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==}
'@changesets/assemble-release-plan@6.0.9':
resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==}
'@changesets/changelog-git@0.2.1':
resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==}
'@changesets/cli@2.29.8':
resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==}
hasBin: true
'@changesets/config@3.1.2':
resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==}
'@changesets/errors@0.2.0':
resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==}
'@changesets/get-dependents-graph@2.1.3':
resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==}
'@changesets/get-release-plan@4.0.14':
resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==}
'@changesets/get-version-range-type@0.4.0':
resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==}
'@changesets/git@3.0.4':
resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==}
'@changesets/logger@0.1.1':
resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==}
'@changesets/parse@0.4.2':
resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==}
'@changesets/pre@2.0.2':
resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==}
'@changesets/read@0.6.6':
resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==}
'@changesets/should-skip-package@0.1.2':
resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==}
'@changesets/types@4.1.0':
resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==}
'@changesets/types@6.1.0':
resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==}
'@changesets/write@0.4.0':
resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
'@chevrotain/cst-dts-gen@11.0.3':
resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==}
@@ -7451,12 +7388,6 @@ packages:
resolution: {integrity: sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==}
engines: {node: '>=8'}
'@manypkg/find-root@1.1.0':
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
'@manypkg/get-packages@1.1.3':
resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
'@mdx-js/mdx@3.1.1':
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
@@ -11216,9 +11147,6 @@ packages:
'@types/node-forge@1.3.14':
resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==}
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
'@types/node@18.19.130':
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
@@ -12316,10 +12244,6 @@ packages:
resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==}
engines: {node: '>=12.0.0'}
better-path-resolve@1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'}
better-sqlite3@12.5.0:
resolution: {integrity: sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
@@ -14323,9 +14247,6 @@ packages:
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
extendable-error@0.1.7:
resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
extract-zip@2.0.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
@@ -14679,10 +14600,6 @@ packages:
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
engines: {node: '>=14.14'}
fs-extra@7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
engines: {node: '>=6 <7 || >=8'}
fs-extra@8.1.0:
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
engines: {node: '>=6 <7 || >=8'}
@@ -15300,10 +15217,6 @@ packages:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
human-id@4.1.3:
resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==}
hasBin: true
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -15779,10 +15692,6 @@ packages:
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
is-subdir@1.2.0:
resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
engines: {node: '>=4'}
is-symbol@1.1.1:
resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
engines: {node: '>= 0.4'}
@@ -18214,9 +18123,6 @@ packages:
os-browserify@0.3.0:
resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==}
outdent@0.5.0:
resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
override-require@1.1.1:
resolution: {integrity: sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==}
@@ -18247,10 +18153,6 @@ packages:
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
engines: {node: '>=12.20'}
p-filter@2.1.0:
resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
engines: {node: '>=8'}
p-finally@1.0.0:
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
engines: {node: '>=4'}
@@ -18283,10 +18185,6 @@ packages:
resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
p-map@2.1.0:
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
engines: {node: '>=6'}
p-map@4.0.0:
resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
engines: {node: '>=10'}
@@ -18350,9 +18248,6 @@ packages:
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
package-manager-detector@0.2.11:
resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==}
package-manager-detector@1.6.0:
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
@@ -18825,11 +18720,6 @@ packages:
engines: {node: '>=10.13.0'}
hasBin: true
prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
prettier@3.7.4:
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
engines: {node: '>=14'}
@@ -18977,9 +18867,6 @@ packages:
resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
engines: {node: '>=0.6'}
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
quansync@1.0.0:
resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==}
@@ -19195,10 +19082,6 @@ packages:
read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
read-yaml-file@1.1.0:
resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
engines: {node: '>=6'}
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
@@ -20099,9 +19982,6 @@ packages:
spawn-command@0.0.2:
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
spawndamnit@3.0.1:
resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==}
spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
@@ -20535,10 +20415,6 @@ packages:
telejson@7.2.0:
resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==}
term-size@2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
terser-webpack-plugin@5.3.16:
resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==}
engines: {node: '>= 10.13.0'}
@@ -25929,150 +25805,6 @@ snapshots:
'@cfworker/json-schema@4.1.1': {}
'@changesets/apply-release-plan@7.0.14':
dependencies:
'@changesets/config': 3.1.2
'@changesets/get-version-range-type': 0.4.0
'@changesets/git': 3.0.4
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
detect-indent: 6.1.0
fs-extra: 7.0.1
lodash.startcase: 4.4.0
outdent: 0.5.0
prettier: 2.8.8
resolve-from: 5.0.0
semver: 7.7.3
'@changesets/assemble-release-plan@6.0.9(patch_hash=1288f70af3f58bbc5f2f72e85c45225162d663de7867bfc05d0e489a4579863d)':
dependencies:
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.1.3
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
semver: 7.7.3
'@changesets/changelog-git@0.2.1':
dependencies:
'@changesets/types': 6.1.0
'@changesets/cli@2.29.8(@types/node@18.19.130)':
dependencies:
'@changesets/apply-release-plan': 7.0.14
'@changesets/assemble-release-plan': 6.0.9(patch_hash=1288f70af3f58bbc5f2f72e85c45225162d663de7867bfc05d0e489a4579863d)
'@changesets/changelog-git': 0.2.1
'@changesets/config': 3.1.2
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.1.3
'@changesets/get-release-plan': 4.0.14
'@changesets/git': 3.0.4
'@changesets/logger': 0.1.1
'@changesets/pre': 2.0.2
'@changesets/read': 0.6.6
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@changesets/write': 0.4.0
'@inquirer/external-editor': 1.0.3(@types/node@18.19.130)
'@manypkg/get-packages': 1.1.3
ansi-colors: 4.1.3
ci-info: 3.9.0
enquirer: 2.4.1
fs-extra: 7.0.1
mri: 1.2.0
p-limit: 2.3.0
package-manager-detector: 0.2.11
picocolors: 1.1.1
resolve-from: 5.0.0
semver: 7.7.3
spawndamnit: 3.0.1
term-size: 2.2.1
transitivePeerDependencies:
- '@types/node'
'@changesets/config@3.1.2':
dependencies:
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.1.3
'@changesets/logger': 0.1.1
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
fs-extra: 7.0.1
micromatch: 4.0.8
'@changesets/errors@0.2.0':
dependencies:
extendable-error: 0.1.7
'@changesets/get-dependents-graph@2.1.3':
dependencies:
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
picocolors: 1.1.1
semver: 7.7.3
'@changesets/get-release-plan@4.0.14':
dependencies:
'@changesets/assemble-release-plan': 6.0.9(patch_hash=1288f70af3f58bbc5f2f72e85c45225162d663de7867bfc05d0e489a4579863d)
'@changesets/config': 3.1.2
'@changesets/pre': 2.0.2
'@changesets/read': 0.6.6
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
'@changesets/get-version-range-type@0.4.0': {}
'@changesets/git@3.0.4':
dependencies:
'@changesets/errors': 0.2.0
'@manypkg/get-packages': 1.1.3
is-subdir: 1.2.0
micromatch: 4.0.8
spawndamnit: 3.0.1
'@changesets/logger@0.1.1':
dependencies:
picocolors: 1.1.1
'@changesets/parse@0.4.2':
dependencies:
'@changesets/types': 6.1.0
js-yaml: 4.1.1
'@changesets/pre@2.0.2':
dependencies:
'@changesets/errors': 0.2.0
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
fs-extra: 7.0.1
'@changesets/read@0.6.6':
dependencies:
'@changesets/git': 3.0.4
'@changesets/logger': 0.1.1
'@changesets/parse': 0.4.2
'@changesets/types': 6.1.0
fs-extra: 7.0.1
p-filter: 2.1.0
picocolors: 1.1.1
'@changesets/should-skip-package@0.1.2':
dependencies:
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
'@changesets/types@4.1.0': {}
'@changesets/types@6.1.0': {}
'@changesets/write@0.4.0':
dependencies:
'@changesets/types': 6.1.0
fs-extra: 7.0.1
human-id: 4.1.3
prettier: 2.8.8
'@chevrotain/cst-dts-gen@11.0.3':
dependencies:
'@chevrotain/gast': 11.0.3
@@ -26859,9 +26591,9 @@ snapshots:
eslint: 8.57.1
eslint-visitor-keys: 3.4.3
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))':
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@1.21.7))':
dependencies:
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -29303,22 +29035,6 @@ snapshots:
dependencies:
'@lukeed/csprng': 1.1.0
'@manypkg/find-root@1.1.0':
dependencies:
'@babel/runtime': 7.28.4
'@types/node': 12.20.55
find-up: 4.1.0
fs-extra: 8.1.0
'@manypkg/get-packages@1.1.3':
dependencies:
'@babel/runtime': 7.28.4
'@changesets/types': 4.1.0
'@manypkg/find-root': 1.1.0
fs-extra: 8.1.0
globby: 11.1.0
read-yaml-file: 1.1.0
'@mdx-js/mdx@3.1.1':
dependencies:
'@types/estree': 1.0.8
@@ -33768,8 +33484,6 @@ snapshots:
dependencies:
'@types/node': 22.19.11
'@types/node@12.20.55': {}
'@types/node@18.19.130':
dependencies:
undici-types: 5.26.5
@@ -33903,16 +33617,16 @@ snapshots:
'@types/node': 22.19.11
optional: true
'@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)':
'@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
'@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/type-utils': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/utils': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/type-utils': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
'@typescript-eslint/utils': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 7.2.0
debug: 4.4.3(supports-color@8.1.1)
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
@@ -33923,14 +33637,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)':
'@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)':
dependencies:
'@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 7.2.0
debug: 4.4.3(supports-color@8.1.1)
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -33941,12 +33655,12 @@ snapshots:
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/visitor-keys': 7.2.0
'@typescript-eslint/type-utils@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)':
'@typescript-eslint/type-utils@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)':
dependencies:
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.9.2)
'@typescript-eslint/utils': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/utils': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
debug: 4.4.3(supports-color@8.1.1)
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
ts-api-utils: 1.4.3(typescript@5.9.2)
optionalDependencies:
typescript: 5.9.2
@@ -33970,15 +33684,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)':
'@typescript-eslint/utils@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
'@types/json-schema': 7.0.15
'@types/semver': 7.7.1
'@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.9.2)
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
semver: 7.7.4
transitivePeerDependencies:
- supports-color
@@ -35154,10 +34868,6 @@ snapshots:
dependencies:
open: 8.4.2
better-path-resolve@1.0.0:
dependencies:
is-windows: 1.0.2
better-sqlite3@12.5.0:
dependencies:
bindings: 1.5.0
@@ -36965,6 +36675,7 @@ snapshots:
dependencies:
ansi-colors: 4.1.3
strip-ansi: 6.0.1
optional: true
entities@2.2.0: {}
@@ -37333,19 +37044,19 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
eslint-config-next@15.4.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2):
eslint-config-next@15.4.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2):
dependencies:
'@next/eslint-plugin-next': 15.4.4
'@rushstack/eslint-patch': 1.15.0
'@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
eslint: 9.39.2(jiti@2.6.1)
'@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
eslint: 9.39.2(jiti@1.21.7)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@2.6.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@1.21.7))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@1.21.7))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@1.21.7))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@1.21.7))
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -37361,33 +37072,33 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@1.21.7)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3(supports-color@8.1.1)
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
get-tsconfig: 4.13.6
is-bun-module: 2.0.0
stable-hash: 0.0.5
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
eslint: 9.39.2(jiti@2.6.1)
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
eslint: 9.39.2(jiti@1.21.7)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@1.21.7))
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -37396,9 +37107,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -37410,13 +37121,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/parser': 7.2.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)):
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@1.21.7)):
dependencies:
aria-query: 5.3.2
array-includes: 3.1.9
@@ -37426,7 +37137,7 @@ snapshots:
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
@@ -37435,11 +37146,11 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
eslint-plugin-react-hooks@5.2.0(eslint@9.39.2(jiti@2.6.1)):
eslint-plugin-react-hooks@5.2.0(eslint@9.39.2(jiti@1.21.7)):
dependencies:
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)):
eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@1.21.7)):
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
@@ -37447,7 +37158,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.2.2
eslint: 9.39.2(jiti@2.6.1)
eslint: 9.39.2(jiti@1.21.7)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -37523,9 +37234,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint@9.39.2(jiti@2.6.1):
eslint@9.39.2(jiti@1.21.7):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.2
'@eslint/config-helpers': 0.4.2
@@ -37560,7 +37271,7 @@ snapshots:
natural-compare: 1.4.0
optionator: 0.9.4
optionalDependencies:
jiti: 2.6.1
jiti: 1.21.7
transitivePeerDependencies:
- supports-color
@@ -37850,8 +37561,6 @@ snapshots:
extend@3.0.2: {}
extendable-error@0.1.7: {}
extract-zip@2.0.1:
dependencies:
debug: 4.4.3(supports-color@8.1.1)
@@ -38291,12 +38000,6 @@ snapshots:
jsonfile: 6.2.0
universalify: 2.0.1
fs-extra@7.0.1:
dependencies:
graceful-fs: 4.2.11
jsonfile: 4.0.0
universalify: 0.1.2
fs-extra@8.1.0:
dependencies:
graceful-fs: 4.2.11
@@ -39216,8 +38919,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
human-id@4.1.3: {}
human-signals@2.1.0: {}
human-signals@5.0.0: {}
@@ -39663,10 +39364,6 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-subdir@1.2.0:
dependencies:
better-path-resolve: 1.0.0
is-symbol@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -43157,8 +42854,6 @@ snapshots:
os-browserify@0.3.0: {}
outdent@0.5.0: {}
override-require@1.1.1: {}
own-keys@1.0.1:
@@ -43220,10 +42915,6 @@ snapshots:
p-cancelable@3.0.0: {}
p-filter@2.1.0:
dependencies:
p-map: 2.1.0
p-finally@1.0.0: {}
p-limit@2.3.0:
@@ -43254,8 +42945,6 @@ snapshots:
dependencies:
p-limit: 4.0.0
p-map@2.1.0: {}
p-map@4.0.0:
dependencies:
aggregate-error: 3.1.0
@@ -43328,10 +43017,6 @@ snapshots:
package-json-from-dist@1.0.1: {}
package-manager-detector@0.2.11:
dependencies:
quansync: 0.2.11
package-manager-detector@1.6.0: {}
pacote@20.0.0:
@@ -43845,8 +43530,6 @@ snapshots:
prettier@2.7.1: {}
prettier@2.8.8: {}
prettier@3.7.4: {}
pretty-error@4.0.0:
@@ -44044,8 +43727,6 @@ snapshots:
dependencies:
side-channel: 1.1.0
quansync@0.2.11: {}
quansync@1.0.0: {}
querystring-es3@0.2.1: {}
@@ -44337,13 +44018,6 @@ snapshots:
dependencies:
pify: 2.3.0
read-yaml-file@1.1.0:
dependencies:
graceful-fs: 4.2.11
js-yaml: 3.14.2
pify: 4.0.1
strip-bom: 3.0.0
readable-stream@2.3.8:
dependencies:
core-util-is: 1.0.3
@@ -45655,11 +45329,6 @@ snapshots:
spawn-command@0.0.2: {}
spawndamnit@3.0.1:
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
spdx-correct@3.2.0:
dependencies:
spdx-expression-parse: 3.0.1
@@ -46259,8 +45928,6 @@ snapshots:
dependencies:
memoizerific: 1.11.3
term-size@2.2.1: {}
terser-webpack-plugin@5.3.16(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.23.1)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.23.1)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
+18
View File
@@ -0,0 +1,18 @@
{
"prereleaseTag": "canary",
"versionedTogether": [
"@copilotkit/a2ui-renderer",
"@copilotkit/core",
"@copilotkit/react-core",
"@copilotkit/react-textarea",
"@copilotkit/react-ui",
"@copilotkit/runtime",
"@copilotkit/runtime-client-gql",
"@copilotkit/sdk-js",
"@copilotkit/shared",
"@copilotkit/sqlite-runner",
"@copilotkit/voice",
"@copilotkit/web-inspector"
],
"versionedIndependently": ["@copilotkitnext/angular", "copilotkit"]
}
@@ -1,58 +0,0 @@
const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const allowedReleasePackages = new Set([
"@copilotkit/a2ui-renderer",
"@copilotkit/core",
"@copilotkit/react-core",
"@copilotkit/react-textarea",
"@copilotkit/react-ui",
"@copilotkit/runtime",
"@copilotkit/runtime-client-gql",
"@copilotkit/sdk-js",
"@copilotkit/shared",
"@copilotkit/sqlite-runner",
"@copilotkit/voice",
"@copilotkit/web-inspector",
"@copilotkitnext/angular",
"copilotkit",
]);
const statusFile = ".changeset-status.release-check.json";
const statusPath = path.join(process.cwd(), statusFile);
try {
const result = spawnSync(
"pnpm",
["changeset", "status", `--output=${statusFile}`],
{
cwd: process.cwd(),
stdio: "inherit",
},
);
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
const status = JSON.parse(fs.readFileSync(statusPath, "utf8"));
const unexpectedReleases = status.releases.filter(
(release) =>
release.type !== "none" && !allowedReleasePackages.has(release.name),
);
if (unexpectedReleases.length > 0) {
console.error("Unexpected release targets found:");
for (const release of unexpectedReleases) {
console.error(
`- ${release.name} (${release.type} -> ${release.newVersion})`,
);
}
process.exit(1);
}
console.log("Release plan only contains allowlisted packages.");
} finally {
fs.rmSync(statusPath, { force: true });
}
@@ -0,0 +1,169 @@
/**
* AI-powered release notes generator + Notion draft creator.
*
* 1. Reads the raw changelog from release-notes.md
* 2. Calls Claude API to generate polished release notes
* 3. Creates a Notion page with the draft (for human editing)
* 4. Writes release-notes.md with the AI version
* 5. Outputs the Notion page URL + ID for the workflow
*
* Env vars:
* ANTHROPIC_API_KEY for AI generation (falls back to raw if missing)
* NOTION_API_KEY for creating the Notion draft (skipped if missing)
* NOTION_RELEASE_NOTES_PAGE parent page ID in Notion
*
* Usage: tsx scripts/release/generate-ai-release-notes.ts <version>
*/
import fs from "fs";
import path from "path";
import https from "https";
import { spawnSync } from "child_process";
import { ROOT } from "./lib/config.js";
import { createReleaseDraft } from "./lib/notion.js";
function getRecentCommits(count = 50): string {
const result = spawnSync(
"git",
["log", "--oneline", `-${count}`, "--no-merges"],
{ cwd: ROOT, encoding: "utf8" },
);
return result.stdout.trim();
}
function callAnthropic(apiKey: string, prompt: string): Promise<string> {
return new Promise((resolve, reject) => {
const body = JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 2048,
messages: [{ role: "user", content: prompt }],
});
const options = {
hostname: "api.anthropic.com",
path: "/v1/messages",
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey,
"anthropic-version": "2023-06-01",
},
};
const req = https.request(options, (res) => {
let data = "";
res.on("data", (chunk: string) => (data += chunk));
res.on("end", () => {
try {
const parsed = JSON.parse(data);
if (parsed.content?.[0]) {
resolve(parsed.content[0].text);
} else {
reject(new Error(`Unexpected API response: ${data}`));
}
} catch (e) {
reject(e);
}
});
});
req.on("error", reject);
req.write(body);
req.end();
});
}
async function main() {
const version = process.argv[2];
if (!version) {
console.error("Usage: generate-ai-release-notes.ts <version>");
process.exit(1);
}
const releaseNotesPath = path.join(ROOT, "release-notes.md");
if (!fs.existsSync(releaseNotesPath)) {
console.error("release-notes.md not found. Run prepare-release.ts first.");
process.exit(1);
}
const rawChangelog = fs.readFileSync(releaseNotesPath, "utf8");
let finalNotes = rawChangelog;
// Step 1: AI-enhance the release notes if API key is available
const anthropicKey = process.env.ANTHROPIC_API_KEY;
if (anthropicKey) {
console.log("Generating AI-enhanced release notes...");
const recentCommits = getRecentCommits();
const prompt = `You are writing release notes for CopilotKit v${version}, an open-source AI agent framework for React applications.
Here is the raw changelog extracted from git history:
${rawChangelog}
Here are the recent git commits for additional context:
${recentCommits}
Write polished, user-facing release notes for a GitHub Release. Guidelines:
- Start with a brief (1-2 sentence) summary of the release
- Group changes into clear sections (Features, Fixes, Breaking Changes as applicable)
- Write in a professional but approachable tone
- Focus on what users care about what changed and why it matters
- Include any migration notes for breaking changes
- Keep it concise no filler, no marketing speak
- Use markdown formatting
- Do NOT include a title/header the GitHub Release title will be "v${version}"
Output ONLY the release notes content, nothing else.`;
try {
finalNotes = await callAnthropic(anthropicKey, prompt);
fs.writeFileSync(releaseNotesPath, finalNotes);
console.log("AI-enhanced release notes written to release-notes.md");
} catch (err: any) {
console.error(`AI generation failed: ${err.message}`);
console.log("Falling back to raw changelog.");
}
} else {
console.log(
"No ANTHROPIC_API_KEY found. Using raw changelog as release notes.",
);
}
// Step 2: Create a Notion draft page for human editing
const notionKey = process.env.NOTION_API_KEY;
const notionParent = process.env.NOTION_RELEASE_NOTES_PAGE;
if (notionKey && notionParent) {
console.log("Creating Notion release notes draft...");
try {
const { pageId, url } = await createReleaseDraft(version, finalNotes);
console.log(`Notion draft created: ${url}`);
// Write the Notion reference so the publish workflow can find it
const notionRef = { pageId, url, version };
const refPath = path.join(ROOT, "release-notes-notion.json");
fs.writeFileSync(refPath, JSON.stringify(notionRef, null, 2) + "\n");
// Output for CI
const outputPath = process.env.GITHUB_OUTPUT;
if (outputPath) {
fs.appendFileSync(outputPath, `notion_url=${url}\n`);
fs.appendFileSync(outputPath, `notion_page_id=${pageId}\n`);
}
} catch (err: any) {
console.error(`Notion draft creation failed: ${err.message}`);
console.log("Continuing without Notion draft.");
}
} else {
console.log(
"No NOTION_API_KEY/NOTION_RELEASE_NOTES_PAGE found. Skipping Notion draft.",
);
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
-62
View File
@@ -1,62 +0,0 @@
const fs = require("fs");
const { join } = require("path");
const packageDirs = [
"react-core",
"react-ui",
"sdk-js",
"react-textarea",
"runtime",
"runtime-client-gql",
"shared",
];
function getPackageChangelog(packageDir, version) {
const packageJsonPath = join(
__dirname,
`../../packages/${packageDir}/package.json`,
);
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
const packageName = packageJson.name;
const changelogPath = join(
__dirname,
`../../packages/${packageDir}/CHANGELOG.md`,
);
const changelog = fs.readFileSync(changelogPath, "utf8");
// Find where the following line starts: "## ${version}"
const changelogStart = changelog.indexOf(`## ${version}`);
if (changelogStart === -1) {
throw new Error(
`Changelog for ${packageName} version ${version} not found`,
);
}
// Find the next "## " after our version section
const changelogEnd = changelog.indexOf("\n## ", changelogStart + 1);
// If no next section found, use the entire rest of the file
const sectionEnd = changelogEnd === -1 ? changelog.length : changelogEnd;
// Return just the section for this version
const changelogSection = changelog.slice(changelogStart, sectionEnd);
const packageChangelog = `# ${packageName}\n\n${changelogSection}`;
return packageChangelog;
}
function getFullReleaseChangelog(version) {
let changelogs = [];
for (const packageDir of packageDirs) {
changelogs.push(getPackageChangelog(packageDir, version));
}
const changelog = changelogs.join("\n\n");
console.log(changelog);
return changelog;
}
module.exports = getFullReleaseChangelog;
+66
View File
@@ -0,0 +1,66 @@
import { spawnSync } from "child_process";
import { ROOT } from "./config.js";
export function getLastReleaseTag(): string | null {
const result = spawnSync(
"git",
["tag", "--list", "v*", "--sort=-v:refname"],
{ cwd: ROOT, encoding: "utf8" },
);
const tags = result.stdout.trim().split("\n").filter(Boolean);
for (const tag of tags) {
if (/^v\d+\.\d+\.\d+$/.test(tag)) {
return tag;
}
}
return null;
}
export interface Commit {
hash: string;
subject: string;
}
export function getCommitsSinceLastRelease(): Commit[] {
const lastTag = getLastReleaseTag();
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
const result = spawnSync(
"git",
["log", range, "--oneline", "--no-merges", "--format=%H %s"],
{ cwd: ROOT, encoding: "utf8" },
);
return result.stdout
.trim()
.split("\n")
.filter(Boolean)
.map((line) => {
const spaceIdx = line.indexOf(" ");
return {
hash: line.slice(0, spaceIdx),
subject: line.slice(spaceIdx + 1),
};
});
}
export interface ChangesSummary {
lastTag: string | null;
commitCount: number;
commits: Commit[];
oneline: string;
}
export function getChangesSummary(): ChangesSummary {
const lastTag = getLastReleaseTag();
const commits = getCommitsSinceLastRelease();
return {
lastTag,
commitCount: commits.length,
commits,
oneline: commits.map((c) => `- ${c.subject}`).join("\n"),
};
}
+15
View File
@@ -0,0 +1,15 @@
import fs from "fs";
import path from "path";
export const ROOT = path.resolve(import.meta.dirname, "../../..");
export interface ReleaseConfig {
prereleaseTag: string;
versionedTogether: string[];
versionedIndependently: string[];
}
export function loadConfig(): ReleaseConfig {
const configPath = path.join(ROOT, "release.config.json");
return JSON.parse(fs.readFileSync(configPath, "utf8"));
}
+196
View File
@@ -0,0 +1,196 @@
import https from "https";
/**
* Notion API helper for release notes management.
*
* Creates a draft release notes page under the configured parent page.
* On merge, reads the (potentially edited) page content back for the
* GitHub Release body.
*
* Required env vars:
* NOTION_API_KEY Notion internal integration token
* NOTION_RELEASE_NOTES_PAGE Parent page ID for release note drafts
*/
const NOTION_API_VERSION = "2022-06-28";
function notionRequest(
apiKey: string,
method: string,
path: string,
body?: unknown,
): Promise<any> {
return new Promise((resolve, reject) => {
const options = {
hostname: "api.notion.com",
path,
method,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Notion-Version": NOTION_API_VERSION,
},
};
const req = https.request(options, (res) => {
let data = "";
res.on("data", (chunk: string) => (data += chunk));
res.on("end", () => {
const parsed = JSON.parse(data);
if (res.statusCode && res.statusCode >= 400) {
reject(
new Error(
`Notion API ${res.statusCode}: ${parsed.message || data}`,
),
);
} else {
resolve(parsed);
}
});
});
req.on("error", reject);
if (body) req.write(JSON.stringify(body));
req.end();
});
}
/** Convert a markdown string into Notion block children (simplified). */
function markdownToBlocks(markdown: string): any[] {
const blocks: any[] = [];
const lines = markdown.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith("### ")) {
blocks.push({
object: "block",
type: "heading_3",
heading_3: {
rich_text: [{ type: "text", text: { content: line.slice(4) } }],
},
});
} else if (line.startsWith("## ")) {
blocks.push({
object: "block",
type: "heading_2",
heading_2: {
rich_text: [{ type: "text", text: { content: line.slice(3) } }],
},
});
} else if (line.startsWith("- ")) {
blocks.push({
object: "block",
type: "bulleted_list_item",
bulleted_list_item: {
rich_text: [{ type: "text", text: { content: line.slice(2) } }],
},
});
} else if (line.trim() === "") {
continue;
} else {
blocks.push({
object: "block",
type: "paragraph",
paragraph: {
rich_text: [{ type: "text", text: { content: line } }],
},
});
}
}
return blocks;
}
/** Convert Notion blocks back to markdown. */
function blocksToMarkdown(blocks: any[]): string {
const lines: string[] = [];
for (const block of blocks) {
const richText =
block[block.type]?.rich_text
?.map((t: any) => t.plain_text || "")
.join("") || "";
switch (block.type) {
case "heading_1":
lines.push(`# ${richText}`);
break;
case "heading_2":
lines.push(`## ${richText}`);
break;
case "heading_3":
lines.push(`### ${richText}`);
break;
case "bulleted_list_item":
lines.push(`- ${richText}`);
break;
case "numbered_list_item":
lines.push(`1. ${richText}`);
break;
case "paragraph":
lines.push(richText || "");
break;
case "divider":
lines.push("---");
break;
default:
if (richText) lines.push(richText);
}
}
return lines.join("\n");
}
/**
* Create a Notion page with release notes content.
* Returns { pageId, url }.
*/
export async function createReleaseDraft(
version: string,
markdownContent: string,
): Promise<{ pageId: string; url: string }> {
const apiKey = process.env.NOTION_API_KEY;
const parentPageId = process.env.NOTION_RELEASE_NOTES_PAGE;
if (!apiKey || !parentPageId) {
throw new Error("NOTION_API_KEY and NOTION_RELEASE_NOTES_PAGE must be set");
}
const blocks = markdownToBlocks(markdownContent);
const page = await notionRequest(apiKey, "POST", "/v1/pages", {
parent: { page_id: parentPageId },
properties: {
title: {
title: [{ text: { content: `v${version} Release Notes (Draft)` } }],
},
},
children: blocks,
});
return {
pageId: page.id,
url: page.url,
};
}
/**
* Read a Notion page's content back as markdown.
* Used on merge to get the (potentially human-edited) release notes.
*/
export async function readReleaseDraft(pageId: string): Promise<string> {
const apiKey = process.env.NOTION_API_KEY;
if (!apiKey) {
throw new Error("NOTION_API_KEY must be set");
}
const response = await notionRequest(
apiKey,
"GET",
`/v1/blocks/${pageId}/children?page_size=100`,
);
return blocksToMarkdown(response.results);
}
+140
View File
@@ -0,0 +1,140 @@
import fs from "fs";
import path from "path";
import { loadConfig, ROOT } from "./config.js";
export type BumpLevel = "patch" | "minor" | "major";
interface SemVer {
major: number;
minor: number;
patch: number;
prerelease: string | null;
}
export interface PublishablePackage {
name: string;
dir: string;
pkgJsonPath: string;
pkg: Record<string, any>;
isVersionedTogether: boolean;
isVersionedIndependently: boolean;
}
export function getCurrentVersion(): string {
const pkgPath = path.join(ROOT, "packages/react-core/package.json");
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
return pkg.version;
}
export function parseSemver(version: string): SemVer {
const match = version.match(
/^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.]+))?(?:\+(.+))?$/,
);
if (!match) {
throw new Error(`Invalid semver: ${version}`);
}
return {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
prerelease: match[4] || null,
};
}
export function computeNextStableVersion(
currentVersion: string,
bumpLevel: BumpLevel,
): string {
const v = parseSemver(currentVersion);
if (v.prerelease) {
return `${v.major}.${v.minor}.${v.patch}`;
}
switch (bumpLevel) {
case "major":
return `${v.major + 1}.0.0`;
case "minor":
return `${v.major}.${v.minor + 1}.0`;
case "patch":
return `${v.major}.${v.minor}.${v.patch + 1}`;
}
}
export function computePrereleaseVersion(
currentVersion: string,
suffix?: string,
): string {
const v = parseSemver(currentVersion);
const config = loadConfig();
const tag = config.prereleaseTag;
const id = suffix || String(Math.floor(Date.now() / 1000));
return `${v.major}.${v.minor}.${v.patch}-${tag}.${id}`;
}
export function getPublishablePackages(): PublishablePackage[] {
const config = loadConfig();
const allPackageNames = new Set([
...config.versionedTogether,
...config.versionedIndependently,
]);
const packagesDir = path.join(ROOT, "packages");
const results: PublishablePackage[] = [];
for (const dir of fs.readdirSync(packagesDir)) {
const pkgJsonPath = path.join(packagesDir, dir, "package.json");
if (!fs.existsSync(pkgJsonPath)) continue;
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
if (allPackageNames.has(pkg.name)) {
results.push({
name: pkg.name,
dir: path.join(packagesDir, dir),
pkgJsonPath,
pkg,
isVersionedTogether: config.versionedTogether.includes(pkg.name),
isVersionedIndependently: config.versionedIndependently.includes(
pkg.name,
),
});
}
}
return results;
}
export function bumpVersionedTogetherPackages(
newVersion: string,
): { name: string; oldVersion: string; newVersion: string }[] {
const packages = getPublishablePackages().filter(
(p) => p.isVersionedTogether,
);
const config = loadConfig();
const togetherNames = new Set(config.versionedTogether);
const updated: { name: string; oldVersion: string; newVersion: string }[] =
[];
for (const p of packages) {
const pkg = JSON.parse(fs.readFileSync(p.pkgJsonPath, "utf8"));
const oldVersion = pkg.version;
pkg.version = newVersion;
for (const depField of [
"dependencies",
"peerDependencies",
"devDependencies",
] as const) {
if (!pkg[depField]) continue;
for (const depName of Object.keys(pkg[depField])) {
if (togetherNames.has(depName)) {
pkg[depField][depName] = newVersion;
}
}
}
fs.writeFileSync(p.pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
updated.push({ name: p.name, oldVersion, newVersion });
}
return updated;
}
+120
View File
@@ -0,0 +1,120 @@
/**
* Prepare a release: bump versions, generate raw release notes.
* Runs inside the "create release PR" workflow.
*
* Usage: tsx scripts/release/prepare-release.ts --bump <patch|minor|major> [--dry-run]
*/
import fs from "fs";
import path from "path";
import {
getCurrentVersion,
computeNextStableVersion,
bumpVersionedTogetherPackages,
getPublishablePackages,
type BumpLevel,
} from "./lib/versions.js";
import {
getChangesSummary,
type ChangesSummary,
type Commit,
} from "./lib/changes.js";
import { ROOT } from "./lib/config.js";
function generateRawReleaseNotes(
version: string,
summary: ChangesSummary,
): string {
const lines: string[] = [];
lines.push(`## v${version}`, "");
if (summary.commits.length === 0) {
lines.push("No changes since last release.");
return lines.join("\n");
}
const features: Commit[] = [];
const fixes: Commit[] = [];
const other: Commit[] = [];
for (const c of summary.commits) {
if (/^feat[:(]/.test(c.subject)) features.push(c);
else if (/^fix[:(]/.test(c.subject)) fixes.push(c);
else other.push(c);
}
if (features.length > 0) {
lines.push("### Features", "");
for (const c of features)
lines.push(`- ${c.subject} (${c.hash.slice(0, 7)})`);
lines.push("");
}
if (fixes.length > 0) {
lines.push("### Fixes", "");
for (const c of fixes) lines.push(`- ${c.subject} (${c.hash.slice(0, 7)})`);
lines.push("");
}
if (other.length > 0) {
lines.push("### Other Changes", "");
for (const c of other) lines.push(`- ${c.subject} (${c.hash.slice(0, 7)})`);
lines.push("");
}
return lines.join("\n");
}
function main() {
const argv = process.argv.slice(2);
const dryRun = argv.includes("--dry-run");
const bumpIdx = argv.indexOf("--bump");
const bumpLevel = (
bumpIdx !== -1 ? argv[bumpIdx + 1] : null
) as BumpLevel | null;
if (!bumpLevel || !["patch", "minor", "major"].includes(bumpLevel)) {
console.error("Usage: prepare-release.ts --bump <patch|minor|major>");
process.exit(1);
}
const currentVersion = getCurrentVersion();
const nextVersion = computeNextStableVersion(currentVersion, bumpLevel);
console.log(`Current version: ${currentVersion}`);
console.log(`Bump level: ${bumpLevel}`);
console.log(`Next version: ${nextVersion}`);
const summary = getChangesSummary();
console.log(
`\nCommits since ${summary.lastTag || "beginning"}: ${summary.commitCount}`,
);
if (dryRun) {
console.log("\n[DRY RUN] Would bump these packages:");
for (const p of getPublishablePackages().filter(
(p) => p.isVersionedTogether,
)) {
console.log(` ${p.name}: ${p.pkg.version} -> ${nextVersion}`);
}
console.log("\n[DRY RUN] Exiting.");
return;
}
const updated = bumpVersionedTogetherPackages(nextVersion);
console.log(`\nBumped ${updated.length} packages to ${nextVersion}`);
const rawNotes = generateRawReleaseNotes(nextVersion, summary);
const releaseNotesPath = path.join(ROOT, "release-notes.md");
fs.writeFileSync(releaseNotesPath, rawNotes);
console.log("Raw release notes written to release-notes.md");
const outputPath = process.env.GITHUB_OUTPUT;
if (outputPath) {
fs.appendFileSync(outputPath, `version=${nextVersion}\n`);
fs.appendFileSync(outputPath, `tag=v${nextVersion}\n`);
}
console.log(`\nRelease prepared: v${nextVersion}`);
}
main();
+88
View File
@@ -0,0 +1,88 @@
/**
* Publish a prerelease (no git commits, no PRs).
*
* Takes the current version on main and appends a canary suffix.
* If --suffix is provided (e.g. "fix-user-issue"), the version becomes
* 1.55.2-canary.fix-user-issue. Otherwise it uses a unix timestamp.
*
* Always publishes with the "canary" dist-tag.
*
* Usage: tsx scripts/release/prerelease.ts [--suffix <label>] [--dry-run]
*/
import { spawnSync } from "child_process";
import {
getCurrentVersion,
computePrereleaseVersion,
bumpVersionedTogetherPackages,
getPublishablePackages,
} from "./lib/versions.js";
import { ROOT, loadConfig } from "./lib/config.js";
function run(cmd: string, args: string[], opts?: { cwd?: string }) {
const result = spawnSync(cmd, args, {
cwd: opts?.cwd ?? ROOT,
stdio: "inherit",
encoding: "utf8",
});
if (result.status !== 0) {
throw new Error(`Command failed: ${cmd} ${args.join(" ")}`);
}
return result;
}
function main() {
const argv = process.argv.slice(2);
const dryRun = argv.includes("--dry-run");
const suffixIdx = argv.indexOf("--suffix");
const suffix = suffixIdx !== -1 ? argv[suffixIdx + 1] : undefined;
const config = loadConfig();
const distTag = config.prereleaseTag;
const currentVersion = getCurrentVersion();
const prereleaseVersion = computePrereleaseVersion(currentVersion, suffix);
console.log(`Current version: ${currentVersion}`);
console.log(`Prerelease version: ${prereleaseVersion}`);
console.log(`Dist tag: ${distTag}`);
if (dryRun) {
console.log("\n[DRY RUN] Would bump these packages:");
for (const p of getPublishablePackages().filter(
(p) => p.isVersionedTogether,
)) {
console.log(` ${p.name}: ${p.pkg.version} -> ${prereleaseVersion}`);
}
console.log("\n[DRY RUN] Exiting.");
return;
}
// Bump versions in working directory (no commit)
const updated = bumpVersionedTogetherPackages(prereleaseVersion);
console.log(`\nBumped ${updated.length} packages to ${prereleaseVersion}`);
// Build all packages
console.log("\nBuilding packages...");
run("pnpm", ["run", "build"]);
// Publish each package
console.log("\nPublishing packages...");
const packages = getPublishablePackages().filter(
(p) => p.isVersionedTogether,
);
for (const p of packages) {
console.log(
` Publishing ${p.name}@${prereleaseVersion} with tag ${distTag}...`,
);
run(
"pnpm",
["publish", "--no-git-checks", "--tag", distTag, "--access", "public"],
{
cwd: p.dir,
},
);
}
console.log(`\nPrerelease published: ${prereleaseVersion} (tag: ${distTag})`);
}
main();
+138
View File
@@ -0,0 +1,138 @@
/**
* Publish a stable release (runs after merge of a release PR).
*
* 1. Reads the current version from package.json (already bumped by the release PR)
* 2. Optionally reads the Notion draft for the final release notes
* 3. Builds all packages
* 4. Publishes to npm with "latest" tag
* 5. Outputs the version for downstream steps (git tag, GitHub Release)
*
* Env vars:
* NPM_TOKEN npm auth token
* NOTION_API_KEY for reading edited release notes from Notion (optional)
* GITHUB_OUTPUT CI output file
*
* Usage: tsx scripts/release/publish-release.ts
*/
import fs from "fs";
import path from "path";
import { spawnSync } from "child_process";
import {
getCurrentVersion,
getPublishablePackages,
parseSemver,
} from "./lib/versions.js";
import { readReleaseDraft } from "./lib/notion.js";
import { ROOT } from "./lib/config.js";
function run(cmd: string, args: string[], opts?: { cwd?: string }) {
const result = spawnSync(cmd, args, {
cwd: opts?.cwd ?? ROOT,
stdio: "inherit",
encoding: "utf8",
});
if (result.status !== 0) {
throw new Error(`Command failed: ${cmd} ${args.join(" ")}`);
}
return result;
}
function getPublishedVersion(packageName: string): string | null {
const result = spawnSync("npm", ["view", packageName, "version"], {
encoding: "utf8",
timeout: 15000,
});
if (result.status !== 0) return null;
return result.stdout.trim() || null;
}
function isGreaterVersion(next: string, current: string): boolean {
const a = parseSemver(next);
const b = parseSemver(current);
if (a.major !== b.major) return a.major > b.major;
if (a.minor !== b.minor) return a.minor > b.minor;
return a.patch > b.patch;
}
async function main() {
const version = getCurrentVersion();
console.log(`Publishing version: ${version}`);
// Safety check: only allow clean semver (no prerelease suffixes like -canary.123)
const v = parseSemver(version);
if (v.prerelease) {
console.error(
`Refusing to publish: ${version} contains a prerelease suffix. ` +
`Stable releases must be clean semver (e.g. 1.55.3).`,
);
process.exit(1);
}
// Safety check: refuse to publish if version isn't greater than what's on npm
const published = getPublishedVersion("@copilotkit/react-core");
if (published) {
console.log(`Currently published version: ${published}`);
if (!isGreaterVersion(version, published)) {
console.error(
`Refusing to publish: ${version} is not greater than the currently published ${published}. ` +
`This can happen if a release/v* branch was created manually without using the release workflow.`,
);
process.exit(1);
}
}
// Try to read edited release notes from Notion
const notionRefPath = path.join(ROOT, "release-notes-notion.json");
const releaseNotesPath = path.join(ROOT, "release-notes.md");
if (fs.existsSync(notionRefPath)) {
try {
const ref = JSON.parse(fs.readFileSync(notionRefPath, "utf8"));
if (ref.pageId && process.env.NOTION_API_KEY) {
console.log("Reading edited release notes from Notion...");
const notionContent = await readReleaseDraft(ref.pageId);
if (notionContent.trim()) {
fs.writeFileSync(releaseNotesPath, notionContent);
console.log("Release notes updated from Notion draft.");
}
}
} catch (err: any) {
console.error(`Failed to read Notion draft: ${err.message}`);
console.log("Using release notes from the PR branch.");
}
}
// Build all packages
console.log("\nBuilding packages...");
run("pnpm", ["run", "build"]);
// Publish each package
console.log("\nPublishing packages...");
const packages = getPublishablePackages().filter(
(p) => p.isVersionedTogether,
);
for (const p of packages) {
console.log(` Publishing ${p.name}@${version}...`);
run(
"pnpm",
["publish", "--no-git-checks", "--tag", "latest", "--access", "public"],
{
cwd: p.dir,
},
);
}
// Output version for downstream steps
const outputPath = process.env.GITHUB_OUTPUT;
if (outputPath) {
fs.appendFileSync(outputPath, `version=${version}\n`);
}
console.log(`\nRelease published: ${version}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
@@ -1,21 +0,0 @@
# save the current branch
current_branch=$(git branch --show-current)
# check if branch starts with "pre/"
suggested_tag=$(echo $current_branch | sed 's/_/-/g')
# replace all non-alphanumeric characters except hyphens
suggested_tag=$(echo $suggested_tag | sed 's/[^a-zA-Z0-9-]/-/g')
# chop leading and trailing hyphens
suggested_tag=$(echo $suggested_tag | sed 's/^-//;s/-$//')
# exit pre mode if already in pre mode
pnpm changeset pre exit
# version
pnpm changeset version --snapshot $suggested_tag --tag pr --no-git-tag
# publish
pnpm changeset publish --no-git-tag --snapshot $suggested_tag --tag pr --no-git-tag