Skip to content

Commit c97c7b2

Browse files
authored
Merge pull request #287 from ckeditor/i/3710-release-via-ci
Internal: Release repository from CI.
2 parents 503c831 + da0e521 commit c97c7b2

11 files changed

+906
-669
lines changed

.circleci/config.yml

+166-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
version: 2.1
22

33
parameters:
4+
triggerCommitHash:
5+
type: string
6+
default: ""
47
isNightly:
58
type: boolean
69
default: false
10+
isRelease:
11+
type: boolean
12+
default: false
713

814
commands:
915
prepare_environment_command:
@@ -21,6 +27,7 @@ commands:
2127
- run:
2228
name: Install dependencies
2329
command: yarn install
30+
- prepare_environment_variables_commands
2431

2532
install_ssh_keys_command:
2633
description: "Install SSH keys"
@@ -29,6 +36,43 @@ commands:
2936
fingerprints:
3037
- "a0:41:a2:56:c8:7d:3f:29:41:d1:87:92:fd:50:2b:6b"
3138

39+
npm_login_command:
40+
description: "Enable interacting with `npm` using an auth token"
41+
steps:
42+
- run:
43+
name: Login to the npm registry using '.npmrc' file
44+
command: echo "//registry.npmjs.org/:_authToken=\${CKE5_NPM_TOKEN}" > ~/.npmrc
45+
46+
git_credentials_command:
47+
description: "Setup git configuration"
48+
steps:
49+
- run:
50+
name: Setup git configuration
51+
command: |
52+
git config --global user.email "[email protected]"
53+
git config --global user.name "CKEditorBot"
54+
55+
prepare_environment_variables_commands:
56+
description: "Prepare non-secret environment variables"
57+
steps:
58+
- run:
59+
name: Prepare environment variables
60+
command: |
61+
#!/bin/bash
62+
63+
# Non-secret environment variables needed for the pipeline scripts.
64+
CKE5_GITHUB_ORGANIZATION="ckeditor"
65+
CKE5_GITHUB_REPOSITORY="ckeditor5-vue"
66+
CKE5_CIRCLE_APPROVAL_JOB_NAME="release_approval"
67+
CKE5_GITHUB_RELEASE_BRANCH="master"
68+
69+
echo export CKE5_CIRCLE_APPROVAL_JOB_NAME=$CKE5_CIRCLE_APPROVAL_JOB_NAME >> $BASH_ENV
70+
echo export CKE5_GITHUB_RELEASE_BRANCH=$CKE5_GITHUB_RELEASE_BRANCH >> $BASH_ENV
71+
echo export CKE5_GITHUB_ORGANIZATION=$CKE5_GITHUB_ORGANIZATION >> $BASH_ENV
72+
echo export CKE5_GITHUB_REPOSITORY=$CKE5_GITHUB_REPOSITORY >> $BASH_ENV
73+
echo export CKE5_GITHUB_REPOSITORY_SLUG="$CKE5_GITHUB_ORGANIZATION/$CKE5_GITHUB_REPOSITORY" >> $BASH_ENV
74+
echo export CKE5_COMMIT_SHA1=$CIRCLE_SHA1 >> $BASH_ENV
75+
3276
jobs:
3377
notify_ci_failure:
3478
machine: true
@@ -58,7 +102,7 @@ jobs:
58102
command: yarn ckeditor5-dev-ci-circle-workflow-notifier
59103
no_output_timeout: 1h
60104

61-
main:
105+
validate_and_tests:
62106
machine: true
63107
steps:
64108
- checkout
@@ -105,22 +149,139 @@ jobs:
105149
name: Upload code coverage
106150
command: cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
107151

