Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ on:
workflow_dispatch:
inputs:
increment:
description: "Version increment. Leave empty to infer it from the conventional commits since the last tag."
description: "Version increment. Choose auto to infer it from the conventional commits since the last tag."
required: false
default: ""
default: "auto"
type: choice
options:
- ""
- "auto"
- patch
- minor
- major
Expand Down Expand Up @@ -57,30 +57,55 @@ jobs:

- run: pnpm run build

- run: pnpm run security:check

# Version planning must run before either registry or GitHub auth checks.
- name: Plan release
id: plan
env:
INCREMENT: ${{ inputs.increment }}
run: |
args=(--ci --release-version --npm.publish=false --github.release=false)
if [ -n "$INCREMENT" ] && [ "$INCREMENT" != "auto" ]; then
args+=("--increment=$INCREMENT")
fi
version=$(pnpm exec release-it "${args[@]}")
echo "version=$version" >> "$GITHUB_OUTPUT"
if [ -z "$version" ]; then
echo "::notice::No new version to release. Nothing was published or tagged."
else
echo "::notice::Planned release v$version."
fi

- name: Configure release identity
if: ${{ steps.plan.outputs.version != '' }}
run: |
git config user.email "gabrielstaveira@gmail.com"
git config user.name "Gabriel Taveira"

- name: Preview release
if: ${{ inputs.dry-run && steps.plan.outputs.version != '' }}
run: pnpm exec release-it --ci --dry-run --increment=${{ steps.plan.outputs.version }} --npm.publish=false --github.release=false

# Publishes to npm, pushes the annotated tag and creates the GitHub
# release. The version bump commit stays local, see .release-it.mjs.
- name: Release
id: release
if: ${{ !inputs.dry-run && steps.plan.outputs.version != '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
INCREMENT: ${{ inputs.increment }}
DRY_RUN: ${{ inputs.dry-run }}
VERSION: ${{ steps.plan.outputs.version }}
run: |
git config user.email "gabrielstaveira@gmail.com"
git config user.name "Gabriel Taveira"
npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"

args="--ci"
if [ -n "$INCREMENT" ]; then
args="$args --increment=$INCREMENT"
fi
if [ "$DRY_RUN" = "true" ]; then
args="$args --dry-run"
if [ -z "$NPM_TOKEN" ]; then
echo "::error::v$VERSION is due, but NPM_TOKEN is missing. Configure npm publishing credentials before retrying. Nothing was published or tagged."
exit 1
fi
npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"
trap 'npm config delete //registry.npmjs.org/:_authToken' EXIT

before=$(node -p "require('./package.json').version")
pnpm run release $args
pnpm run release --ci --increment="$VERSION"
after=$(node -p "require('./package.json').version")

echo "version=$after" >> "$GITHUB_OUTPUT"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"format": "oxfmt --check .",
"format:fix": "oxfmt .",
"typecheck": "tsc --noEmit",
"test": "node tools/dependency-security-check.mjs && jest",
"test": "node tools/dependency-security-check.mjs && node tools/release-check.mjs && jest",
"prepare": "pnpm run build",
"release": "release-it",
"security:check": "node tools/dependency-security-check.mjs && pnpm audit --audit-level high"
Expand Down
101 changes: 101 additions & 0 deletions tools/release-check.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import {
cpSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { env as processEnv } from "node:process";

const root = join(import.meta.dirname, "..");
const cli = join(root, "node_modules/release-it/bin/release-it.js");
const cases = [
["chore: maintain tooling", "", ""],
["build(deps): patch tooling", "", "2.0.2"],
["fix: correct plugin", "", "2.0.2"],
["feat: add option", "", "2.1.0"],
[
"ci!: change contract\n\nBREAKING CHANGE: migrate the contract",
"",
"3.0.0",
],
["chore: maintain tooling", "patch", "2.0.2"],
];
for (const [message, increment, expected] of cases) {
const dir = mkdtempSync(join(tmpdir(), "code-push-release-check-"));
const git = (...args) =>
execFileSync("git", args, { cwd: dir, encoding: "utf8" }).trim();
try {
cpSync(join(root, ".release-it.mjs"), join(dir, ".release-it.mjs"));
cpSync(join(root, "tools"), join(dir, "tools"), { recursive: true });
symlinkSync(join(root, "node_modules"), join(dir, "node_modules"), "dir");
writeFileSync(join(dir, ".gitignore"), "node_modules\n");
writeFileSync(
join(dir, "package.json"),
'{"name":"react-native-code-push-plugin","version":"2.0.1"}\n',
);
git("init", "--quiet", "--initial-branch", "master");
git("config", "user.name", "Gabriel Taveira");
git("config", "user.email", "gabrielstaveira@gmail.com");
git("add", ".");
git("commit", "--quiet", "-m", "chore: initial");
git("tag", "-a", "v2.0.1", "-m", "v2.0.1");
git("commit", "--quiet", "--allow-empty", "-m", message);
const before = git("rev-parse", "HEAD");
assert.equal(
git("log", "-1", "--format=%an <%ae>|%cn <%ce>"),
"Gabriel Taveira <gabrielstaveira@gmail.com>|Gabriel Taveira <gabrielstaveira@gmail.com>",
);
const env = {
...processEnv,
NPM_TOKEN: "",
NODE_AUTH_TOKEN: "",
GITHUB_TOKEN: "",
npm_config_registry: "http://127.0.0.1:1",
};
const args = [
cli,
"--ci",
"--release-version",
"--npm.publish=false",
"--github.release=false",
];
if (increment) args.push(`--increment=${increment}`);
const actual = execFileSync(process.execPath, args, {
cwd: dir,
env,
encoding: "utf8",
timeout: 30_000,
}).trim();
assert.equal(actual, expected, message);
if (actual) {
execFileSync(
process.execPath,
[
cli,
"--ci",
"--dry-run",
`--increment=${actual}`,
"--npm.publish=false",
"--github.release=false",
],
{ cwd: dir, env, stdio: "pipe", timeout: 30_000 },
);
}
assert.equal(git("rev-parse", "HEAD"), before);
assert.equal(git("status", "--porcelain"), "");
assert.equal(git("tag"), "v2.0.1");
const manifest = JSON.parse(readFileSync(join(dir, "package.json")));
assert.equal(manifest.version, "2.0.1");
console.log(
`Release plan passed: ${message.split("\n")[0]} -> ${actual || "skip"}`,
);
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
Loading