diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 5d86aba..ab247ac 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -13,6 +13,11 @@ concurrency: group: ci-${{ github.ref }} cancel-in-progress: true +# Pin Bun version across all jobs. Bun 1.3.x broke spyOn(fs, ...) in tests. +# Bump this when upstream tests are updated for newer Bun versions. +env: + BUN_VERSION: "1.2.21" + jobs: test: runs-on: grunt @@ -25,9 +30,16 @@ jobs: - name: Install Bun run: | if command -v bun &> /dev/null; then - echo "Bun already installed: $(bun --version)" + INSTALLED=$(bun --version) + if [ "$INSTALLED" = "$BUN_VERSION" ]; then + echo "Bun $BUN_VERSION already installed" + else + echo "Bun $INSTALLED installed, need $BUN_VERSION — reinstalling" + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" + echo "$HOME/.bun/bin" >> "$GITHUB_PATH" + fi else - curl -fsSL https://bun.sh/install | bash + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" echo "$HOME/.bun/bin" >> "$GITHUB_PATH" fi @@ -56,9 +68,16 @@ jobs: - name: Install Bun run: | if command -v bun &> /dev/null; then - echo "Bun already installed: $(bun --version)" + INSTALLED=$(bun --version) + if [ "$INSTALLED" = "$BUN_VERSION" ]; then + echo "Bun $BUN_VERSION already installed" + else + echo "Bun $INSTALLED installed, need $BUN_VERSION — reinstalling" + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" + echo "$HOME/.bun/bin" >> "$GITHUB_PATH" + fi else - curl -fsSL https://bun.sh/install | bash + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" echo "$HOME/.bun/bin" >> "$GITHUB_PATH" fi @@ -79,9 +98,16 @@ jobs: - name: Install Bun run: | if command -v bun &> /dev/null; then - echo "Bun already installed: $(bun --version)" + INSTALLED=$(bun --version) + if [ "$INSTALLED" = "$BUN_VERSION" ]; then + echo "Bun $BUN_VERSION already installed" + else + echo "Bun $INSTALLED installed, need $BUN_VERSION — reinstalling" + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" + echo "$HOME/.bun/bin" >> "$GITHUB_PATH" + fi else - curl -fsSL https://bun.sh/install | bash + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" echo "$HOME/.bun/bin" >> "$GITHUB_PATH" fi diff --git a/src/create-prompt/index.ts b/src/create-prompt/index.ts index 4624c19..d9f99cd 100644 --- a/src/create-prompt/index.ts +++ b/src/create-prompt/index.ts @@ -20,6 +20,8 @@ import { import type { ParsedGitHubContext } from "../github/context"; import type { CommonFields, PreparedContext, EventData } from "./types"; import { GITHUB_SERVER_URL } from "../github/api/config"; +import { detectPlatform } from "../platform/detector"; +import { getForgejoRunIndex } from "../forgejo/comment-logic"; import { extractUserRequest } from "../utils/extract-user-request"; export type { CommonFields, PreparedContext } from "./types"; @@ -525,7 +527,11 @@ Images from comments have been saved to disk. Paths are in the formatted content : "No description provided"; const entityType = eventData.isPR ? "pull request" : "issue"; - const jobUrl = `${GITHUB_SERVER_URL}/${context.repository}/actions/runs/${process.env.GITHUB_RUN_ID}`; + const runIdentifier = + detectPlatform() === "forgejo" + ? getForgejoRunIndex() + : process.env.GITHUB_RUN_ID; + const jobUrl = `${GITHUB_SERVER_URL}/${context.repository}/actions/runs/${runIdentifier}`; let promptContent = `You were tagged on a GitHub ${entityType} via "${context.triggerPhrase}". Read the request and decide how to help. @@ -624,6 +630,10 @@ export function generateDefaultPrompt( const { eventData } = context; const { eventType, triggerContext } = getEventTypeAndContext(context); + const runIdentifier = + detectPlatform() === "forgejo" + ? getForgejoRunIndex() + : process.env.GITHUB_RUN_ID; const formattedContext = formatContext(contextData, eventData.isPR); const formattedComments = formatComments(comments, imageUrlMap); @@ -831,7 +841,7 @@ ${ - Display the todo list as a checklist in the GitHub comment and mark things off as you go. - REPOSITORY SETUP INSTRUCTIONS: The repository's CLAUDE.md file(s) contain critical repo-specific setup instructions, development guidelines, and preferences. Always read and follow these files, particularly the root CLAUDE.md, as they provide essential context for working with the codebase effectively. - Use h3 headers (###) for section titles in your comments, not h1 headers (#). -- Your comment must always include the job run link in the format "[View job run](${GITHUB_SERVER_URL}/${context.repository}/actions/runs/${process.env.GITHUB_RUN_ID})" at the bottom of your response (branch link if there is one should also be included there). +- Your comment must always include the job run link in the format "[View job run](${GITHUB_SERVER_URL}/${context.repository}/actions/runs/${runIdentifier})" at the bottom of your response (branch link if there is one should also be included there). CAPABILITIES AND LIMITATIONS: When users ask you to do something, be aware of what you can and cannot do. This section helps you understand how to respond when users request actions outside your scope. diff --git a/src/entrypoints/update-comment-link.ts b/src/entrypoints/update-comment-link.ts index 3dc6e71..40f1c54 100644 --- a/src/entrypoints/update-comment-link.ts +++ b/src/entrypoints/update-comment-link.ts @@ -10,6 +10,7 @@ import { import { updateForgejoCommentBody, createForgejoPrCompareUrl, + getForgejoRunIndex, } from "../forgejo/comment-logic"; import { detectPlatform } from "../platform/detector"; import { @@ -54,7 +55,9 @@ export async function updateCommentLink( const platform = detectPlatform(); const serverUrl = GITHUB_SERVER_URL; - const jobUrl = `${serverUrl}/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; + const runIdentifier = + platform === "forgejo" ? getForgejoRunIndex() : process.env.GITHUB_RUN_ID; + const jobUrl = `${serverUrl}/${owner}/${repo}/actions/runs/${runIdentifier}`; let comment; let isPRReviewComment = false; diff --git a/src/forgejo/comment-logic.ts b/src/forgejo/comment-logic.ts index 9b83b9a..2fccefb 100644 --- a/src/forgejo/comment-logic.ts +++ b/src/forgejo/comment-logic.ts @@ -34,6 +34,24 @@ function getServerUrl(): string { return process.env.GITHUB_SERVER_URL || "https://github.com"; } +// --------------------------------------------------------------------------- +// Run index helper +// --------------------------------------------------------------------------- + +/** + * Get the run identifier for Forgejo URLs. + * + * Forgejo uses the per-repo sequential run index in URLs (`/actions/runs/{index}`), + * not the internal database run ID. The Forgejo runner sets: + * - GITHUB_RUN_ID = internal DB primary key (e.g. 11943) + * - GITHUB_RUN_NUMBER = per-repo sequential index (e.g. 661) + * + * GitHub uses GITHUB_RUN_ID in URLs, but Forgejo uses GITHUB_RUN_NUMBER. + */ +export function getForgejoRunIndex(): string { + return process.env.GITHUB_RUN_NUMBER || process.env.GITHUB_RUN_ID || "0"; +} + // --------------------------------------------------------------------------- // Spinner // --------------------------------------------------------------------------- diff --git a/src/github/operations/comments/create-initial.ts b/src/github/operations/comments/create-initial.ts index 5ee0024..dd678c0 100644 --- a/src/github/operations/comments/create-initial.ts +++ b/src/github/operations/comments/create-initial.ts @@ -10,6 +10,7 @@ import { createJobRunLink, createCommentBody } from "./common"; import { createForgejoJobRunLink, createForgejoCommentBody, + getForgejoRunIndex, } from "../../../forgejo/comment-logic"; import { detectPlatform } from "../../../platform/detector"; import { @@ -30,7 +31,7 @@ export async function createInitialComment( const jobRunLink = platform === "forgejo" - ? createForgejoJobRunLink(owner, repo, context.runId) + ? createForgejoJobRunLink(owner, repo, getForgejoRunIndex()) : createJobRunLink(owner, repo, context.runId); const initialBody = platform === "forgejo" diff --git a/test/forgejo-comment-logic.test.ts b/test/forgejo-comment-logic.test.ts index e77b8dc..093bfe8 100644 --- a/test/forgejo-comment-logic.test.ts +++ b/test/forgejo-comment-logic.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, beforeEach, afterEach } from "bun:test"; import { FORGEJO_SPINNER_HTML, + getForgejoRunIndex, createForgejoJobRunLink, createForgejoBranchLink, createForgejoPrCompareUrl, @@ -37,6 +38,46 @@ describe("FORGEJO_SPINNER_HTML", () => { }); }); +// --------------------------------------------------------------------------- +// getForgejoRunIndex +// --------------------------------------------------------------------------- + +describe("getForgejoRunIndex", () => { + const origRunNumber = process.env.GITHUB_RUN_NUMBER; + const origRunId = process.env.GITHUB_RUN_ID; + + afterEach(() => { + if (origRunNumber !== undefined) { + process.env.GITHUB_RUN_NUMBER = origRunNumber; + } else { + delete process.env.GITHUB_RUN_NUMBER; + } + if (origRunId !== undefined) { + process.env.GITHUB_RUN_ID = origRunId; + } else { + delete process.env.GITHUB_RUN_ID; + } + }); + + it("prefers GITHUB_RUN_NUMBER over GITHUB_RUN_ID", () => { + process.env.GITHUB_RUN_NUMBER = "661"; + process.env.GITHUB_RUN_ID = "11943"; + expect(getForgejoRunIndex()).toBe("661"); + }); + + it("falls back to GITHUB_RUN_ID when GITHUB_RUN_NUMBER is not set", () => { + delete process.env.GITHUB_RUN_NUMBER; + process.env.GITHUB_RUN_ID = "11943"; + expect(getForgejoRunIndex()).toBe("11943"); + }); + + it("returns '0' when neither env var is set", () => { + delete process.env.GITHUB_RUN_NUMBER; + delete process.env.GITHUB_RUN_ID; + expect(getForgejoRunIndex()).toBe("0"); + }); +}); + // --------------------------------------------------------------------------- // createForgejoJobRunLink // ---------------------------------------------------------------------------