Skip to content

Commit 4954360

Browse files
chore: manual release process (#1305)
# πŸ”’ Implement Simple Manual Release Process with Security Fixes Example release PR created from release command: #1307 **Problem**: The existing automated release workflow used `lerna version` commands that pushed commits directly to the `master` branch, bypassing code review processes and branch protection rules. The new Policy bot makes this fail, as every commit to master requires approval. ## πŸ“‹ Summary This PR implements a streamlined manual release process that eliminates direct master branch commits while maintaining simplicity and reliability. The new process requires only 3 steps: 1. **Release**: `yarn release` (creates PR automatically) 2. **Review**: Review and approve the generated PR 3. **Publish**: Merge PR β†’ packages automatically publish to NPM ## πŸ”§ Changes Made ### New Files Added - **`scripts/create-release-pr.js`** - Automated script that creates release PRs - **`.github/workflows/publish-on-merge.yaml`** - New workflow for safe publishing - **`docs/MANUAL_RELEASE_DESIGN.md`** - Complete documentation of new process ### Files Modified - **`package.json`** - Added `yarn release` command - **`lerna.json`** - Security fixes: removed master from allowBranch, added push/gitTagVersion protections - **`.github/workflows/release.yaml`** - Disabled old workflow (renamed to `.disabled`) ## πŸš€ New Release Workflow ### For Release Managers First time only: ```bash brew install gh gh auth login ``` Then: ```bash # Simple 3-step process git checkout master && git pull origin master yarn release # β†’ Review PR β†’ Merge β†’ Auto-publish ✨ ``` ### What Happens Automatically 1. **`yarn release`** calculates versions and creates PR with changes 2. **PR merge** triggers publishing workflow 3. **`lerna publish from-package`** publishes to NPM and creates git tags ## πŸ›‘οΈ Technical Details ### Lerna Configuration Security Fixes ```json { "allowBranch": ["release/*"], // Removed "master" "push": false, // Prevents automatic pushing "gitTagVersion": false // Prevents automatic tagging } ``` ## πŸ“š Documentation Complete implementation guide available in `docs/MANUAL_RELEASE_DESIGN.md` including: - Step-by-step usage instructions - Security configuration requirements - Emergency procedures - Troubleshooting guide ## ⚠️ Required Setup (Post-Merge) **Enable Branch Protection**: - Require PR reviews for master branch - Prevent direct pushes (including from administrators) --------- Co-authored-by: Christian Roligheten <christian.roligheten@cognite.com>
1 parent 7f53521 commit 4954360

File tree

7 files changed

+218
-177
lines changed

7 files changed

+218
-177
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Publish on Release PR Merge
2+
3+
on:
4+
push:
5+
branches: [master]
6+
paths: ['packages/*/package.json']
7+
8+
jobs:
9+
publish:
10+
if: contains(github.event.head_commit.message, 'chore(release): publish new package versions')
11+
runs-on: ubuntu-latest
12+
environment: production
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: '20'
19+
20+
- name: Install dependencies
21+
run: yarn install --immutable
22+
23+
- name: Build packages
24+
run: yarn build
25+
26+
- name: Configure NPM
27+
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
28+
env:
29+
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
30+
31+
- name: Publish to NPM
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+
run: lerna publish from-package --yes --no-git-reset

β€Ž.github/workflows/release.yamlβ€Ž

Lines changed: 0 additions & 137 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Validate Release PR
2+
3+
on:
4+
pull_request:
5+
branches: [master]
6+
7+
jobs:
8+
validate-release:
9+
if: startsWith(github.head_ref, 'release/automated-')
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
fetch-depth: 0
15+
16+
- name: Validate release branch base
17+
run: |
18+
# Get the merge-base (common ancestor) of this branch and master
19+
MERGE_BASE=$(git merge-base HEAD origin/master)
20+
MASTER_HEAD=$(git rev-parse origin/master)
21+
22+
# Check if this branch was created from current master HEAD
23+
if [ "$MERGE_BASE" != "$MASTER_HEAD" ]; then
24+
echo "❌ Release branch was not created from current master HEAD"
25+
echo "Branch base: $MERGE_BASE"
26+
echo "Master HEAD: $MASTER_HEAD"
27+
echo ""
28+
echo "This release branch is based on outdated master."
29+
echo "Please close this PR and create a fresh one with 'yarn release'"
30+
exit 1
31+
fi
32+
33+
# Additional check: ensure no merge commits from master
34+
MERGE_COMMITS=$(git log --merges --grep="Merge.*master" --oneline | wc -l)
35+
if [ "$MERGE_COMMITS" -gt 0 ]; then
36+
echo "❌ Release branch contains merge commits from master"
37+
echo "This invalidates version calculations."
38+
echo "Please close this PR and create a fresh one with 'yarn release'"
39+
exit 1
40+
fi
41+
42+
echo "βœ… Release branch is properly based on current master"

β€ŽCONTRIBUTING.mdβ€Ž

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -75,50 +75,57 @@ Once the change is pushed to the master branch, it is time for a release.
7575

7676
## Releases & Versioning
7777

78-
Releases are done from the master branch, so when a pull request is merged,
79-
CI/CD will run tests, and if successful, do deploys.
80-
Documentation is built and deployed, and code snippets
81-
are exported to the service contract repo as a pull request.
82-
83-
Updating and uploading npm packages only happens if the HEAD commit of the master branch
84-
contains `[release]` in its description and the PR title starts with `feat` or `fix`.
85-
When CI/CD sees this, it will use lerna to update
86-
package versions of changed packages based on commit messages, and add the
87-
changes to the changelogs. The changes are comitted to the master branch
88-
with the new versions as git tags, and the new package versions are uploaded to npm.
89-
90-
We restrict new npm releases to `[release]`-tagged commits because lerna is
91-
quite aggressive in its versioning. Changes to any file not ignored by lerna will
92-
cause a PATCH bump. Markdown files and tests are ignored, but changing anything else,
93-
like a comment in a source file, will trigger a new version,
94-
irrespective of conventional commits.
95-
96-
This does _not_ mean you should store unfinished work on the master branch.
97-
Another package may be ready for release, and once a `[release]`
98-
commit is pushed, all changed packages are updated.
99-
Repository administrators should be in control of `[release]` commits.
100-
101-
To add a release commit to a clean working tree, use the command
78+
**We use a simple manual release process for enhanced security and control.**
79+
80+
### How to Release
81+
82+
Releases are initiated manually but executed automatically through pull requests:
10283

10384
```bash
85+
# 1. Pull latest master and run release command
10486
git checkout master
105-
git pull
106-
git commit --allow-empty -m "chore: trigger [release]"
107-
git push
87+
git pull origin master
88+
yarn release
89+
90+
# 2. Review the generated PR in GitHub UI
91+
# 3. Merge PR β†’ packages automatically publish to NPM
10892
```
10993

110-
If you want to push the empty commit to master via a pull request,
111-
use a squash merge (not rebase+ff). Otherwise GitHub will ignore the empty PR.
94+
### What Happens
95+
96+
1. **`yarn release`** - Calculates version bumps using conventional commits and creates a PR with:
97+
98+
- Updated `package.json` versions for changed packages
99+
- Generated changelog entries
100+
- Automatic branch creation and push
101+
102+
2. **PR Review** - Review the version changes and release notes in the generated PR
103+
104+
3. **Auto-publish** - When the PR is merged to master:
105+
- Packages are built and published to NPM
106+
- Git tags are created automatically
107+
- GitHub releases are generated
108+
109+
### Security Features
112110

113-
Also, keep in mind that the `[release]` commit has to be the HEAD of
114-
master, and Github Action only runs on the HEAD. If HEAD has changed by the time
115-
the versioning happens, Github Action will fail.
111+
- βœ… **No direct master pushes** - All changes go through PR review
112+
- βœ… **Manual approval required** - Human oversight for every release
113+
- βœ… **Clear audit trail** - Every release documented in PR history
114+
- βœ… **Branch protection compatible** - Works with strict branch protection rules
116115

117-
In order to perform a major release by merging a release candidate branch to master by keeping the same major version in release candidate version.
116+
### Prerequisites
118117

119-
- Create a PR from release-* to master
120-
- Make a PR title and the message when merging to be ` chore(): details [release]`
121-
- The release tag is to trigger the release of the package.
118+
- Repository must have `production` environment configured for manual approval
119+
- `NPM_PUBLISH_TOKEN` secret must be available
120+
121+
### Emergency Releases
122+
123+
Use the same process - no exceptions. The security model ensures all releases are reviewable:
124+
125+
```bash
126+
yarn release # Create emergency release PR
127+
# Review β†’ Merge β†’ Auto-publish
128+
```
122129

123130
## Patching older major versions
124131

@@ -196,7 +203,9 @@ We have multiple utilities to easy pagination handling. The first entrypoint is
196203
```
197204
- autoPagingEach
198205
```ts
199-
for await (const asset of client.assets.list().autoPagingEach({ limit: Infinity })) {
206+
for await (const asset of client.assets
207+
.list()
208+
.autoPagingEach({ limit: Infinity })) {
200209
// ...
201210
}
202211
```
@@ -221,6 +230,7 @@ We offer users to get hold of the HTTP response status code and headers through
221230
### Cognite Clients
222231

223232
There is a Cognite Client per SDK package:
233+
224234
- [Core](./packages/core/src/baseCogniteClient.ts)
225235
- [Stable](./packages/stable/src/cogniteClient.ts)
226236
- [Beta](./packages/beta/src/cogniteClient.ts)
@@ -229,7 +239,6 @@ There is a Cognite Client per SDK package:
229239

230240
The Core one is the base, meaning the others extends from it.
231241

232-
233242
#### Authentication
234243

235244
The authentication logic lives in the [core BaseCogniteClient](./packages/core/src/baseCogniteClient.ts).
@@ -239,4 +248,4 @@ The SDK will call this method when:
239248

240249
- The user calls `authenticate` on the client.
241250
- The SDK receives a 401 from the API.
242-
When multiple requests receives a 401, then only a single call to `oidcTokenProvider` will be invoked. All requests will wait for `oidcTokenProvider` to resolve/reject. If it's resolved, then all the requests will retry before returning the response to the SDK caller. However, if the resolved access token matches the original access token, then no retry will be performed.
251+
When multiple requests receives a 401, then only a single call to `oidcTokenProvider` will be invoked. All requests will wait for `oidcTokenProvider` to resolve/reject. If it's resolved, then all the requests will retry before returning the response to the SDK caller. However, if the resolved access token matches the original access token, then no retry will be performed.

β€Žlerna.jsonβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
"**/*.test.ts"
1111
],
1212
"message": "chore(release): publish new package versions [skip ci]",
13-
"allowBranch": ["master", "release-*"]
13+
"allowBranch": ["release/*"],
14+
"push": false,
15+
"gitTagVersion": false
1416
}
1517
}
1618
}

β€Žpackage.jsonβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"test-snippets": "yarn allsdk test-snippets --stream",
5252
"test-samples": "SKIP_PREFLIGHT_CHECK=true yarn allsamples test --stream",
5353
"codegen": "ts-node packages/codegen/src/cli.ts",
54+
"release": "node scripts/create-release-pr.js",
5455
"prepare": "husky"
5556
},
5657
"devDependencies": {

0 commit comments

Comments
Β (0)