-
- Notifications
You must be signed in to change notification settings - Fork 11.4k
🎨 Added separate Server and Admin build versions to About dialog #26769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,54 @@ | ||
| // @ts-expect-error - semver subpath has no types | ||
| import semverParse from 'semver/functions/parse'; | ||
| | ||
| // This function needs to support: | ||
| // - 5.94.1+moya | ||
| // - 5.94.1-0-g1f3e72eac8+moya | ||
| // - 5.95.0-pre-g028c1a6+moya | ||
| // Supported version formats: | ||
| // - 6.21.2 → release tag | ||
| // - 6.21.2+1710072000.abc1234 → commit (server: semver+epoch.sha) | ||
| // - 6.21.2+abc1234 → commit (admin: semver+sha) | ||
| // - 5.94.1+moya → release tag (legacy) | ||
| // - 5.94.1-0-gabcdef+moya → commit (legacy git describe) | ||
| // - 5.95.0-pre-gabcdef+moya → commit (legacy canary) | ||
| export function linkToGitHubReleases(version: string): string { | ||
| if (!version) { | ||
| return ''; | ||
| } | ||
| | ||
| const cleanedVersion = version.replace('+moya', ''); | ||
| // Extract build metadata (everything after +) before semver parsing strips it | ||
| const plusIndex = version.indexOf('+'); | ||
| const buildMetadata = plusIndex !== -1 ? version.slice(plusIndex + 1) : ''; | ||
| const versionWithoutBuild = plusIndex !== -1 ? version.slice(0, plusIndex) : version; | ||
| | ||
| // Check build metadata for a commit SHA | ||
| if (buildMetadata && buildMetadata !== 'moya') { | ||
| // New format: "epoch.sha" or just "sha" | ||
| const parts = buildMetadata.split('.'); | ||
| const sha = parts[parts.length - 1]; | ||
| | ||
| if (sha && /^[0-9a-f]{7,40}$/.test(sha)) { | ||
| return `https://github.com/TryGhost/Ghost/commit/${sha}`; | ||
| } | ||
| } | ||
| | ||
| // Check pre-release segment for a commit SHA (legacy format: -0-gabcdef or -pre-gabcdef) | ||
| try { | ||
| const semverVersion = semverParse(cleanedVersion, {includePrerelease: true} as any); | ||
| const semverVersion = semverParse(versionWithoutBuild, {includePrerelease: true} as any); | ||
| const prerelease = semverVersion?.prerelease; | ||
| | ||
| if (prerelease && prerelease?.length > 0) { | ||
| if (prerelease && prerelease.length > 0) { | ||
| const splitPrerelease = String(prerelease[0]).split('-'); | ||
| const commitHash = splitPrerelease[1]; | ||
| | ||
| if (!commitHash || !commitHash.startsWith('g')) { | ||
| return ''; | ||
| if (commitHash?.startsWith('g')) { | ||
| return `https://github.com/TryGhost/Ghost/commit/${commitHash.slice(1)}`; | ||
| } | ||
| | ||
| const commitHashWithoutG = commitHash.slice(1); | ||
| | ||
| return `https://github.com/TryGhost/Ghost/commit/${commitHashWithoutG}`; | ||
| // Has pre-release but no recognizable commit hash | ||
| return ''; | ||
| } | ||
| | ||
| return `https://github.com/TryGhost/Ghost/releases/tag/v${cleanedVersion}`; | ||
| } catch { | ||
| return ''; | ||
| } | ||
| | ||
| // Plain semver (with or without +moya) → release tag | ||
| return `https://github.com/TryGhost/Ghost/releases/tag/v${versionWithoutBuild}`; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -45,6 +45,25 @@ describe('Public-config Service', function () { | |
| assert.deepEqual(Object.keys(configProperties), allowedKeys); | ||
| }); | ||
| | ||
| it('should return GHOST_BUILD_VERSION as version when set', function () { | ||
| process.env.GHOST_BUILD_VERSION = '6.21.2+abc1234'; | ||
| | ||
| const configProperties = getConfigProperties(); | ||
| | ||
| assert.equal(configProperties.version, '6.21.2+abc1234'); | ||
| | ||
| delete process.env.GHOST_BUILD_VERSION; | ||
| }); | ||
| | ||
| it('should return package version when GHOST_BUILD_VERSION is not set', function () { | ||
| delete process.env.GHOST_BUILD_VERSION; | ||
| | ||
| const configProperties = getConfigProperties(); | ||
| | ||
| assert.match(configProperties.version, /^\d+\.\d+\.\d+/); | ||
| assert.notEqual(configProperties.version, '6.21.2+abc1234'); | ||
| }); | ||
| Comment on lines +48 to +65 Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improve test isolation by cleaning up env var in The current pattern of manually deleting Consider moving the cleanup to the existing Proposed fix afterEach(async function () { await configUtils.restore(); sinon.restore(); + delete process.env.GHOST_BUILD_VERSION; });Then simplify the tests: it('should return GHOST_BUILD_VERSION as version when set', function () { process.env.GHOST_BUILD_VERSION = '6.21.2+abc1234'; const configProperties = getConfigProperties(); assert.equal(configProperties.version, '6.21.2+abc1234'); - - delete process.env.GHOST_BUILD_VERSION; }); it('should return package version when GHOST_BUILD_VERSION is not set', function () { - delete process.env.GHOST_BUILD_VERSION; - const configProperties = getConfigProperties();🤖 Prompt for AI Agents | ||
| | ||
| it('should return null for tenor apikey when unset', function () { | ||
| let configProperties = getConfigProperties(); | ||
| | ||
| | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard the release-tag fallback for unsupported version strings.
apps/admin-x-framework/src/vite.ts:47-51passesGHOST_BUILD_VERSIONthrough verbatim, andapps/admin-x-settings/src/components/settings/general/about.tsx:9-17will render any non-empty URL this helper returns. With the current fallback, a raw SHA or other non-semver value still becomesreleases/tag/v..., which gives the About dialog a broken link instead of plain text. Return''whenversionWithoutBuildis not a valid semver before constructing the tag URL.🔧 Proposed fix
try { const semverVersion = semverParse(versionWithoutBuild, {includePrerelease: true} as any); - const prerelease = semverVersion?.prerelease; + if (!semverVersion) { + return ''; + } + + const prerelease = semverVersion.prerelease; if (prerelease && prerelease.length > 0) { const splitPrerelease = String(prerelease[0]).split('-'); const commitHash = splitPrerelease[1];📝 Committable suggestion
🤖 Prompt for AI Agents