mirror of
https://github.com/immich-app/immich.git
synced 2026-06-12 11:01:45 -07:00
feat: add prerelease support to pump version (#28922)
refactor: pump script
This commit is contained in:
@@ -10,9 +10,13 @@ on:
|
|||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- 'false'
|
- 'false'
|
||||||
- major
|
|
||||||
- minor
|
- minor
|
||||||
- patch
|
- patch
|
||||||
|
- premajor
|
||||||
|
- preminor
|
||||||
|
- prepatch
|
||||||
|
- prerelease
|
||||||
|
- release
|
||||||
mobileBump:
|
mobileBump:
|
||||||
description: 'Bump mobile build number'
|
description: 'Bump mobile build number'
|
||||||
required: false
|
required: false
|
||||||
@@ -74,7 +78,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SERVER_BUMP: ${{ inputs.serverBump }}
|
SERVER_BUMP: ${{ inputs.serverBump }}
|
||||||
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
||||||
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
run: pnpm --silent release -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
||||||
|
|
||||||
- id: output
|
- id: output
|
||||||
run: echo "version=$IMMICH_VERSION" >> $GITHUB_OUTPUT
|
run: echo "version=$IMMICH_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
|
root:
|
||||||
|
- 'misc/**'
|
||||||
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'mise.toml'
|
||||||
i18n:
|
i18n:
|
||||||
- 'i18n/**'
|
- 'i18n/**'
|
||||||
- 'mise.toml'
|
- 'mise.toml'
|
||||||
@@ -62,6 +66,34 @@ jobs:
|
|||||||
- '.github/workflows/test.yml'
|
- '.github/workflows/test.yml'
|
||||||
force-events: 'workflow_dispatch'
|
force-events: 'workflow_dispatch'
|
||||||
|
|
||||||
|
root-unit-tests:
|
||||||
|
name: Test the root workspace
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).root == true }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- id: token
|
||||||
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
|
with:
|
||||||
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Setup Mise
|
||||||
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
|
with:
|
||||||
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: pnpm test
|
||||||
|
|
||||||
server-unit-tests:
|
server-unit-tests:
|
||||||
name: Test & Lint Server
|
name: Test & Lint Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
|
|||||||
Vendored
+1
@@ -60,6 +60,7 @@
|
|||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
||||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||||
|
"*.js": "${capture}.spec.js,${capture}.mock.js",
|
||||||
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
||||||
},
|
},
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#! /usr/bin/env node
|
#! /usr/bin/env node
|
||||||
const { readFileSync, writeFileSync } = require('node:fs');
|
import { readFileSync, writeFileSync } from 'node:fs';
|
||||||
|
|
||||||
const asVersion = (item) => {
|
const asVersion = (item) => {
|
||||||
const { label, url } = item;
|
const { label, url } = item;
|
||||||
const [major, minor, patch] = label.substring(1).split('.').map(Number);
|
const [version] = label.substring(1).split('-');
|
||||||
|
const [major, minor, patch] = version.split('.').map(Number);
|
||||||
return { major, minor, patch, label, url };
|
return { major, minor, patch, label, url };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ for (const item of versions) {
|
|||||||
) {
|
) {
|
||||||
versions = versions.filter((item) => item.label !== version.label);
|
versions = versions.filter((item) => item.label !== version.label);
|
||||||
console.log(
|
console.log(
|
||||||
`Removed ${version.label} (replaced with ${lastVersion.label})`
|
`Removed ${version.label} (replaced with ${lastVersion.label})`,
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -41,5 +42,5 @@ for (const item of versions) {
|
|||||||
|
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
filename,
|
filename,
|
||||||
JSON.stringify([newVersion, ...versions], null, 2) + '\n'
|
JSON.stringify([newVersion, ...versions], null, 2) + '\n',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
#
|
#
|
||||||
# Pump one or both of the server/mobile versions in appropriate files
|
# Pump one or both of the server/mobile versions in appropriate files
|
||||||
#
|
#
|
||||||
# usage: './scripts/pump-version.sh -s <major|minor|patch> <-m> <true|false>
|
# usage: './scripts/pump-version.sh -s <minor|patch|premajor|preminor|prepatch|prerelease> <-m> <true|false>
|
||||||
#
|
#
|
||||||
# examples:
|
# examples:
|
||||||
# ./scripts/pump-version.sh -s major # 1.0.0+50 => 2.0.0+50
|
# ./scripts/pump-version.sh -s major # 1.0.0+50 => 2.0.0+50
|
||||||
# ./scripts/pump-version.sh -s minor -m true # 1.0.0+50 => 1.1.0+51
|
# ./scripts/pump-version.sh -s minor -m true # 1.0.0+50 => 1.1.0+51
|
||||||
# ./scripts/pump-version.sh -m true # 1.0.0+50 => 1.0.0+51
|
# ./scripts/pump-version.sh -s premajor # 1.0.0+50 => 2.0.0-rc.0+50
|
||||||
|
# ./scripts/pump-version.sh -s prerelease # 2.0.0-rc.0+50 => 2.0.0-rc.1+50
|
||||||
|
# ./scripts/pump-version.sh -m true # 1.0.0+50 => 1.0.0+51
|
||||||
#
|
#
|
||||||
|
|
||||||
SERVER_PUMP="false"
|
SERVER_PUMP="false"
|
||||||
@@ -25,31 +27,15 @@ while getopts 's:m:' flag; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
CURRENT_SERVER=$(jq -r '.version' server/package.json)
|
CURRENT_SERVER=$(jq -r '.version' package.json)
|
||||||
MAJOR=$(echo "$CURRENT_SERVER" | cut -d '.' -f1)
|
if ! NEXT_SERVER=$(pnpm --silent pump "$CURRENT_SERVER" "$SERVER_PUMP"); then
|
||||||
MINOR=$(echo "$CURRENT_SERVER" | cut -d '.' -f2)
|
echo "Fatal: failed to pump server version: $NEXT_SERVER" >&2
|
||||||
PATCH=$(echo "$CURRENT_SERVER" | cut -d '.' -f3)
|
|
||||||
|
|
||||||
if [[ $SERVER_PUMP == "major" ]]; then
|
|
||||||
MAJOR=$((MAJOR + 1))
|
|
||||||
MINOR=0
|
|
||||||
PATCH=0
|
|
||||||
elif [[ $SERVER_PUMP == "minor" ]]; then
|
|
||||||
MINOR=$((MINOR + 1))
|
|
||||||
PATCH=0
|
|
||||||
elif [[ $SERVER_PUMP == "patch" ]]; then
|
|
||||||
PATCH=$((PATCH + 1))
|
|
||||||
elif [[ $SERVER_PUMP == "false" ]]; then
|
|
||||||
echo 'Skipping Server Pump'
|
|
||||||
else
|
|
||||||
echo 'Expected <major|minor|patch|false> for the server argument'
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
NEXT_SERVER=$MAJOR.$MINOR.$PATCH
|
|
||||||
|
|
||||||
CURRENT_MOBILE=$(grep "^version: .*+[0-9]\+$" mobile/pubspec.yaml | cut -d "+" -f2)
|
CURRENT_MOBILE=$(grep "^version: .*+[0-9]\+$" mobile/pubspec.yaml | cut -d "+" -f2)
|
||||||
NEXT_MOBILE=$CURRENT_MOBILE
|
NEXT_MOBILE=$CURRENT_MOBILE
|
||||||
|
|
||||||
if [[ $MOBILE_PUMP == "true" ]]; then
|
if [[ $MOBILE_PUMP == "true" ]]; then
|
||||||
set $((NEXT_MOBILE++))
|
set $((NEXT_MOBILE++))
|
||||||
elif [[ $MOBILE_PUMP == "false" ]]; then
|
elif [[ $MOBILE_PUMP == "false" ]]; then
|
||||||
@@ -59,15 +45,17 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
||||||
echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER"
|
echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER"
|
||||||
|
|
||||||
pnpm version "$NEXT_SERVER" --no-git-tag-version
|
pnpm version "$NEXT_SERVER" --no-git-tag-version --no-git-checks
|
||||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix server
|
pnpm version "$NEXT_SERVER" --no-git-tag-version --no-git-checks --prefix server
|
||||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix packages/cli
|
pnpm version "$NEXT_SERVER" --no-git-tag-version --no-git-checks --prefix packages/cli
|
||||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix web
|
pnpm version "$NEXT_SERVER" --no-git-tag-version --no-git-checks --prefix web
|
||||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix e2e
|
pnpm version "$NEXT_SERVER" --no-git-tag-version --no-git-checks --prefix e2e
|
||||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix packages/sdk
|
pnpm version "$NEXT_SERVER" --no-git-tag-version --no-git-checks --prefix packages/sdk
|
||||||
|
|
||||||
# copy version to open-api spec
|
# copy version to open-api spec
|
||||||
mise run //:open-api
|
mise run //:open-api
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { pump } from './pump.js';
|
||||||
|
|
||||||
|
const [versionRaw, type] = process.argv.slice(2);
|
||||||
|
const { message, exitCode } = pump(versionRaw, type);
|
||||||
|
|
||||||
|
console.log(message);
|
||||||
|
process.exit(exitCode);
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import semver, { SemVer } from 'semver';
|
||||||
|
|
||||||
|
const printUsage = () => {
|
||||||
|
return {
|
||||||
|
message:
|
||||||
|
'Usage: ./pump_cli.js <semver> <minor|patch|premajor|preminor|prepatch|prerelease|release>',
|
||||||
|
exitCode: 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPrerelease = (version) => version.prerelease.length > 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SemVer} version
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const inc = (version, type) => `v${semver.inc(version, type, {}, 'rc')}`;
|
||||||
|
|
||||||
|
/** @param {string} version */
|
||||||
|
const normalize = (version) => {
|
||||||
|
if (version.startsWith('v')) {
|
||||||
|
version = version.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} versionRaw
|
||||||
|
* @param {string} type
|
||||||
|
*/
|
||||||
|
export const pump = (versionRaw, type) => {
|
||||||
|
if (!versionRaw) {
|
||||||
|
return printUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
versionRaw = normalize(versionRaw);
|
||||||
|
|
||||||
|
const version = semver.parse(versionRaw);
|
||||||
|
if (!version) {
|
||||||
|
return printUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
let newVersionRaw;
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'patch':
|
||||||
|
case 'prepatch':
|
||||||
|
case 'minor':
|
||||||
|
case 'preminor':
|
||||||
|
case 'premajor': {
|
||||||
|
newVersionRaw = inc(version, type);
|
||||||
|
// can only use while not in a prerelease
|
||||||
|
valid = !isPrerelease(version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'prerelease': {
|
||||||
|
newVersionRaw = inc(version, type);
|
||||||
|
// can only use while in a prerelease
|
||||||
|
valid = isPrerelease(version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'release': {
|
||||||
|
// drop prerelease part
|
||||||
|
newVersionRaw = `${version.major}.${version.minor}.${version.patch}`;
|
||||||
|
// can only use to promote a prerelease to a release (no version change)
|
||||||
|
valid = isPrerelease(version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return printUsage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newVersionRaw) {
|
||||||
|
return printUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
newVersionRaw = normalize(newVersionRaw);
|
||||||
|
|
||||||
|
const newVersion = semver.parse(newVersionRaw);
|
||||||
|
if (!newVersion) {
|
||||||
|
return printUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
const invalidUpgrade =
|
||||||
|
isPrerelease(version) &&
|
||||||
|
!isPrerelease(newVersion) &&
|
||||||
|
(version.major !== newVersion.major ||
|
||||||
|
version.minor !== newVersion.minor ||
|
||||||
|
version.patch !== newVersion.patch);
|
||||||
|
|
||||||
|
if (!valid || invalidUpgrade) {
|
||||||
|
return {
|
||||||
|
message: `Invalid pump: ${type}. Pumping from ${versionRaw} to ${newVersionRaw} is not allowed.`,
|
||||||
|
exitCode: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { message: newVersionRaw, exitCode: 0 };
|
||||||
|
};
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { pump } from './pump';
|
||||||
|
|
||||||
|
describe(pump.name, () => {
|
||||||
|
describe('usage', () => {
|
||||||
|
it.each([
|
||||||
|
[],
|
||||||
|
['2.7.5'],
|
||||||
|
['2.7.5', 'invalid'],
|
||||||
|
['invalid', 'patch'],
|
||||||
|
['2.7.5', 'major'],
|
||||||
|
])('should not accept $0, $1 as inputs', (version, type) => {
|
||||||
|
expect(pump(version, type)).toEqual({
|
||||||
|
message: expect.stringContaining('Usage: '),
|
||||||
|
exitCode: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transitions', () => {
|
||||||
|
const valid = [
|
||||||
|
{
|
||||||
|
name: 'patch',
|
||||||
|
items: [['patch', '2.7.5', '2.7.6']],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'prepatch',
|
||||||
|
items: [
|
||||||
|
['prepatch', '2.7.5', '2.7.6-rc.0'],
|
||||||
|
['prerelease', '2.7.6-rc.0', '2.7.6-rc.1'],
|
||||||
|
['release', '2.7.6-rc.1', '2.7.6'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'minor',
|
||||||
|
items: [['minor', '2.7.5', '2.8.0']],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'preminor',
|
||||||
|
items: [
|
||||||
|
['preminor', '2.7.5', '2.8.0-rc.0'],
|
||||||
|
['prerelease', '2.8.0-rc.0', '2.8.0-rc.1'],
|
||||||
|
['release', '2.8.0-rc.1', '2.8.0'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'premajor',
|
||||||
|
items: [
|
||||||
|
['premajor', '2.7.5', '3.0.0-rc.0'],
|
||||||
|
['prerelease', '3.0.0-rc.0', '3.0.0-rc.1'],
|
||||||
|
['release', '3.0.0-rc.1', '3.0.0'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const group of valid) {
|
||||||
|
describe(group.name, () => {
|
||||||
|
it.each(group.items)(
|
||||||
|
'should allow a $0 from $1 to $2',
|
||||||
|
(type, version, next) => {
|
||||||
|
expect(pump(version, type)).toEqual({
|
||||||
|
message: next,
|
||||||
|
exitCode: 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('invalid', () => {
|
||||||
|
it.each([
|
||||||
|
['patch', 'v3.0.0-rc.0'],
|
||||||
|
['prepatch', 'v3.0.0-rc.0'],
|
||||||
|
['minor', 'v3.0.0-rc.0'],
|
||||||
|
['preminor', 'v3.0.0-rc.0'],
|
||||||
|
['premajor', 'v3.0.0-rc.0'],
|
||||||
|
['prerelease', 'v3.0.0'],
|
||||||
|
['release', 'v3.0.0'],
|
||||||
|
])('should not allow a $0 on $1', (type, version) => {
|
||||||
|
expect(pump(version, type)).toEqual({
|
||||||
|
message: expect.stringContaining('Invalid pump'),
|
||||||
|
exitCode: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+9
-2
@@ -2,17 +2,24 @@
|
|||||||
"name": "immich-monorepo",
|
"name": "immich-monorepo",
|
||||||
"version": "2.7.5",
|
"version": "2.7.5",
|
||||||
"description": "Monorepo for Immich",
|
"description": "Monorepo for Immich",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --cache --check i18n/",
|
"format": "prettier --cache --check i18n/",
|
||||||
"format:fix": "prettier --cache --write --list-different i18n"
|
"format:fix": "prettier --cache --write --list-different i18n",
|
||||||
|
"test": "vitest",
|
||||||
|
"release": "./misc/release/pump-version.sh",
|
||||||
|
"pump": "node ./misc/release/pump-wrapper.js"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@11.4.0",
|
"packageManager": "pnpm@11.4.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"pnpm": ">=10.0.0"
|
"pnpm": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.12.4",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"prettier-plugin-sort-json": "^4.2.0"
|
"prettier-plugin-sort-json": "^4.2.0",
|
||||||
|
"semver": "^7.8.1",
|
||||||
|
"vitest": "^4.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+230
-218
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
include: ['misc/**/*.spec.js'],
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user