mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
feat: derive mobile build number from semver (#30569)
This commit is contained in:
parent
9273a4ca2a
commit
cf17dcbfee
5 changed files with 104 additions and 41 deletions
|
|
@ -23,32 +23,25 @@ export const cli = (argv: string[]) => {
|
|||
.choices(RELEASE_TYPES)
|
||||
.makeOptionMandatory(),
|
||||
)
|
||||
.addOption(
|
||||
new Option('-m, --mobile <value>', 'pump mobile build number')
|
||||
.choices(['true', 'false'])
|
||||
.default('false'),
|
||||
)
|
||||
.action(
|
||||
({ type, mobile }: { type: ReleaseType; mobile: 'true' | 'false' }) => {
|
||||
try {
|
||||
console.log(handleRelease({ type, mobile: mobile === 'true' }));
|
||||
} catch (error) {
|
||||
if (error instanceof ReleaseInputError) {
|
||||
console.log(program.usage());
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (error instanceof ReleaseError) {
|
||||
console.log(
|
||||
`Invalid pump: ${type}. Pumping from ${error.version} to ${error.newVersion} is not allowed.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
throw error;
|
||||
.action(({ type }: { type: ReleaseType }) => {
|
||||
try {
|
||||
console.log(handleRelease({ type }));
|
||||
} catch (error) {
|
||||
if (error instanceof ReleaseInputError) {
|
||||
console.log(program.usage());
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error instanceof ReleaseError) {
|
||||
console.log(
|
||||
`Invalid pump: ${type}. Pumping from ${error.version} to ${error.newVersion} is not allowed.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return program.parse(argv);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
import semver from 'semver';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
resolveArchivedVersions,
|
||||
ArchivedVersion,
|
||||
getMobileBuild,
|
||||
getNewVersion,
|
||||
ReleaseError,
|
||||
} from './release';
|
||||
|
||||
const mobileBuild = (version: string) =>
|
||||
getMobileBuild(semver.parse(version) as semver.SemVer);
|
||||
|
||||
const archived = (label: string): ArchivedVersion => ({
|
||||
label,
|
||||
url: `https://docs.${label}.archive.immich.app`,
|
||||
|
|
@ -137,3 +142,56 @@ describe(resolveArchivedVersions.name, () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(getMobileBuild.name, () => {
|
||||
it('should encode the version', () => {
|
||||
expect(mobileBuild('3.1.0')).toBe(3_010_099);
|
||||
expect(mobileBuild('3.1.4')).toBe(3_010_499);
|
||||
expect(mobileBuild('3.2.0-rc.0')).toBe(3_020_000);
|
||||
expect(mobileBuild('3.2.0-rc.3')).toBe(3_020_003);
|
||||
});
|
||||
|
||||
it('should be above the last hand-maintained build number', () => {
|
||||
expect(mobileBuild('3.1.0')).toBeGreaterThan(3057);
|
||||
});
|
||||
|
||||
it('should stay under the play store limit', () => {
|
||||
expect(mobileBuild('99.99.99')).toBeLessThan(2_100_000_000);
|
||||
});
|
||||
|
||||
it('should increase across a release cycle', () => {
|
||||
const versions = [
|
||||
'3.1.0',
|
||||
'3.1.1',
|
||||
'3.2.0-rc.0',
|
||||
'3.2.0-rc.1',
|
||||
'3.2.0',
|
||||
'3.2.1',
|
||||
'3.3.0-rc.0',
|
||||
'4.0.0-rc.0',
|
||||
'4.0.0',
|
||||
];
|
||||
|
||||
const builds = versions.map((version) => mobileBuild(version));
|
||||
expect(builds).toEqual([...builds].sort((a, b) => a - b));
|
||||
expect(new Set(builds).size).toBe(builds.length);
|
||||
});
|
||||
|
||||
it('should rank a release above its own candidates', () => {
|
||||
expect(mobileBuild('3.2.0')).toBeGreaterThan(mobileBuild('3.2.0-rc.98'));
|
||||
});
|
||||
|
||||
it('should rank a patch on an older line below the newer line', () => {
|
||||
expect(mobileBuild('3.1.5')).toBeLessThan(mobileBuild('3.2.0'));
|
||||
});
|
||||
|
||||
it('should refuse versions it cannot encode', () => {
|
||||
expect(() => mobileBuild('100.0.0')).toThrow('Cannot derive');
|
||||
expect(() => mobileBuild('3.100.0')).toThrow('Cannot derive');
|
||||
expect(() => mobileBuild('3.1.100')).toThrow('Cannot derive');
|
||||
expect(() => mobileBuild('3.1.0-rc.99')).toThrow('Cannot derive');
|
||||
expect(() => mobileBuild('3.1.0-beta')).toThrow('Cannot derive');
|
||||
expect(() => mobileBuild('3.1.0-rc.-1')).toThrow('Cannot derive');
|
||||
expect(() => mobileBuild('3.1.0-rc.x')).toThrow('Cannot derive');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const Files = {
|
|||
},
|
||||
};
|
||||
|
||||
export const handleRelease = ({ type, mobile }: ReleaseOptions) => {
|
||||
export const handleRelease = ({ type }: ReleaseOptions) => {
|
||||
const versionRaw = getVersion();
|
||||
const newVersionRaw = getNewVersion(versionRaw, type);
|
||||
const newVersion = semver.parse(normalize(newVersionRaw));
|
||||
|
|
@ -43,8 +43,7 @@ export const handleRelease = ({ type, mobile }: ReleaseOptions) => {
|
|||
throw new ReleaseInputError();
|
||||
}
|
||||
const newVersionNoRc = `${newVersion.major}.${newVersion.minor}.${newVersion.patch}`;
|
||||
const mobileBuild = getMobileBuild();
|
||||
const newMobileBuild = mobile ? mobileBuild + 1 : mobileBuild;
|
||||
const newMobileBuild = getMobileBuild(newVersion);
|
||||
|
||||
// pump versions everywhere
|
||||
|
||||
|
|
@ -189,14 +188,32 @@ export const getNewVersion = (versionRaw: string, type: string) => {
|
|||
return newVersionRaw;
|
||||
};
|
||||
|
||||
const getMobileBuild = () => {
|
||||
const pubspec = new TextFile(Files.Mobile.Pubspec).read();
|
||||
const match = pubspec.match(/^version: .*\+(\d+)$/m);
|
||||
if (!match) {
|
||||
throw new Error('Could not find mobile build number in pubspec.yaml');
|
||||
const RADIX = 100;
|
||||
const STABLE = RADIX - 1;
|
||||
|
||||
export const getMobileBuild = (version: SemVer) => {
|
||||
const { major, minor, patch, prerelease } = version;
|
||||
const candidate = prerelease[1];
|
||||
const digit = typeof candidate === 'number' ? candidate : STABLE;
|
||||
|
||||
const valid =
|
||||
major < RADIX &&
|
||||
minor < RADIX &&
|
||||
patch < RADIX &&
|
||||
(prerelease.length === 0 || digit < STABLE);
|
||||
|
||||
if (!valid) {
|
||||
throw new Error(
|
||||
`Cannot derive a mobile build number from ${version.format()}`,
|
||||
);
|
||||
}
|
||||
|
||||
return Number(match[1]);
|
||||
return (
|
||||
major * Math.pow(RADIX, 3) +
|
||||
minor * Math.pow(RADIX, 2) +
|
||||
patch * RADIX +
|
||||
digit
|
||||
);
|
||||
};
|
||||
|
||||
const pump = (path: string, pattern: RegExp, replacement: string) => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const RELEASE_TYPES = [
|
|||
] as const;
|
||||
|
||||
export type ReleaseType = (typeof RELEASE_TYPES)[number];
|
||||
export type ReleaseOptions = { type: string; mobile: boolean };
|
||||
export type ReleaseOptions = { type: string };
|
||||
|
||||
export class TextFile {
|
||||
constructor(private file: string) {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue