From c85b55fb0a6a2949f57628a520e7ee8d752b2166 Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 28 Feb 2026 08:26:11 +0000 Subject: [PATCH 1/7] feat: add forgejo/gitea compatibility --- src/run.test.ts | 22 ++++++++- src/run.ts | 124 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 119 insertions(+), 27 deletions(-) diff --git a/src/run.test.ts b/src/run.test.ts index 90c9fc00..fb317f95 100644 --- a/src/run.test.ts +++ b/src/run.test.ts @@ -6,7 +6,7 @@ import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { Git } from "./git.ts"; import { setupOctokit } from "./octokit.ts"; -import { runVersion } from "./run.ts"; +import { isForgejoOrGitea, runVersion } from "./run.ts"; vi.mock("@actions/github", () => ({ context: { @@ -278,3 +278,23 @@ fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis. ); }); }); + +describe("isForgejoOrGitea", () => { + it("returns false when no Forgejo/Gitea env vars are set", () => { + delete process.env.GITEA_ACTIONS; + delete process.env.FORGEJO_ACTIONS; + expect(isForgejoOrGitea()).toBe(false); + }); + + it("returns true when GITEA_ACTIONS is set", () => { + process.env.GITEA_ACTIONS = "true"; + expect(isForgejoOrGitea()).toBe(true); + delete process.env.GITEA_ACTIONS; + }); + + it("returns true when FORGEJO_ACTIONS is set", () => { + process.env.FORGEJO_ACTIONS = "true"; + expect(isForgejoOrGitea()).toBe(true); + delete process.env.FORGEJO_ACTIONS; + }); +}); diff --git a/src/run.ts b/src/run.ts index 5ddb33e3..bb57d3c7 100644 --- a/src/run.ts +++ b/src/run.ts @@ -20,6 +20,84 @@ import { const require = createRequire(import.meta.url); +/** + * Detects if we're running on Forgejo or Gitea Actions. + * These platforms set specific environment variables when running actions. + */ +export function isForgejoOrGitea(): boolean { + return !!(process.env.GITEA_ACTIONS || process.env.FORGEJO_ACTIONS); +} + +type PullRequestData = { + number: number; + title: string; + body: string | null; + state: string; +}; + +/** + * Fetches existing pull requests from the version branch to the base branch. + * Uses different API endpoints for GitHub vs Forgejo/Gitea. + * + * GitHub: GET /repos/{owner}/{repo}/pulls?state=open&head={owner}:{branch}&base={base} + * Forgejo/Gitea: GET /repos/{owner}/{repo}/pulls/{base}/{head} + */ +export async function getExistingPullRequests( + octokit: Octokit, + options: { + owner: string; + repo: string; + head: string; + base: string; + } +): Promise { + if (isForgejoOrGitea()) { + core.info("Detected Forgejo/Gitea environment, using compatible API"); + try { + // Forgejo/Gitea API: GET /repos/{owner}/{repo}/pulls/{base}/{head} + // The head should be just the branch name, not owner:branch + const response = await octokit.request( + "GET /repos/{owner}/{repo}/pulls/{base}/{head}", + { + owner: options.owner, + repo: options.repo, + base: options.base, + head: options.head, + } + ); + // This endpoint returns a single PR object, not an array + const pr = response.data as { + number: number; + title: string; + body: string | null; + state: string; + }; + // Only return if the PR is open + if (pr.state === "open") { + return [pr]; + } + return []; + } catch (err: any) { + // 404 means no PR exists, which is fine + if (err.status === 404) { + core.info("No existing pull request found"); + return []; + } + throw err; + } + } else { + // GitHub API: GET /repos/{owner}/{repo}/pulls with query params + const response = await octokit.rest.pulls.list({ + owner: options.owner, + repo: options.repo, + state: "open", + head: `${options.owner}:${options.head}`, + base: options.base, + }); + return response.data; + } +} + // GitHub Issues/PRs messages have a max size limit on the // message body payload. // `body is too long (maximum is 65536 characters)`. @@ -71,12 +149,12 @@ type PublishedPackage = { name: string; version: string }; type PublishResult = | { - published: true; - publishedPackages: PublishedPackage[]; - } + published: true; + publishedPackages: PublishedPackage[]; + } | { - published: false; - }; + published: false; + }; export async function runPublish({ script, @@ -111,7 +189,7 @@ export async function runPublish({ if (pkg === undefined) { throw new Error( `Package "${pkgName}" not found.` + - "This is probably a bug in the action, please open an issue" + "This is probably a bug in the action, please open an issue" ); } releasedPackages.push(pkg); @@ -130,7 +208,7 @@ export async function runPublish({ if (packages.length === 0) { throw new Error( `No package found.` + - "This is probably a bug in the action, please open an issue" + "This is probably a bug in the action, please open an issue" ); } let pkg = packages[0]; @@ -200,11 +278,10 @@ export async function getVersionPrBody({ prBodyMaxCharacters, branch, }: GetMessageOptions) { - let messageHeader = `This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and ${ - hasPublishScript - ? `the packages will be published to npm automatically` - : `publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing)` - }. If you're not ready to do a release yet, that's fine, whenever you add more changesets to ${branch}, this PR will be updated. + let messageHeader = `This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and ${hasPublishScript + ? `the packages will be published to npm automatically` + : `publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing)` + }. If you're not ready to do a release yet, that's fine, whenever you add more changesets to ${branch}, this PR will be updated. `; let messagePrestate = !!preState ? `⚠️⚠️⚠️⚠️⚠️⚠️ @@ -330,9 +407,8 @@ export async function runVersion({ ); const finalPrTitle = `${prTitle}${!!preState ? ` (${preState.tag})` : ""}`; - const finalCommitMessage = `${commitMessage}${ - !!preState ? ` (${preState.tag})` : "" - }`; + const finalCommitMessage = `${commitMessage}${!!preState ? ` (${preState.tag})` : "" + }`; /** * Fetch any existing pull requests that are open against the branch, @@ -341,18 +417,14 @@ export async function runVersion({ * (`@changesets/ghcommit` has to reset the branch to the same commit as the base, * which GitHub will then react to by closing the PRs) */ - const existingPullRequests = await octokit.rest.pulls.list({ - ...github.context.repo, - state: "open", - head: `${github.context.repo.owner}:${versionBranch}`, + const existingPullRequests = await getExistingPullRequests(octokit, { + owner: github.context.repo.owner, + repo: github.context.repo.repo, + head: versionBranch, base: branch, }); core.info( - `Existing pull requests: ${JSON.stringify( - existingPullRequests.data, - null, - 2 - )}` + `Existing pull requests: ${JSON.stringify(existingPullRequests, null, 2)}` ); await git.pushChanges({ branch: versionBranch, message: finalCommitMessage }); @@ -369,7 +441,7 @@ export async function runVersion({ prBodyMaxCharacters, }); - if (existingPullRequests.data.length === 0) { + if (existingPullRequests.length === 0) { core.info("creating pull request"); const { data: newPullRequest } = await octokit.rest.pulls.create({ base: branch, @@ -383,7 +455,7 @@ export async function runVersion({ pullRequestNumber: newPullRequest.number, }; } else { - const [pullRequest] = existingPullRequests.data; + const [pullRequest] = existingPullRequests; core.info(`updating found pull request #${pullRequest.number}`); await octokit.rest.pulls.update({ From 905ea6d481f54d210f11da2daa15655dadd89f15 Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 28 Feb 2026 08:33:57 +0000 Subject: [PATCH 2/7] chore: add changeset --- .changeset/six-snails-hide.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/six-snails-hide.md diff --git a/.changeset/six-snails-hide.md b/.changeset/six-snails-hide.md new file mode 100644 index 00000000..2fc17d8b --- /dev/null +++ b/.changeset/six-snails-hide.md @@ -0,0 +1,5 @@ +--- +"@changesets/action": minor +--- + +Add Forgejo/Gitea actions support From f9217b36d3616f9ae4cfc48d52f6457d8fe68018 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 08:35:23 +0000 Subject: [PATCH 3/7] Version Packages --- .changeset/six-snails-hide.md | 5 ----- CHANGELOG.md | 6 ++++++ package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/six-snails-hide.md diff --git a/.changeset/six-snails-hide.md b/.changeset/six-snails-hide.md deleted file mode 100644 index 2fc17d8b..00000000 --- a/.changeset/six-snails-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@changesets/action": minor ---- - -Add Forgejo/Gitea actions support diff --git a/CHANGELOG.md b/CHANGELOG.md index be10b90f..7a8f06b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @changesets/action +## 1.8.0 + +### Minor Changes + +- [`905ea6d`](https://github.com/changesets/action/commit/905ea6d481f54d210f11da2daa15655dadd89f15) Thanks [@openscript](https://github.com/openscript)! - Add Forgejo/Gitea actions support + ## 1.7.0 ### Minor Changes diff --git a/package.json b/package.json index 47455ea7..1ec8707a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@changesets/action", - "version": "1.7.0", + "version": "1.8.0", "main": "dist/index.js", "type": "module", "license": "MIT", From 213546601666ba543dd4a716db7b148c55f4c36f Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 28 Feb 2026 08:42:00 +0000 Subject: [PATCH 4/7] chore: encode branch and base --- .changeset/old-files-dream.md | 5 +++++ src/run.ts | 21 ++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 .changeset/old-files-dream.md diff --git a/.changeset/old-files-dream.md b/.changeset/old-files-dream.md new file mode 100644 index 00000000..243ce27c --- /dev/null +++ b/.changeset/old-files-dream.md @@ -0,0 +1,5 @@ +--- +"@changesets/action": patch +--- + +Encode branch and base diff --git a/src/run.ts b/src/run.ts index bb57d3c7..2c583373 100644 --- a/src/run.ts +++ b/src/run.ts @@ -55,16 +55,13 @@ export async function getExistingPullRequests( core.info("Detected Forgejo/Gitea environment, using compatible API"); try { // Forgejo/Gitea API: GET /repos/{owner}/{repo}/pulls/{base}/{head} - // The head should be just the branch name, not owner:branch - const response = await octokit.request( - "GET /repos/{owner}/{repo}/pulls/{base}/{head}", - { - owner: options.owner, - repo: options.repo, - base: options.base, - head: options.head, - } - ); + // Branch names with slashes need to be URL-encoded + const encodedBase = encodeURIComponent(options.base); + const encodedHead = encodeURIComponent(options.head); + const url = `/repos/${options.owner}/${options.repo}/pulls/${encodedBase}/${encodedHead}`; + core.info(`Fetching PR from: ${url}`); + + const response = await octokit.request(`GET ${url}`); // This endpoint returns a single PR object, not an array const pr = response.data as { number: number; @@ -72,6 +69,7 @@ export async function getExistingPullRequests( body: string | null; state: string; }; + core.info(`Found PR #${pr.number} with state: ${pr.state}`); // Only return if the PR is open if (pr.state === "open") { return [pr]; @@ -80,9 +78,10 @@ export async function getExistingPullRequests( } catch (err: any) { // 404 means no PR exists, which is fine if (err.status === 404) { - core.info("No existing pull request found"); + core.info("No existing pull request found (404)"); return []; } + core.info(`Error fetching PR: ${err.status} - ${err.message}`); throw err; } } else { From c28a78b90eb318d33d07ff990797d656a8ab8c1c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 08:43:15 +0000 Subject: [PATCH 5/7] Version Packages --- .changeset/old-files-dream.md | 5 ----- CHANGELOG.md | 6 ++++++ package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/old-files-dream.md diff --git a/.changeset/old-files-dream.md b/.changeset/old-files-dream.md deleted file mode 100644 index 243ce27c..00000000 --- a/.changeset/old-files-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@changesets/action": patch ---- - -Encode branch and base diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8f06b5..0b934aa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @changesets/action +## 1.8.1 + +### Patch Changes + +- [`2135466`](https://github.com/changesets/action/commit/213546601666ba543dd4a716db7b148c55f4c36f) Thanks [@openscript](https://github.com/openscript)! - Encode branch and base + ## 1.8.0 ### Minor Changes diff --git a/package.json b/package.json index 1ec8707a..04cf4d3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@changesets/action", - "version": "1.8.0", + "version": "1.8.1", "main": "dist/index.js", "type": "module", "license": "MIT", From e47dcdcda2d3c570ab43fc31f1ac43872b47a5a4 Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 28 Feb 2026 10:40:03 +0000 Subject: [PATCH 6/7] chore: filter clientside for forgejo --- .changeset/fuzzy-carrots-rest.md | 5 ++++ src/run.ts | 50 ++++++++++++-------------------- 2 files changed, 23 insertions(+), 32 deletions(-) create mode 100644 .changeset/fuzzy-carrots-rest.md diff --git a/.changeset/fuzzy-carrots-rest.md b/.changeset/fuzzy-carrots-rest.md new file mode 100644 index 00000000..91942157 --- /dev/null +++ b/.changeset/fuzzy-carrots-rest.md @@ -0,0 +1,5 @@ +--- +"@changesets/action": patch +--- + +Filter clientside for Forgejo diff --git a/src/run.ts b/src/run.ts index 2c583373..d7d88b4a 100644 --- a/src/run.ts +++ b/src/run.ts @@ -33,6 +33,8 @@ type PullRequestData = { title: string; body: string | null; state: string; + head?: { ref: string }; + base?: { ref: string }; }; /** @@ -40,7 +42,7 @@ type PullRequestData = { * Uses different API endpoints for GitHub vs Forgejo/Gitea. * * GitHub: GET /repos/{owner}/{repo}/pulls?state=open&head={owner}:{branch}&base={base} - * Forgejo/Gitea: GET /repos/{owner}/{repo}/pulls/{base}/{head} + * Forgejo/Gitea: List all open PRs and filter client-side (their list API doesn't support head/base filters) */ export async function getExistingPullRequests( octokit: Octokit, @@ -53,37 +55,21 @@ export async function getExistingPullRequests( ): Promise { if (isForgejoOrGitea()) { core.info("Detected Forgejo/Gitea environment, using compatible API"); - try { - // Forgejo/Gitea API: GET /repos/{owner}/{repo}/pulls/{base}/{head} - // Branch names with slashes need to be URL-encoded - const encodedBase = encodeURIComponent(options.base); - const encodedHead = encodeURIComponent(options.head); - const url = `/repos/${options.owner}/${options.repo}/pulls/${encodedBase}/${encodedHead}`; - core.info(`Fetching PR from: ${url}`); - - const response = await octokit.request(`GET ${url}`); - // This endpoint returns a single PR object, not an array - const pr = response.data as { - number: number; - title: string; - body: string | null; - state: string; - }; - core.info(`Found PR #${pr.number} with state: ${pr.state}`); - // Only return if the PR is open - if (pr.state === "open") { - return [pr]; - } - return []; - } catch (err: any) { - // 404 means no PR exists, which is fine - if (err.status === 404) { - core.info("No existing pull request found (404)"); - return []; - } - core.info(`Error fetching PR: ${err.status} - ${err.message}`); - throw err; - } + // Forgejo/Gitea list endpoint doesn't support head/base filters, + // so we list all open PRs and filter client-side + const response = await octokit.rest.pulls.list({ + owner: options.owner, + repo: options.repo, + state: "open", + }); + // Filter by head and base branch + const filtered = response.data.filter( + (pr) => pr.head.ref === options.head && pr.base.ref === options.base + ); + core.info( + `Found ${response.data.length} open PR(s), ${filtered.length} matching head=${options.head} base=${options.base}` + ); + return filtered; } else { // GitHub API: GET /repos/{owner}/{repo}/pulls with query params const response = await octokit.rest.pulls.list({ From 862f4b2e45dcd6d1c40dc483cdc2fc627d5a8005 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 10:40:42 +0000 Subject: [PATCH 7/7] Version Packages --- .changeset/fuzzy-carrots-rest.md | 5 ----- CHANGELOG.md | 6 ++++++ package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/fuzzy-carrots-rest.md diff --git a/.changeset/fuzzy-carrots-rest.md b/.changeset/fuzzy-carrots-rest.md deleted file mode 100644 index 91942157..00000000 --- a/.changeset/fuzzy-carrots-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@changesets/action": patch ---- - -Filter clientside for Forgejo diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b934aa7..b6d43c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @changesets/action +## 1.8.2 + +### Patch Changes + +- [`e47dcdc`](https://github.com/changesets/action/commit/e47dcdcda2d3c570ab43fc31f1ac43872b47a5a4) Thanks [@openscript](https://github.com/openscript)! - Filter clientside for Forgejo + ## 1.8.1 ### Patch Changes diff --git a/package.json b/package.json index 04cf4d3f..2289192c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@changesets/action", - "version": "1.8.1", + "version": "1.8.2", "main": "dist/index.js", "type": "module", "license": "MIT",