diff --git a/.changeset/target-commitish-support.md b/.changeset/target-commitish-support.md new file mode 100644 index 00000000000..611dc7f2c03 --- /dev/null +++ b/.changeset/target-commitish-support.md @@ -0,0 +1,8 @@ +--- +"electron-publish": minor +"builder-util-runtime": minor +--- + +feat(electron-publish): add targetCommitish support for GitHub releases + +Allow specifying branch or commit for release creation via config, EP_TARGET_COMMITISH or GITHUB_SHA env vars diff --git a/packages/app-builder-lib/scheme.json b/packages/app-builder-lib/scheme.json index 2eadec7b1cc..0ec31f72bdf 100644 --- a/packages/app-builder-lib/scheme.json +++ b/packages/app-builder-lib/scheme.json @@ -1615,6 +1615,13 @@ "description": "If defined, sets the prefix of the tag name that comes before the semver number.\ne.g. \"v\" in \"v1.2.3\" or \"test\" of \"test1.2.3\".\nOverrides `vPrefixedTagName`", "type": "string" }, + "targetCommitish": { + "description": "The branch or commit to create the release from.\nBy default, GitHub creates the release tag on the default branch (e.g. `main`).\nUse this to target a specific branch (e.g. `development`) or commit SHA.\n\nCan be set via `EP_TARGET_COMMITISH` env var, or `GITHUB_SHA` in GitHub Actions (current commit).", + "type": [ + "null", + "string" + ] + }, "timeout": { "default": 120000, "description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)", diff --git a/packages/app-builder-lib/src/toolsets/linux.ts b/packages/app-builder-lib/src/toolsets/linux.ts index 809a4cab10e..63fc7866fed 100644 --- a/packages/app-builder-lib/src/toolsets/linux.ts +++ b/packages/app-builder-lib/src/toolsets/linux.ts @@ -59,14 +59,7 @@ export async function getAppImageTools(targetArch: Arch) { artifactPath = path.resolve(artifactPath) - const runtimeArch = - targetArch === Arch.armv7l - ? "arm32" - : targetArch === Arch.arm64 - ? "arm64" - : targetArch === Arch.ia32 - ? "ia32" - : "x64" + const runtimeArch = targetArch === Arch.armv7l ? "arm32" : targetArch === Arch.arm64 ? "arm64" : targetArch === Arch.ia32 ? "ia32" : "x64" return { mksquashfs: path.join(artifactPath, "mksquashfs"), diff --git a/packages/builder-util-runtime/src/publishOptions.ts b/packages/builder-util-runtime/src/publishOptions.ts index c2a366fd158..87920cb10cb 100644 --- a/packages/builder-util-runtime/src/publishOptions.ts +++ b/packages/builder-util-runtime/src/publishOptions.ts @@ -144,6 +144,15 @@ export interface GithubOptions extends PublishConfiguration { * @default draft */ releaseType?: "draft" | "prerelease" | "release" | null + + /** + * The branch or commit to create the release from. + * By default, GitHub creates the release tag on the default branch (e.g. `main`). + * Use this to target a specific branch (e.g. `development`) or commit SHA. + * + * Can be set via `EP_TARGET_COMMITISH` env var, or `GITHUB_SHA` in GitHub Actions (current commit). + */ + readonly targetCommitish?: string | null } /** @private */ diff --git a/packages/electron-publish/src/gitHubPublisher.ts b/packages/electron-publish/src/gitHubPublisher.ts index 922ad6b2ca2..0acc9d1b4f5 100644 --- a/packages/electron-publish/src/gitHubPublisher.ts +++ b/packages/electron-publish/src/gitHubPublisher.ts @@ -27,6 +27,7 @@ interface Asset { export class GitHubPublisher extends HttpPublisher { private readonly tag: string + private readonly targetCommitish: string | undefined readonly _release = new Lazy(() => (this.token === "__test__" ? Promise.resolve(null as any) : this.getOrCreateRelease())) private readonly token: string @@ -67,6 +68,11 @@ export class GitHubPublisher extends HttpPublisher { this.tag = githubTagPrefix(info) + version + this.targetCommitish = info.targetCommitish ?? process.env.EP_TARGET_COMMITISH ?? process.env.GITHUB_SHA ?? undefined + if (this.targetCommitish != null && this.targetCommitish.length > 0) { + log.info({ targetCommitish: this.targetCommitish }, "GitHub release target_commitish is set") + } + if (isEnvTrue(process.env.EP_DRAFT)) { this.releaseType = "draft" log.info({ reason: "env EP_DRAFT is set to true" }, "GitHub provider release type is set to draft") @@ -230,12 +236,16 @@ export class GitHubPublisher extends HttpPublisher { } private createRelease() { - return this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases`, this.token, { + const body: Record = { tag_name: this.tag, name: this.version, draft: this.releaseType === "draft", prerelease: this.releaseType === "prerelease", - }) + } + if (this.targetCommitish != null && this.targetCommitish.length > 0) { + body.target_commitish = this.targetCommitish + } + return this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases`, this.token, body) } // test only diff --git a/pages/publish.md b/pages/publish.md index 61a82244fff..97bf4b70252 100644 --- a/pages/publish.md +++ b/pages/publish.md @@ -53,6 +53,23 @@ win: - bitbucket ``` +To create GitHub releases from a specific branch or commit (instead of the default branch), use `targetCommitish`: + +```json +{ + "build": { + "publish": { + "provider": "github", + "owner": "my-org", + "repo": "my-app", + "targetCommitish": "development" + } + } +} +``` + +Or via env in CI: `EP_TARGET_COMMITISH` or `GITHUB_SHA` (GitHub Actions). + You can also configure publishing using CLI arguments, for example, to force publishing snap not to Snap Store, but to GitHub: `-c.snap.publish=github` [Custom](https://github.com/electron-userland/electron-builder/issues/3261) publish provider can be used if need. diff --git a/test/src/publisher/github/GitHubPublisherTest.ts b/test/src/publisher/github/GitHubPublisherTest.ts new file mode 100644 index 00000000000..29310a364d7 --- /dev/null +++ b/test/src/publisher/github/GitHubPublisherTest.ts @@ -0,0 +1,94 @@ +import { CancellationToken, GithubOptions } from "builder-util-runtime" +import { GitHubPublisher, PublishContext } from "electron-publish" +import { beforeEach, describe, test } from "vitest" + +describe("GitHub Publisher - targetCommitish", () => { + let publishContext: PublishContext + + beforeEach(() => { + publishContext = { + cancellationToken: new CancellationToken(), + progress: null, + } + delete process.env.EP_TARGET_COMMITISH + delete process.env.GITHUB_SHA + delete process.env.GH_TOKEN + delete process.env.GITHUB_TOKEN + }) + + test("constructor accepts targetCommitish in config", ({ expect }) => { + process.env.GH_TOKEN = "test-token-for-constructor" + + const publisher = new GitHubPublisher( + publishContext, + { + provider: "github", + owner: "owner", + repo: "repo", + token: "test-token", + targetCommitish: "development", + } as GithubOptions, + "1.0.0" + ) + + expect(publisher.toString()).toContain("owner") + expect(publisher.toString()).toContain("repo") + expect(publisher.providerName).toBe("github") + }) + + test("constructor accepts empty config without targetCommitish", ({ expect }) => { + process.env.GH_TOKEN = "test-token-for-constructor" + + const publisher = new GitHubPublisher( + publishContext, + { + provider: "github", + owner: "owner", + repo: "repo", + token: "test-token", + } as GithubOptions, + "1.0.0" + ) + + expect(publisher.toString()).toContain("owner") + expect(publisher.providerName).toBe("github") + }) + + test("constructor resolves targetCommitish from EP_TARGET_COMMITISH when not in config", ({ expect }) => { + process.env.GH_TOKEN = "test-token" + process.env.EP_TARGET_COMMITISH = "feature-branch" + + const publisher = new GitHubPublisher( + publishContext, + { + provider: "github", + owner: "owner", + repo: "repo", + token: "test-token", + } as GithubOptions, + "1.0.0" + ) + + expect(publisher.toString()).toContain("owner") + expect(publisher.providerName).toBe("github") + }) + + test("constructor resolves targetCommitish from GITHUB_SHA when EP_TARGET_COMMITISH and config not set", ({ expect }) => { + process.env.GH_TOKEN = "test-token" + process.env.GITHUB_SHA = "abc123def456" + + const publisher = new GitHubPublisher( + publishContext, + { + provider: "github", + owner: "owner", + repo: "repo", + token: "test-token", + } as GithubOptions, + "1.0.0" + ) + + expect(publisher.toString()).toContain("owner") + expect(publisher.providerName).toBe("github") + }) +})