152+
release_prepare:
153+
machine: true
154+
steps:
155+
- checkout
156+
- bootstrap_repository_command
157+
- run:
158+
name: Check if packages are ready to be released
159+
command: npm run release:prepare-packages -- --verbose --compile-only
160+
161+
trigger_release_process:
162+
machine: true
163+
steps:
164+
- checkout
165+
- bootstrap_repository_command
166+
- run:
167+
name: Verify if the project is ready to release
168+
command: |
169+
#!/bin/bash
170+
171+
# Do not fail if the Node script ends with non-zero exit code.
172+
set +e
173+
174+
node scripts/ci/is-project-ready-to-release.js
175+
EXIT_CODE=$( echo $? )
176+
177+
if [ ${EXIT_CODE} -eq 1 ];
178+
then
179+
circleci-agent step halt
180+
fi
181+
- run:
182+
name: Trigger the release pipeline
183+
command: yarn ckeditor5-dev-ci-trigger-circle-build
184+
185+
release_project:
186+
machine: true
187+
steps:
188+
- checkout
189+
- bootstrap_repository_command
190+
- run:
191+
name: Verify the trigger commit from the repository
192+
command: |
193+
#!/bin/bash
194+
195+
CKE5_LATEST_COMMIT_HASH=$( git log -n 1 --pretty=format:%H origin/master )
196+
CKE5_TRIGGER_COMMIT_HASH=<< pipeline.parameters.triggerCommitHash >>
197+
198+
if [[ "${CKE5_LATEST_COMMIT_HASH}" != "${CKE5_TRIGGER_COMMIT_HASH}" ]]; then
199+
echo "There is a newer commit in the repository on the \`#master\` branch. Use its build to start the release."
200+
circleci-agent step halt
201+
fi
202+
- npm_login_command
203+
- git_credentials_command
204+
- run:
205+
name: Verify if a releaser triggered the job
206+
command: |
207+
#!/bin/bash
208+
209+
# Do not fail if the Node script ends with non-zero exit code.
210+
set +e
211+
212+
yarn ckeditor5-dev-ci-is-job-triggered-by-member
213+
EXIT_CODE=$( echo $? )
214+
215+
if [ ${EXIT_CODE} -ne 0 ];
216+
then
217+
echo "Aborting the release due to failed verification of the approver (no rights to release)."
218+
circleci-agent step halt
219+
fi
220+
- run:
221+
name: Disable the redundant workflows option
222+
command: yarn ckeditor5-dev-ci-circle-disable-auto-cancel-builds
223+
- run:
224+
name: Prepare the new version to release
225+
command: npm run release:prepare-packages -- --verbose
226+
- run:
227+
name: Publish the packages
228+
command: npm run release:publish-packages -- --verbose
229+
- run:
230+
name: Enable the redundant workflows option
231+
command: yarn ckeditor5-dev-ci-circle-enable-auto-cancel-builds
232+
when: always
233+
- run:
234+
name: Pack the "release/" directory (in case of failure)
235+
command: |
236+
zip -r ./release.zip ./release
237+
when: always
238+
- store_artifacts:
239+
path: ./release.zip
240+
when: always
241+
108242
workflows:
109243
version: 2
110244
main:
111-
unless: << pipeline.parameters.isNightly >>
245+
when:
246+
and:
247+
- equal: [ false, << pipeline.parameters.isNightly >> ]
248+
- equal: [ false, << pipeline.parameters.isRelease >> ]
112249
jobs:
113-
- main
250+
- validate_and_tests
251+
- release_prepare
252+
- trigger_release_process:
253+
requires:
254+
- validate_and_tests
255+
- release_prepare
256+
filters:
257+
branches:
258+
only:
259+
- master
114260
- notify_ci_failure:
115261
filters:
116262
branches:
117263
only:
118264
- master
119265

266+
release:
267+
when:
268+
and:
269+
- equal: [ false, << pipeline.parameters.isNightly >> ]
270+
- equal: [ true, << pipeline.parameters.isRelease >> ]
271+
jobs:
272+
- release_approval:
273+
type: approval
274+
- release_project:
275+
requires:
276+
- release_approval
277+
120278
nightly:
121-
when: << pipeline.parameters.isNightly >>
279+
when:
280+
and:
281+
- equal: [ true, << pipeline.parameters.isNightly >> ]
282+
- equal: [ false, << pipeline.parameters.isRelease >> ]
122283
jobs:
123-
- main
284+
- validate_and_tests
124285
- notify_ci_failure:
125286
hideAuthor: "true"
126287
filters:

README.md

+9-25
Original file line numberDiff line numberDiff line change
@@ -71,31 +71,15 @@ Before releasing a new version, run a demo project to confirm that the integrati
7171
3. Run `npm run test` to see if the project passes all automated tests.
7272
4. Run `npm run build` to see if the project with the integration builds without errors.
7373

