Skip to content

Commit 2a880fb

Browse files
committed
ci: add scope dropdown (monorepo, cli, angular) to release workflows
Each release scope has its own packages, version source, and independent version track: - monorepo: 12 core @copilotkit/* packages (shared version) - cli: copilotkit CLI (independent version) - angular: @copilotkitnext/angular (independent version) Branch pattern is now release/publish/<scope>/v<version> and git tags use <scope>/v<version> for non-monorepo scopes.
1 parent dacc21e commit 2a880fb

11 files changed

Lines changed: 274 additions & 127 deletions

File tree

.github/workflows/prerelease.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ name: release / pre
33
on:
44
workflow_dispatch:
55
inputs:
6+
scope:
7+
description: "What to release"
8+
required: true
9+
type: choice
10+
options:
11+
- monorepo
12+
- cli
13+
- angular
614
suffix:
715
description: "Version suffix (e.g. 'fix-user-issue'). Leave blank for timestamp."
816
required: false
@@ -55,9 +63,9 @@ jobs:
5563

5664
- name: Publish prerelease
5765
run: |
58-
ARGS=""
66+
ARGS="--scope ${{ inputs.scope }}"
5967
if [ -n "${{ inputs.suffix }}" ]; then
60-
ARGS="--suffix ${{ inputs.suffix }}"
68+
ARGS="$ARGS --suffix ${{ inputs.suffix }}"
6169
fi
6270
if [ "${{ inputs.dry_run }}" == "true" ]; then
6371
ARGS="$ARGS --dry-run"

.github/workflows/publish-release.yml

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@ jobs:
2020
# Only run when a release PR is merged (not just closed)
2121
if: >
2222
github.event.pull_request.merged == true &&
23-
startsWith(github.event.pull_request.head.ref, 'release/publish/v')
23+
startsWith(github.event.pull_request.head.ref, 'release/publish/')
2424
runs-on: ubuntu-latest
2525
timeout-minutes: 20
2626
steps:
27+
- name: Extract scope from branch
28+
id: meta
29+
run: |
30+
BRANCH="${{ github.event.pull_request.head.ref }}"
31+
# Branch format: release/publish/<scope>/v<version>
32+
SCOPE=$(echo "$BRANCH" | sed 's|release/publish/\([^/]*\)/v.*|\1|')
33+
echo "scope=$SCOPE" >> $GITHUB_OUTPUT
34+
echo "Detected scope: $SCOPE"
35+
2736
- name: Checkout Repo
2837
uses: actions/checkout@v4
2938
with:
@@ -54,7 +63,7 @@ jobs:
5463

5564
- name: Publish to npm
5665
id: publish
57-
run: pnpm tsx scripts/release/publish-release.ts
66+
run: pnpm tsx scripts/release/publish-release.ts --scope ${{ steps.meta.outputs.scope }}
5867
env:
5968
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
6069
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -67,22 +76,33 @@ jobs:
6776
6877
- name: Create and push git tag
6978
run: |
70-
TAG="v${{ steps.publish.outputs.version }}"
71-
git tag -a "$TAG" -m "Release ${{ steps.publish.outputs.version }}"
79+
SCOPE="${{ steps.meta.outputs.scope }}"
80+
VERSION="${{ steps.publish.outputs.version }}"
81+
if [ "$SCOPE" == "monorepo" ]; then
82+
TAG="v${VERSION}"
83+
else
84+
TAG="${SCOPE}/v${VERSION}"
85+
fi
86+
git tag -a "$TAG" -m "Release ${SCOPE} ${VERSION}"
7287
git push origin "$TAG"
88+
echo "tag=$TAG" >> $GITHUB_OUTPUT
89+
id: tag
7390

7491
- name: Create GitHub Release
7592
uses: actions/github-script@v7
7693
env:
77-
RELEASE_TAG: v${{ steps.publish.outputs.version }}
78-
RELEASE_NAME: v${{ steps.publish.outputs.version }}
94+
RELEASE_TAG: ${{ steps.tag.outputs.tag }}
95+
RELEASE_SCOPE: ${{ steps.meta.outputs.scope }}
96+
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
7997
with:
8098
github-token: ${{ secrets.GITHUB_TOKEN }}
8199
script: |
82100
const fs = require("fs");
83101
const { owner, repo } = context.repo;
84102
const tag = process.env.RELEASE_TAG;
85-
const name = process.env.RELEASE_NAME;
103+
const scope = process.env.RELEASE_SCOPE;
104+
const version = process.env.RELEASE_VERSION;
105+
const name = scope === "monorepo" ? `v${version}` : `${scope}/v${version}`;
86106
87107
let body = "";
88108
try {
@@ -112,6 +132,6 @@ jobs:
112132
run: |
113133
echo "## Release Published" >> $GITHUB_STEP_SUMMARY
114134
echo "" >> $GITHUB_STEP_SUMMARY
135+
echo "**Scope:** ${{ steps.meta.outputs.scope }}" >> $GITHUB_STEP_SUMMARY
115136
echo "**Version:** ${{ steps.publish.outputs.version }}" >> $GITHUB_STEP_SUMMARY
116-
echo "**Tag:** v${{ steps.publish.outputs.version }}" >> $GITHUB_STEP_SUMMARY
117-
echo "**npm:** https://www.npmjs.com/package/@copilotkit/react-core/v/${{ steps.publish.outputs.version }}" >> $GITHUB_STEP_SUMMARY
137+
echo "**Tag:** ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY

.github/workflows/stable-release.yml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ name: release / create-pr
33
on:
44
workflow_dispatch:
55
inputs:
6+
scope:
7+
description: "What to release"
8+
required: true
9+
type: choice
10+
options:
11+
- monorepo
12+
- cli
13+
- angular
614
bump:
715
description: "Version bump level"
816
required: true
@@ -44,10 +52,10 @@ jobs:
4452
owner,
4553
repo,
4654
state: "open",
47-
head_prefix: `${owner}:release/publish/v`,
55+
head_prefix: `${owner}:release/publish/`,
4856
});
4957
50-
const releasePRs = prs.filter(pr => pr.head.ref.startsWith("release/publish/v"));
58+
const releasePRs = prs.filter(pr => pr.head.ref.startsWith("release/publish/"));
5159
if (releasePRs.length > 0) {
5260
const existing = releasePRs.map(pr => ` - #${pr.number}: ${pr.title} (${pr.html_url})`).join("\n");
5361
core.setFailed(
@@ -78,9 +86,9 @@ jobs:
7886
id: prepare
7987
run: |
8088
if [ "${{ inputs.dry_run }}" == "true" ]; then
81-
pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }} --dry-run
89+
pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }} --scope ${{ inputs.scope }} --dry-run
8290
else
83-
pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }}
91+
pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }} --scope ${{ inputs.scope }}
8492
fi
8593
8694
- name: Generate AI release notes
@@ -98,21 +106,21 @@ jobs:
98106
uses: peter-evans/create-pull-request@v8
99107
with:
100108
token: ${{ secrets.GITHUB_TOKEN }}
101-
branch: release/publish/v${{ steps.prepare.outputs.version }}
109+
branch: release/publish/${{ inputs.scope }}/v${{ steps.prepare.outputs.version }}
102110
delete-branch: true
103-
commit-message: "chore: release v${{ steps.prepare.outputs.version }}"
104-
title: "chore: release v${{ steps.prepare.outputs.version }}"
111+
commit-message: "chore: release ${{ inputs.scope }} v${{ steps.prepare.outputs.version }}"
112+
title: "chore: release ${{ inputs.scope }} v${{ steps.prepare.outputs.version }}"
105113
body: |
106-
## Release v${{ steps.prepare.outputs.version }}
114+
## Release ${{ inputs.scope }} v${{ steps.prepare.outputs.version }}
107115
108-
**Bump:** `${{ inputs.bump }}`
116+
**Scope:** `${{ inputs.scope }}` | **Bump:** `${{ inputs.bump }}`
109117
110118
---
111119
112120
### How this release process works
113121
114-
1. **This PR was created automatically** by the "Create Release PR" workflow.
115-
It bumped all `@copilotkit/*` package versions to `${{ steps.prepare.outputs.version }}`
122+
1. **This PR was created automatically** by the "release / create-pr" workflow.
123+
It bumped the `${{ inputs.scope }}` packages to `${{ steps.prepare.outputs.version }}`
116124
and generated AI-enhanced release notes.
117125
118126
2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build)
@@ -121,10 +129,10 @@ jobs:
121129
3. **Review the release notes** in `release-notes.md` in this PR.
122130
If a Notion draft was created, you can edit the release notes there before merging.
123131
124-
4. **When this PR is merged**, the `publish-release` workflow automatically:
132+
4. **When this PR is merged**, the `release / publish` workflow automatically:
125133
- Builds all packages
126-
- Publishes all `@copilotkit/*` packages to npm at version `${{ steps.prepare.outputs.version }}`
127-
- Creates git tag `v${{ steps.prepare.outputs.version }}`
134+
- Publishes the `${{ inputs.scope }}` packages to npm at version `${{ steps.prepare.outputs.version }}`
135+
- Creates git tag `${{ inputs.scope }}/v${{ steps.prepare.outputs.version }}`
128136
- Creates a GitHub Release with the final release notes
129137
130138
### Before merging

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
"docs": "pnpm -C examples/v2/docs dev",
2222
"demo:angular": "nx run-many -t dev --projects=examples/v2/angular/demo,examples/v2/angular/demo-server",
2323
"demo:react": "pnpm -C examples/v2/react/demo dev",
24-
"release:prepare:dry": "tsx scripts/release/prepare-release.ts --dry-run",
25-
"release:prerelease:dry": "tsx scripts/release/prerelease.ts --dry-run",
24+
"release:prepare:dry": "tsx scripts/release/prepare-release.ts --scope monorepo --bump patch --dry-run",
25+
"release:prerelease:dry": "tsx scripts/release/prerelease.ts --scope monorepo --dry-run",
2626
"prepare": "lefthook install",
2727
"graph": "nx graph",
2828
"publint": "nx run-many -t publint --projects=packages/**",

release.config.json

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
{
22
"prereleaseTag": "canary",
3-
"versionedTogether": [
4-
"@copilotkit/a2ui-renderer",
5-
"@copilotkit/core",
6-
"@copilotkit/react-core",
7-
"@copilotkit/react-textarea",
8-
"@copilotkit/react-ui",
9-
"@copilotkit/runtime",
10-
"@copilotkit/runtime-client-gql",
11-
"@copilotkit/sdk-js",
12-
"@copilotkit/shared",
13-
"@copilotkit/sqlite-runner",
14-
"@copilotkit/voice",
15-
"@copilotkit/web-inspector"
16-
],
17-
"versionedIndependently": ["@copilotkitnext/angular", "copilotkit"]
3+
"scopes": {
4+
"monorepo": {
5+
"packages": [
6+
"@copilotkit/a2ui-renderer",
7+
"@copilotkit/core",
8+
"@copilotkit/react-core",
9+
"@copilotkit/react-textarea",
10+
"@copilotkit/react-ui",
11+
"@copilotkit/runtime",
12+
"@copilotkit/runtime-client-gql",
13+
"@copilotkit/sdk-js",
14+
"@copilotkit/shared",
15+
"@copilotkit/sqlite-runner",
16+
"@copilotkit/voice",
17+
"@copilotkit/web-inspector"
18+
],
19+
"versionSource": "@copilotkit/react-core",
20+
"sharedVersion": true
21+
},
22+
"cli": {
23+
"packages": ["copilotkit"],
24+
"versionSource": "copilotkit",
25+
"sharedVersion": false
26+
},
27+
"angular": {
28+
"packages": ["@copilotkitnext/angular"],
29+
"versionSource": "@copilotkitnext/angular",
30+
"sharedVersion": false
31+
}
32+
}
1833
}

scripts/release/lib/config.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,31 @@ export const ROOT = path.resolve(
77
"../../..",
88
);
99

10+
export type ReleaseScope = "monorepo" | "cli" | "angular";
11+
12+
export interface ScopeConfig {
13+
packages: string[];
14+
versionSource: string;
15+
sharedVersion: boolean;
16+
}
17+
1018
export interface ReleaseConfig {
1119
prereleaseTag: string;
12-
versionedTogether: string[];
13-
versionedIndependently: string[];
20+
scopes: Record<ReleaseScope, ScopeConfig>;
1421
}
1522

1623
export function loadConfig(): ReleaseConfig {
1724
const configPath = path.join(ROOT, "release.config.json");
1825
return JSON.parse(fs.readFileSync(configPath, "utf8"));
1926
}
27+
28+
export function getScopeConfig(scope: ReleaseScope): ScopeConfig {
29+
const config = loadConfig();
30+
const scopeConfig = config.scopes[scope];
31+
if (!scopeConfig) {
32+
throw new Error(
33+
`Unknown scope: ${scope}. Valid scopes: ${Object.keys(config.scopes).join(", ")}`,
34+
);
35+
}
36+
return scopeConfig;
37+
}

scripts/release/lib/versions.test.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, it, expect, vi, beforeEach } from "vitest";
1+
import { describe, it, expect, vi } from "vitest";
22
import {
33
parseSemver,
44
computeNextStableVersion,
@@ -10,9 +10,36 @@ vi.mock("./config.js", () => ({
1010
ROOT: "/mock",
1111
loadConfig: () => ({
1212
prereleaseTag: "canary",
13-
versionedTogether: [],
14-
versionedIndependently: [],
13+
scopes: {
14+
monorepo: {
15+
packages: [],
16+
versionSource: "@copilotkit/react-core",
17+
sharedVersion: true,
18+
},
19+
cli: { packages: [], versionSource: "copilotkit", sharedVersion: false },
20+
angular: {
21+
packages: [],
22+
versionSource: "@copilotkitnext/angular",
23+
sharedVersion: false,
24+
},
25+
},
1526
}),
27+
getScopeConfig: (scope: string) => {
28+
const scopes: Record<string, any> = {
29+
monorepo: {
30+
packages: [],
31+
versionSource: "@copilotkit/react-core",
32+
sharedVersion: true,
33+
},
34+
cli: { packages: [], versionSource: "copilotkit", sharedVersion: false },
35+
angular: {
36+
packages: [],
37+
versionSource: "@copilotkitnext/angular",
38+
sharedVersion: false,
39+
},
40+
};
41+
return scopes[scope];
42+
},
1643
}));
1744

1845
describe("parseSemver", () => {

0 commit comments

Comments
 (0)