Skip to content

Commit 70d2c38

Browse files
committed
improve beta release ci
1 parent abe4267 commit 70d2c38

File tree

2 files changed

+164
-36
lines changed

2 files changed

+164
-36
lines changed
Lines changed: 118 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,132 @@
1+
/* eslint-disable no-console */
12
const { execSync } = require('node:child_process');
23
const fs = require('node:fs');
34
const path = require('node:path');
45

56
const PKG_JSON_PATH = path.join(__dirname, '..', '..', 'package.json');
67

7-
const pkgJson = require(PKG_JSON_PATH); // eslint-disable-line import/no-dynamic-require
8-
9-
const PACKAGE_NAME = pkgJson.name;
10-
const VERSION = pkgJson.version;
11-
console.log(`before-deploy: Current version is ${VERSION}`); // eslint-disable-line no-console
8+
function execCommand(command, options = {}) {
9+
try {
10+
return execSync(command, { encoding: 'utf8', ...options }).trim();
11+
} catch (error) {
12+
console.error(`Command failed: ${command}`);
13+
console.error(error.message);
14+
process.exit(1);
15+
return null; // Ensure a return value for all code paths
16+
}
17+
}
1218

13-
const nextVersion = getNextVersion(VERSION);
14-
console.log(`before-deploy: Setting version to ${nextVersion}`); // eslint-disable-line no-console
15-
pkgJson.version = nextVersion;
19+
function getPackageInfo() {
20+
const pkgJson = JSON.parse(fs.readFileSync(PKG_JSON_PATH, 'utf8'));
21+
return {
22+
name: pkgJson.name,
23+
version: pkgJson.version,
24+
pkgJson,
25+
};
26+
}
1627

17-
fs.writeFileSync(PKG_JSON_PATH, `${JSON.stringify(pkgJson, null, 2)}\n`);
28+
function getBaseVersionFromGit() {
29+
try {
30+
// Get the base version from the latest commit that updated package.json
31+
// This ensures we use the version that was set by the release_metadata step
32+
const gitShow = execCommand('git show HEAD:package.json');
33+
const gitPackageJson = JSON.parse(gitShow);
34+
return gitPackageJson.version;
35+
} catch (error) {
36+
console.error('Could not get base version from git');
37+
throw error;
38+
}
39+
}
1840