74-
### Changelog
75-
76-
Before starting the release process, you need to generate the changelog:
77-
78-
```bash
79-
npm run changelog
80-
```
81-
82-
### Publishing
83-
84-
After generating the changelog, you are able to release the package.
85-
86-
First, you need to bump the version:
87-
88-
```bash
89-
npm run release:prepare-packages
90-
```
91-
92-
After bumping the version, you can publish the changes:
93-
94-
```bash
95-
npm run release:publish-packages
96-
```
97-
98-
Note: The `release/` directory will be published.
74+
This package's release process is automated via CircleCI. Before you start a new release, you'll need to prepare the changelog entries.
75+
76+
1. Make sure the `#master` branch is up-to-date: `git fetch && git checkout master && git pull`.
77+
1. Prepare a release branch: `git checkout -b release-[YYYY-MM-DD]` where `YYYY-MM-DD` is the current day.
78+
1. Generate the changelog entries: `yarn run changelog --branch release-[YYYY-MM-DD]`.
79+
* This task checks what changed in each package and bumps the version accordingly. If nothing changes at all, it won't create a new changelog entry. If changes were irrelevant (e.g., only dependencies), it would make an "internal changes" entry.
80+
* Scan the logs printed by the tool to search for errors (incorrect changelog entries). Incorrect entries (e.g., ones without the type) should be addressed. You may need to create entries for them manually. This is done directly in CHANGELOG.md (in the root directory). Make sure to verify the proposed version after you modify the changelog.
81+
1. Commit all changes and prepare a new pull request targeting the `#master` branch.
82+
1. Ping the @ckeditor/ckeditor-5-devops team to review the pull request and trigger the release process.
9983

10084
## License
10185

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"lint-staged": "^10.2.11",
5353
"listr2": "^6.5.0",
5454
"minimist": "^1.2.5",
55+
"semver": "^7.6.2",
5556
"typescript": "~5.4.5",
5657
"vite": "^5.3.1",
5758
"vitest": "^2.0.0",
@@ -60,6 +61,11 @@
6061
"vue-tsc": "^2.0.22",
6162
"webdriverio": "^8.39.0"
6263
},
64+
"resolutions": {
65+
"wrap-ansi": "7.0.0",
66+
"string-width": "4.1.0",
67+
"semver": "^7.0.0"
68+
},
6369
"engines": {
6470
"node": ">=18.0.0"
6571
},

scripts/changelog.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77

88
/* eslint-env node */
99

10+
import parseArguments from './utils/parsearguments.js';
11+
1012
/**
1113
* Scripts for generating the changelog before starting the release process.
1214
*/
1315

16+
const cliArguments = parseArguments( process.argv.slice( 2 ) );
17+
1418
import { generateChangelogForSinglePackage } from '@ckeditor/ckeditor5-dev-release-tools';
1519

16-
await generateChangelogForSinglePackage();
20+
await generateChangelogForSinglePackage( {
21+
releaseBranch: cliArguments.branch
22+
} );
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
5+
* For licensing, see LICENSE.md.
6+
*/
7+
8+
/* eslint-env node */
9+
10+
import { createRequire } from 'module';
11+
import releaseTools from '@ckeditor/ckeditor5-dev-release-tools';
12+
import { execSync } from 'child_process';
13+
import semver from 'semver';
14+
15+
const require = createRequire( import.meta.url );
16+
const { name } = require( '../../package.json' );
17+
18+
const latestPublishedVersion = execSync( `npm view ${ name }@latest version`, { encoding: 'utf-8' } ).trim();
19+
const changelogVersion = releaseTools.getLastFromChangelog();
20+
21+
if ( getVersionTag( changelogVersion ) !== 'latest' ) {
22+
console.log( `Aborting due non-latest changelog version (${ changelogVersion }).` );
23+
process.exit( 1 );
24+
}
25+
26+
if ( semver.lte( changelogVersion, latestPublishedVersion ) ) {
27+
console.log(
28+
`The proposed changelog (${ changelogVersion }) version is not greater than the published one (${ latestPublishedVersion }).`
29+
);
30+
process.exit( 1 );
31+
}
32+
33+
console.log( 'The project is ready to release.' );
34+
35+
/**
36+
* Returns an npm tag based on the specified release version.
37+
*
38+
* @param {String} version
39+
* @returns {String}
40+
*/
41+
function getVersionTag( version ) {
42+
const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ];
43+
44+
return versionTag;
45+
}

scripts/postinstall.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@ const ROOT_DIRECTORY = join(
1414
'..'
1515
);
1616

17-
// When installing a repository as a dependency, the `.git` directory does not exist.
18-
// In such a case, husky should not attach its hooks as npm treats it as a package, not a git repository.
19-
if ( existsSync( join( ROOT_DIRECTORY, '.git' ) ) ) {
20-
const { install } = await import( 'husky' );
17+
main()
18+
.catch( err => {
19+
console.error( err );
20+
} );
2121

22-
install();
22+
async function main() {
23+
// When installing a repository as a dependency, the `.git` directory does not exist.
24+
// In such a case, husky should not attach its hooks as npm treats it as a package, not a git repository.
25+
if ( existsSync( join( ROOT_DIRECTORY, '.git' ) ) ) {
26+
const { install } = await import( 'husky' );
27+
28+
install();
29+
}
2330
}

0 commit comments

Comments
 (0)