diff --git a/RELEASE.md b/RELEASE.md
index 9f3e94323..8e5920b94 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -15,90 +15,69 @@ QUnit aims for its releases to be reproducible. Recent releases are automaticall
>
> System prerequisites:
>
-> * Node.js 12, or later.
+> * Node.js 14, or later.
> * Git 2.11, or later.
-1. Ensure that all changes for this release have been merged into the main branch. For patch releases, try landing any other bug fixes; for minor releases, ensure new features have been documented and tested. Major releases likely have their own checklist.
+Ensure that all changes for this release have been merged into the main branch. For patch releases, try landing any other bug fixes; for minor releases, ensure new features have been documented and tested. Major releases likely have their own checklist.
-2. Create a local `release` branch, and ensure it is up-to-date:
- * Verify that the canonical repository is cloned (not a fork):
+1. Create a local `release` branch, and ensure it is up-to-date:
+ Verify that the canonical repository is cloned (not a fork):
```
git remote -v
# …
# origin git@github.com:qunitjs/qunit.git
```
- * Create or reset the `release` branch:
+ Create or reset the `release` branch:
```
git remote update && git checkout -B release -t origin/main
```
-3. Install dev dependencies and run the tests:
+2. Install dev dependencies:
```
- npm ci && npm test
+ npm ci
```
- Run the tests in various real browsers, either locally or via [BrowserStack](https://www.browserstack.com/):
- ```
- python3 -m http.server 4000
- # or:
- # php -S localhost:4000
- open http://localhost:4000/test/
+3. Create the release preparation commit:
+ ```
+ node build/prep-release.js @VERSION
```
-4. Create and push the release preparation commit:
+ * Use `git add -p` to review the changes.
+ * In `AUTHORS.txt`, if you see duplicate entries, then use the `.mailmap` file to normalize them to a canonical name and e-mail address, and then re-run the above command.
+ * Edit `History.md` to remove changes not relevant to end-users (e.g. changes relating to tests, build, internal refactoring, doc fixes, etc.).
+
+ Commit your changes with the following message (replace `@VERSION` with the release version):
+ ```
+ Build: Prepare @VERSION release
+ ```
- 1. Update the package.json and AUTHORS.txt files, by running the below command (replace `@VERSION` with the release version):
- ```
- node build/prep-release.js @VERSION
- ```
- * Use `git add -p` to review the changes.
- * In `AUTHORS.txt`, if you see duplicate entries, then use the `.mailmap` file to normalize them to a canonical name and e-mail address, and then re-run the above command.
- * Edit `History.md` to remove change not relevant to end-users (e.g. changes relating to tests, build, internal refactoring, doc fixes, etc.).
- 2. Commit the above changes with the following message (replace `@VERSION` with the release version):
- ```
- Build: Prepare @VERSION release
- ```
- 3. Push the `release` branch to GitHub.
- 4. Create a pull request, and merge it once CI is passing.
+ Push the `release` branch to GitHub.
+ Once CI is passing, push again, this time to the (protected) `main` branch.
## Performing the release
-5. Create a local `release` branch, and ensure it is up-to-date:
- * Run `git remote -v` and verify the following:
- ```
- origin git@github.com:qunitjs/qunit.git
- ```
- * Create or reset the `release` branch:
- ```
- git remote update && git checkout -B release -t origin/main
- ```
- * Verify that the latest commit is your release preparation commit:
- ```
- git show
- # Build: Prepare x.y.z release
- # …
- ```
+Verify that your local repo is at the release preparation commit:
-6. Make changes for the release commit:
- * Set the release version for npm and Bower metadata (replace `@VERSION` with the release version):
- ```
- node build/set-release.js @VERSION
- ```
- This script will edit `package.json` and `bower.json`. It does not need any credentials or permissions, apart from read-write in the project directory.
+```
+git show
+# Build: Prepare x.y.z release
+# …
+```
- * Generate the release artifacts:
+4. Build the release:
```
- npm run build
+ node build/build-release.js @VERSION
```
+ This script does not need any credentials or permissions, and may be run in a container that can only read-write the current directory. It will edit `package.json` and `bower.json`, and then execute `npm run build` to generate the release artifacts.
- * Review the changes to the package and library files, compared to the previous release.
+ Review the changes to the package, compared to the previous release.
```
node build/review-package.js @LAST_VERSION
# … reviews package.json, qunit.js, and qunit.css
```
-7. Commit and publish the release to GitHub.
⚠️ Do not push to the main branch!
+5. Publish to GitHub.
⚠️ Do not push to the main branch!
```
git add -f package.json bower.json qunit/
git commit -m "Release @VERSION"
@@ -106,15 +85,14 @@ QUnit aims for its releases to be reproducible. Recent releases are automaticall
git push --tags
```
-8. Verify that Bower sees the release, by running `npx bower info qunit` and checking that the latest
- version is indeed the version we just published.
-
-9. Publish the release to npm:
- * Use `git status` to confirm once more that you have a clean working copy, apart from release artifacts in `qunit/`.
- * Run `npm publish`, this will bundle the current directory and publish it to npm with the name and version specified in `package.json`.
- * Verify that the release is displayed at .
+6. Publish to npm:
+ ```
+ npm publish
+ ````
+ This will bundle the current directory and publish it to npm with the name and version specified in `package.json`.
+ Verify that the release is displayed at .
-10. Publish the release to the jQuery CDN:
+7. Publish to the jQuery CDN:
* Prepare the commit locally:
```
node build/auth-cdn-commit.js real @VERSION
@@ -124,7 +102,6 @@ QUnit aims for its releases to be reproducible. Recent releases are automaticall
```
cd __codeorigin
git show
- # …
git push
```
* Verify that the release is listed at and accessible via
diff --git a/build/auth-cdn-commit.js b/build/auth-cdn-commit.js
deleted file mode 100644
index 44dc14662..000000000
--- a/build/auth-cdn-commit.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// Helper to commit QUnit releases to the CDN repository.
-//
-// See also RELEASE.md.
-//
-// Inspired by .
-
-const fs = require('fs');
-const cp = require('child_process');
-const path = require('path');
-const remotes = {
- real: 'git@github.com:jquery/codeorigin.jquery.com.git',
- test: 'git@github.com:jquery/fake-cdn.git'
-};
-const files = {
- 'qunit/qunit.js': 'cdn/qunit/qunit-@VERSION.js',
- 'qunit/qunit.css': 'cdn/qunit/qunit-@VERSION.css'
-};
-const commitMessage = 'qunit: Added version @VERSION';
-
-const Cdn = {
- clone (remoteKey, repoPath) {
- const remote = remotes[remoteKey];
- if (!remote) {
- throw new Error('Unknown remote ' + remoteKey);
- }
- console.log('... cloning ' + remote);
- if (fs.existsSync(repoPath)) {
- fs.rmdirSync(repoPath, { recursive: true });
- }
- cp.execFileSync('git', [
- 'clone',
- '--depth=5',
- '-q',
- remote,
- repoPath
- ]);
- },
- commit (remoteKey, version) {
- if (!remoteKey || !version) {
- throw new Error(
- 'Parameters are \n\n ' +
- ' One of "test" or "real".\n ' +
- ' E.g. "1.2.3".\n\nExample: node auth-cdn-push.js test 2.4.0\n'
- );
- }
- const repoPath = path.join(__dirname, '..', '__codeorigin');
- Cdn.clone(remoteKey, repoPath);
-
- const toCommit = [];
- for (const src in files) {
- const srcPath = path.join(__dirname, '..', src);
- const dest = files[src].replace('@VERSION', version);
- const destPath = path.join(repoPath, dest);
- console.log('... copying ' + src + ' to ' + dest);
- if (remoteKey === 'test') {
- const destParent = path.dirname(destPath);
- fs.mkdirSync(destParent, { recursive: true });
- }
- fs.copyFileSync(srcPath, destPath, fs.constants.COPYFILE_EXCL);
- toCommit.push(dest);
- }
-
- console.log('... creating commit');
- cp.execFileSync('git', [
- 'add',
- ...toCommit
- ], { cwd: repoPath });
- cp.execFileSync('git', [
- 'commit',
- '-m',
- commitMessage.replace('@VERSION', version)
- ], { cwd: repoPath });
- }
-};
-
-const [remote, version] = process.argv.slice(2);
-
-try {
- Cdn.commit(remote, version);
-} catch (e) {
- console.error(e.toString());
- process.exit(1);
-}
diff --git a/build/build-release.js b/build/build-release.js
new file mode 100644
index 000000000..b4e973add
--- /dev/null
+++ b/build/build-release.js
@@ -0,0 +1,144 @@
+// Helper to set the QUnit release version in various places,
+// and prepare a local commit in a codeorigin.git clone.
+//
+// To test the local commit step, with an alternate remote
+// pass "test" as the second argument.
+//
+// See also RELEASE.md.
+//
+// Inspired by .
+
+const cp = require('child_process');
+const fs = require('fs');
+const path = require('path');
+
+const cdnRemotes = {
+ anonymous: 'https://github.com/jquery/codeorigin.jquery.com.git',
+ auth: 'git@github.com:jquery/codeorigin.jquery.com.git'
+};
+const cdnFiles = {
+ 'qunit/qunit.js': 'cdn/qunit/qunit-@VERSION.js',
+ 'qunit/qunit.css': 'cdn/qunit/qunit-@VERSION.css'
+};
+const cdnCommitMessage = 'qunit: Added version @VERSION';
+
+const Repo = {
+ buildFiles (version) {
+ if (typeof version !== 'string' || !/^\d+\.\d+\.\d+$/.test(version)) {
+ throw new Error('Invalid or missing version argument');
+ }
+ {
+ const file = 'bower.json';
+ console.log(`Updating ${file}...`);
+ const filePath = path.join(__dirname, '..', file);
+ const json = fs.readFileSync(filePath, 'utf8');
+ const packageIndentation = json.match(/\n([\t\s]+)/)[1];
+ const data = JSON.parse(json);
+
+ data.version = version;
+
+ fs.writeFileSync(
+ filePath,
+ JSON.stringify(data, null, packageIndentation) + '\n'
+ );
+ }
+ {
+ const file = 'package.json';
+ console.log(`Updating ${file}...`);
+ const filePath = path.join(__dirname, '..', file);
+ const json = fs.readFileSync(filePath, 'utf8');
+ const packageIndentation = json.match(/\n([\t\s]+)/)[1];
+ const data = JSON.parse(json);
+
+ data.version = version;
+ data.author.url = data.author.url.replace('main', version);
+
+ fs.writeFileSync(
+ filePath,
+ JSON.stringify(data, null, packageIndentation) + '\n'
+ );
+ }
+ {
+ console.log('Running `npm run build`...');
+ cp.execFileSync('npm', [
+ 'run',
+ 'build'
+ ], { encoding: 'utf8' });
+ }
+ },
+ cdnClone (repoPath) {
+ const remote = cdnRemotes.anonymous;
+ console.log('... cloning ' + remote);
+ if (fs.existsSync(repoPath)) {
+ fs.rmdirSync(repoPath, { recursive: true });
+ }
+ cp.execFileSync('git', [
+ 'clone',
+ '--depth=5',
+ '-q',
+ remote,
+ repoPath
+ ]);
+ },
+ cdnCommit (version) {
+ if (!version) {
+ throw new Error('Missing parameters');
+ }
+ const repoPath = path.join(__dirname, '..', '__codeorigin');
+ Repo.cdnClone(repoPath);
+
+ const toCommit = [];
+ for (const src in cdnFiles) {
+ const srcPath = path.join(__dirname, '..', src);
+ const dest = cdnFiles[src].replace('@VERSION', version);
+ const destPath = path.join(repoPath, dest);
+ console.log('... copying ' + src + ' to ' + dest);
+ fs.copyFileSync(srcPath, destPath, fs.constants.COPYFILE_EXCL);
+ toCommit.push(dest);
+ }
+
+ console.log('... creating commit');
+
+ const author = cp.execSync('git log -1 --format=%aN%n%aE',
+ { encoding: 'utf8', cwd: path.join(__dirname, '..') }
+ )
+ .trim()
+ .split('\n');
+
+ cp.execFileSync('git', ['add', ...toCommit],
+ { cwd: repoPath }
+ );
+
+ cp.execFileSync('git',
+ ['commit', '-m', cdnCommitMessage.replace('@VERSION', version)],
+ {
+ cwd: repoPath,
+ env: {
+ GIT_AUTHOR_NAME: author[0],
+ GIT_AUTHOR_EMAIL: author[1],
+ GIT_COMMITTER_NAME: author[0],
+ GIT_COMMITTER_EMAIL: author[1]
+ }
+ }
+ );
+
+ // prepre for user to push from the host shell after review
+ cp.execFileSync('git', ['remote', 'set-url', 'origin', cdnRemotes.auth],
+ { cwd: repoPath }
+ );
+ }
+};
+
+const [version, remote] = process.argv.slice(2);
+
+try {
+ Repo.buildFiles(version);
+ Repo.cdnCommit(version, remote || 'real');
+} catch (e) {
+ if (e.stderr) {
+ e.stdout = e.stdout.toString();
+ e.stderr = e.stderr.toString();
+ }
+ console.error(e);
+ process.exit(1);
+}
diff --git a/build/prep-release.js b/build/prep-release.js
index 29369871d..7fb40216e 100644
--- a/build/prep-release.js
+++ b/build/prep-release.js
@@ -147,6 +147,6 @@ const version = process.argv[2];
(async function main () {
await Repo.prep(version);
}()).catch(e => {
- console.error(e.toString());
+ console.error(e);
process.exit(1);
});
diff --git a/build/set-release.js b/build/set-release.js
deleted file mode 100644
index f89df3ad4..000000000
--- a/build/set-release.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// Helper to set the QUnit release version in various places.
-//
-// See also RELEASE.md.
-//
-// Inspired by .
-
-const fs = require('fs');
-const path = require('path');
-
-const Repo = {
- setFiles (version) {
- if (typeof version !== 'string' || !/^\d+\.\d+\.\d+$/.test(version)) {
- throw new Error('Invalid or missing version argument');
- }
- {
- const file = 'bower.json';
- console.log(`Updating ${file}...`);
- const filePath = path.join(__dirname, '..', file);
- const json = fs.readFileSync(filePath, 'utf8');
- const packageIndentation = json.match(/\n([\t\s]+)/)[1];
- const data = JSON.parse(json);
-
- data.version = version;
-
- fs.writeFileSync(
- filePath,
- JSON.stringify(data, null, packageIndentation) + '\n'
- );
- }
- {
- const file = 'package.json';
- console.log(`Updating ${file}...`);
- const filePath = path.join(__dirname, '..', file);
- const json = fs.readFileSync(filePath, 'utf8');
- const packageIndentation = json.match(/\n([\t\s]+)/)[1];
- const data = JSON.parse(json);
-
- data.version = version;
- data.author.url = data.author.url.replace('main', version);
-
- fs.writeFileSync(
- filePath,
- JSON.stringify(data, null, packageIndentation) + '\n'
- );
- }
- }
-};
-
-const version = process.argv[2];
-
-try {
- Repo.setFiles(version);
-} catch (e) {
- console.error(e.toString());
- process.exit(1);
-}
diff --git a/build/utils.js b/build/utils.js
index f3e4cd4ef..9dc4ce1e5 100644
--- a/build/utils.js
+++ b/build/utils.js
@@ -54,7 +54,7 @@ async function downloadFile (url, dest) {
function cleanDir (dirPath) {
if (fs.existsSync(dirPath)) {
- fs.rmdirSync(dirPath, { recursive: true });
+ fs.rmSync(dirPath, { force: true, recursive: true });
}
fs.mkdirSync(dirPath, { recursive: true });
}