From 3bace041afc756cdfb0f185418092f0a09fd1bc6 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 15 Nov 2023 13:47:46 +0100 Subject: [PATCH 1/2] feat: Allow to configure `latestTargetBranch` --- README.md | 13 +++++++++ src/commands/publish.ts | 45 ++++++++++++++++++++++++++--- src/schemas/projectConfig.schema.ts | 1 + src/schemas/project_config.ts | 1 + src/targets/base.ts | 5 ++-- src/targets/github.ts | 12 ++++++-- src/targets/npm.ts | 30 +++++++++++++------ src/targets/upm.ts | 8 ++++- 8 files changed, 97 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e74f0756..6f6517cf 100644 --- a/README.md +++ b/README.md @@ -322,6 +322,19 @@ for a release is `{releaseBranchPrefix}/{version}`. The prefix defaults to releaseBranchPrefix: publish ``` +### Latest Branch Name + +If configured, only tag releases as "latest" when merging into this branch. +This can be useful to make sure that releases from e.g. a `v1` branch do not get tagged as "latest". +Note that not all targets may use this. + +```yaml +latestTargetBranch: main +``` + +If you specific `minVersion: '1.8.0'` or above, this will default to use the default branch. +If your `minVersion` is older, the previous behavior (of always tagging as latest, for any branch) will continue to be used. + ### Changelog Policies `craft` can help you to maintain change logs for your projects. At the moment, diff --git a/src/commands/publish.ts b/src/commands/publish.ts index cfb5025c..0f194b8c 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -31,7 +31,7 @@ import { hasExecutable, spawnProcess, } from '../utils/system'; -import { isValidVersion } from '../utils/version'; +import { isValidVersion, parseVersion } from '../utils/version'; import { BaseStatusProvider } from '../status_providers/base'; import { BaseArtifactProvider } from '../artifact_providers/base'; import { SimpleGit } from 'simple-git'; @@ -160,7 +160,8 @@ function checkVersion(argv: Arguments, _opt: any): any { async function publishToTarget( target: BaseTarget, version: string, - revision: string + revision: string, + isLatest: boolean ): Promise { const publishMessage = `=== Publishing to target: ${chalk.bold.cyan( target.id @@ -170,7 +171,7 @@ async function publishToTarget( logger.info(delim); logger.info(publishMessage); logger.info(delim); - await target.publish(version, revision); + await target.publish(version, revision, isLatest); } /** @@ -527,12 +528,27 @@ export async function publishMain(argv: PublishOptions): Promise { logger.info(' '); await promptConfirmation(); + // Check if latest target & merge target match, + // to determine if we should tag this release as "latest" + const latestTarget = + config.latestTargetBranch || + getDefaultLatestTargetBranch(git, argv.remote, config.minVersion); + const mergeTarget = + argv.mergeTarget || (await getDefaultBranch(git, argv.remote)); + const isLatest = !latestTarget || latestTarget === mergeTarget; + + if (!isLatest) { + logger.info( + `Not tagging as "latest" because merge target is: ${mergeTarget}, not ${latestTarget}` + ); + } + await withTempDir(async (downloadDirectory: string) => { artifactProvider.setDownloadDirectory(downloadDirectory); // Publish to all targets for (const target of targetList) { - await publishToTarget(target, newVersion, revision); + await publishToTarget(target, newVersion, revision, isLatest); publishState.published[BaseTarget.getId(target.config)] = true; if (!isDryRun()) { writeFileSync(publishStateFile, JSON.stringify(publishState)); @@ -606,3 +622,24 @@ export const handler = async (args: { handleGlobalError(e); } }; + +function getDefaultLatestTargetBranch( + git: SimpleGit, + remote: string, + minVersion?: string +): Promise | undefined { + // If running this on an older min version, we stick to the old behavior where we always mark as latest + if (!minVersion) { + return undefined; + } + const parsedMinVersion = parseVersion(minVersion); + if ( + !parsedMinVersion || + parsedMinVersion.major < 1 || + parsedMinVersion.minor < 8 + ) { + return undefined; + } + + return getDefaultBranch(git, remote); +} diff --git a/src/schemas/projectConfig.schema.ts b/src/schemas/projectConfig.schema.ts index 60da30de..96b0c93a 100644 --- a/src/schemas/projectConfig.schema.ts +++ b/src/schemas/projectConfig.schema.ts @@ -35,6 +35,7 @@ const projectConfigJsonSchema = { preReleaseCommand: { type: 'string' }, postReleaseCommand: { type: 'string' }, releaseBranchPrefix: { type: 'string' }, + latestTargetBranch: { type: 'string' }, changelog: { type: 'string' }, changelogPolicy: { title: 'ChangelogPolicy', diff --git a/src/schemas/project_config.ts b/src/schemas/project_config.ts index cd4fc778..860b80b5 100644 --- a/src/schemas/project_config.ts +++ b/src/schemas/project_config.ts @@ -13,6 +13,7 @@ export interface CraftProjectConfig { preReleaseCommand?: string; postReleaseCommand?: string; releaseBranchPrefix?: string; + latestTargetBranch?: string; changelog?: string; changelogPolicy?: ChangelogPolicy; minVersion?: string; diff --git a/src/targets/base.ts b/src/targets/base.ts index 7fb08e45..5a1551f5 100644 --- a/src/targets/base.ts +++ b/src/targets/base.ts @@ -60,11 +60,12 @@ export class BaseTarget { * * @param version New version to be released * @param revision Git commit SHA to be published + * @param isLatest If this release should be marked as "latest" */ public async publish( _version: string, - - _revision: string + _revision: string, + _isLatest: boolean ): Promise { throw new Error('Not implemented'); return; diff --git a/src/targets/github.ts b/src/targets/github.ts index 593f2977..17cc3710 100644 --- a/src/targets/github.ts +++ b/src/targets/github.ts @@ -106,6 +106,7 @@ export class GitHubTarget extends BaseTarget { public async createDraftRelease( version: string, revision: string, + isLatest: boolean, changes?: Changeset ): Promise { const tag = versionToTag(version, this.githubConfig.tagPrefix); @@ -130,6 +131,7 @@ export class GitHubTarget extends BaseTarget { repo: this.githubConfig.repo, tag_name: tag, target_commitish: revision, + make_latest: isLatest && !isPreview ? 'true' : 'false', ...changes, }); return data; @@ -259,7 +261,7 @@ export class GitHubTarget extends BaseTarget { release: GitHubRelease, path: string, contentType?: string, - retries = 3, + retries = 3 ): Promise<{ url: string; size: number }> { const contentTypeProcessed = contentType || DEFAULT_CONTENT_TYPE; const stats = statSync(path); @@ -369,8 +371,13 @@ export class GitHubTarget extends BaseTarget { * * @param version New version to be released * @param revision Git commit SHA to be published + * @param isLatest If this release should be marked as latest */ - public async publish(version: string, revision: string): Promise { + public async publish( + version: string, + revision: string, + isLatest: boolean + ): Promise { if (this.githubConfig.tagOnly) { this.logger.info( `Not creating a GitHub release because "tagOnly" flag was set.` @@ -395,6 +402,7 @@ export class GitHubTarget extends BaseTarget { const draftRelease = await this.createDraftRelease( version, revision, + isLatest, changelog ); diff --git a/src/targets/npm.ts b/src/targets/npm.ts index ecee4e64..28b2d2ce 100644 --- a/src/targets/npm.ts +++ b/src/targets/npm.ts @@ -54,6 +54,8 @@ interface NpmPublishOptions { otp?: string; /** New version to publish */ version: string; + /** If defined, set this tag instead of "latest" */ + tag?: string; } /** @@ -178,14 +180,8 @@ export class NpmTarget extends BaseTarget { args.push(`--access=${this.npmConfig.access}`); } - // In case we have a prerelease, there should never be a reason to publish - // it with the latest tag in npm. - if (isPreviewRelease(options.version)) { - this.logger.warn('Detected pre-release version for npm package!'); - this.logger.warn( - 'Adding tag "next" to not make it "latest" in registry.' - ); - args.push('--tag=next'); + if (options.tag) { + args.push(`--tag=${options.tag}`); } return withTempFile(filePath => { @@ -219,7 +215,11 @@ export class NpmTarget extends BaseTarget { * @param version New version to be released * @param revision Git commit SHA to be published */ - public async publish(version: string, revision: string): Promise { + public async publish( + version: string, + revision: string, + isLatest: boolean + ): Promise { this.logger.debug('Fetching artifact list...'); const packageFiles = await this.getArtifactsForRevision(revision, { includeNames: DEFAULT_PACKAGE_REGEX, @@ -235,6 +235,18 @@ export class NpmTarget extends BaseTarget { publishOptions.otp = await this.requestOtp(); } + // In case we have a prerelease, there should never be a reason to publish + // it with the latest tag in npm. + if (isPreviewRelease(version)) { + this.logger.warn('Detected pre-release version for npm package!'); + this.logger.warn( + 'Adding tag "next" to not make it "latest" in registry.' + ); + publishOptions.tag = 'next'; + } else if (!isLatest) { + publishOptions.tag = 'old'; + } + await Promise.all( packageFiles.map(async (file: RemoteArtifact) => { const path = await this.artifactProvider.downloadArtifact(file); diff --git a/src/targets/upm.ts b/src/targets/upm.ts index 472c83a7..8294cdab 100644 --- a/src/targets/upm.ts +++ b/src/targets/upm.ts @@ -92,8 +92,13 @@ export class UpmTarget extends BaseTarget { * * @param version New version to be released * @param revision Git commit SHA to be published + * @param isLatest If this release should be marked as latest */ - public async publish(version: string, revision: string): Promise { + public async publish( + version: string, + revision: string, + isLatest: boolean + ): Promise { this.logger.info('Fetching artifact...'); const packageFile = await this.fetchArtifact(revision); if (!packageFile) { @@ -147,6 +152,7 @@ export class UpmTarget extends BaseTarget { const draftRelease = await this.githubTarget.createDraftRelease( version, targetRevision, + isLatest, changes ); await this.githubTarget.publishRelease(draftRelease); From cabf8e1ad07a02261484689387d85010fcebe2d0 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 15 Nov 2023 15:23:18 +0100 Subject: [PATCH 2/2] fix test --- src/targets/__tests__/upm.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/targets/__tests__/upm.test.ts b/src/targets/__tests__/upm.test.ts index f9c0d392..3af59fc8 100644 --- a/src/targets/__tests__/upm.test.ts +++ b/src/targets/__tests__/upm.test.ts @@ -60,7 +60,7 @@ describe('UPM Target', () => { test('publish', () => { return expect( - upmTarget.publish('version', 'revision') + upmTarget.publish('version', 'revision', true) ).resolves.not.toThrow(); }); });