19-
function getNextVersion(version) {
20-
const versionString = execSync(`npm show ${PACKAGE_NAME} versions --json`, { encoding: 'utf8' });
21-
const versions = JSON.parse(versionString);
41+
function getNextBetaVersion(packageName, baseVersion) {
42+
console.log(`Calculating next beta version for base: ${baseVersion}`);
2243

23-
if (versions.some((v) => v === VERSION)) {
24-
console.error(`before-deploy: A release with version ${VERSION} already exists. Please increment version accordingly.`); // eslint-disable-line no-console
44+
// Validate base version format
45+
if (!/^\d+\.\d+\.\d+$/.test(baseVersion)) {
46+
console.error(`Invalid base version format: ${baseVersion}`);
2547
process.exit(1);
2648
}
2749

28-
const prereleaseNumbers = versions
29-
.filter((v) => (v.startsWith(VERSION) && v.includes('-')))
30-
.map((v) => Number(v.match(/\.(\d+)$/)[1]));
31-
const lastPrereleaseNumber = Math.max(-1, ...prereleaseNumbers);
32-
return `${version}-beta.${lastPrereleaseNumber + 1}`;
50+
let npmBetaNumber = 0;
51+
let gitBetaNumber = 0;
52+
53+
// Check NPM for existing beta versions
54+
try {
55+
const versionString = execCommand(`npm show ${packageName} versions --json`);
56+
const versions = JSON.parse(versionString);
57+
58+
// Check if base version already exists as stable
59+
if (versions.includes(baseVersion)) {
60+
console.error(`Base version ${baseVersion} already exists as stable on NPM!`);
61+
process.exit(1);
62+
}
63+
64+
const versionPrefix = `${baseVersion}-beta.`;
65+
const npmBetas = versions
66+
.filter((v) => v.startsWith(versionPrefix))
67+
.map((v) => {
68+
const match = v.match(/^.+-beta\.(\d+)$/);
69+
return match ? parseInt(match[1], 10) : 0;
70+
});
71+
72+
npmBetaNumber = npmBetas.length > 0 ? Math.max(...npmBetas) : 0;
73+
console.log(`Latest beta on NPM: ${npmBetaNumber}`);
74+
} catch {
75+
console.log('No existing versions found on NPM');
76+
}
77+
78+
// Check Git tags for existing beta versions
79+
try {
80+
const tagPattern = `v${baseVersion}-beta.*`;
81+
const tags = execCommand(`git tag -l "${tagPattern}" --sort=-version:refname`);
82+
83+
if (tags) {
84+
const tagList = tags.split('\n').filter((tag) => tag.trim());
85+
if (tagList.length > 0) {
86+
const latestTag = tagList[0];
87+
const match = latestTag.match(/v\d+\.\d+\.\d+-beta\.(\d+)$/);
88+
if (match) {
89+
gitBetaNumber = parseInt(match[1], 10);
90+
console.log(`Latest beta in Git: ${gitBetaNumber}`);
91+
}
92+
}
93+
}
94+
} catch {
95+
console.log('No existing beta tags found in Git');
96+
}
97+
98+
// Use the higher number to avoid conflicts
99+
const nextBetaNumber = Math.max(npmBetaNumber, gitBetaNumber) + 1;
100+
const nextVersion = `${baseVersion}-beta.${nextBetaNumber}`;
101+
102+
console.log(`Next beta version: ${nextVersion}`);
103+
return nextVersion;
33104
}
105+
106+
function updatePackageVersion(newVersion) {
107+
const { pkgJson } = getPackageInfo();
108+
pkgJson.version = newVersion;
109+
fs.writeFileSync(PKG_JSON_PATH, `${JSON.stringify(pkgJson, null, 2)}\n`);
110+
console.log(`Updated package.json to ${newVersion}`);
111+
}
112+
113+
function main() {
114+
console.log('🚀 Starting beta version calculation...');
115+
116+
const { name: packageName } = getPackageInfo();
117+
118+
// Get the base version from Git (what was committed by release_metadata)
119+
const baseVersion = getBaseVersionFromGit();
120+
console.log(`Base version from Git: ${baseVersion}`);
121+
122+
// Calculate next beta version
123+
const nextBetaVersion = getNextBetaVersion(packageName, baseVersion);
124+
125+
// Update package.json with the beta version
126+
updatePackageVersion(nextBetaVersion);
127+
128+
console.log('✅ Beta version preparation completed!');
129+
console.log(`Package will be published as: ${nextBetaVersion}`);
130+
}
131+
132+
main();

.github/workflows/pre_release.yaml

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
name: Create a pre-release
22

33
on:
4-
workflow_dispatch:
5-
# Push to master will deploy a beta version
6-
push:
7-
branches:
8-
- master
9-
tags-ignore:
10-
- "**" # Ignore all tags to prevent duplicate builds when tags are pushed.
4+
# Only trigger on PRs with "beta" label
5+
pull_request:
6+
types: [labeled, synchronize, reopened]
117

128
concurrency:
139
group: release
1410
cancel-in-progress: false
1511

1612
jobs:
1713
release_metadata:
18-
if: "!startsWith(github.event.head_commit.message, 'docs') && !startsWith(github.event.head_commit.message, 'ci') && startsWith(github.repository, 'apify/')"
14+
# Run ONLY when PR has the "beta" label
15+
if: contains(github.event.pull_request.labels.*.name, 'beta')
1916
name: Prepare release metadata
2017
runs-on: ubuntu-latest
2118
outputs:
@@ -40,12 +37,13 @@ jobs:
4037
check-regexp: (Code checks)
4138
wait-interval: 5
4239

43-
update_changelog:
40+
update_changelog_and_version:
4441
needs: [ release_metadata, wait_for_checks ]
45-
name: Update changelog
42+
name: Update changelog and prepare beta version
4643
runs-on: ubuntu-latest
4744
outputs:
4845
changelog_commitish: ${{ steps.commit.outputs.commit_long_sha || github.sha }}
46+
beta_version: ${{ steps.beta_version.outputs.version }}
4947

5048
steps:
5149
- name: Checkout repository
@@ -57,10 +55,25 @@ jobs:
5755
uses: actions/setup-node@v4
5856
with:
5957
node-version: 22
58+
cache: 'npm'
59+
cache-dependency-path: 'package-lock.json'
6060

61-
- name: Update package version in package.json
61+
- name: Install dependencies
62+
run: npm ci
63+
64+
- name: Update package version to base version
6265
run: npm version --no-git-tag-version --allow-same-version ${{ needs.release_metadata.outputs.version_number }}
6366

67+
- name: Calculate and set beta version
68+
id: beta_version
69+
run: |
70+
# Use the improved beta script
71+
node ./.github/scripts/before-beta-release.cjs
72+
# Output the version for later jobs
73+
BETA_VERSION=$(node -p "require('./package.json').version")
74+
echo "version=$BETA_VERSION" >> $GITHUB_OUTPUT
75+
echo "Beta version: $BETA_VERSION"
76+
6477
- name: Update CHANGELOG.md
6578
uses: DamianReeves/write-file-action@master
6679
with:
@@ -74,32 +87,48 @@ jobs:
7487
with:
7588
author_name: Apify Release Bot
7689
author_email: [email protected]
77-
message: "chore(release): Update changelog and package version [skip ci]"
90+
message: "chore(release): Update to ${{ steps.beta_version.outputs.version }} [skip ci]"
91+
92+
- name: Create and push beta tag
93+
run: |
94+
git tag "v${{ steps.beta_version.outputs.version }}"
95+
git push origin "v${{ steps.beta_version.outputs.version }}"
7896
7997
publish_to_npm:
8098
name: Publish to NPM
81-
needs: [ release_metadata, wait_for_checks ]
99+
needs: [ update_changelog_and_version ]
82100
runs-on: ubuntu-latest
83101
steps:
84102
- uses: actions/checkout@v4
85103
with:
86-
ref: ${{ needs.update_changelog.changelog_commitish }}
104+
ref: ${{ needs.update_changelog_and_version.outputs.changelog_commitish }}
105+
87106
- name: Use Node.js 22
88107
uses: actions/setup-node@v4
89108
with:
90109
node-version: 22
91110
cache: 'npm'
92111
cache-dependency-path: 'package-lock.json'
112+
93113
- name: Install dependencies
94114
run: |
95115
echo "access=public" >> .npmrc
96116
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
97117
npm ci
98-
- # Check version consistency and increment pre-release version number for beta only.
99-
name: Bump pre-release version
100-
run: node ./.github/scripts/before-beta-release.cjs
118+
119+
- name: Verify version consistency
120+
run: |
121+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
122+
EXPECTED_VERSION="${{ needs.update_changelog_and_version.outputs.beta_version }}"
123+
if [ "$PACKAGE_VERSION" != "$EXPECTED_VERSION" ]; then
124+
echo "Version mismatch! Package: $PACKAGE_VERSION, Expected: $EXPECTED_VERSION"
125+
exit 1
126+
fi
127+
echo "Version verified: $PACKAGE_VERSION"
128+
101129
- name: Build module
102130
run: npm run build
131+
103132
- name: Publish to NPM
104133
run: npm publish --tag beta
105134

0 commit comments

Comments
 (0)