diff --git a/CLAUDE.md b/CLAUDE.md index 312c187..4f65b8a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,13 @@ # CLAUDE.md +## Public Repository + +This repo is open-source and synced to Codeberg. When making changes: + +- **Never commit sensitive data** — no internal URLs, tokens, credentials, private infrastructure details, or instance-specific configuration in `src/`, `action.yml`, `base-action/`, or `docs/`. The `.forgejo/workflows/` directory is the only place with instance-specific config (runner labels, bot IDs, internal URLs) and that's fine — users write their own workflows. +- **Keep Forgejo support generic** — all Forgejo code must work for any Forgejo instance, not just ours. Don't hardcode assumptions about our setup. Use action inputs and environment variables for instance-specific values. +- **Use generic URLs in tests** — test fixtures use `forgejo.example.com` as the sample Forgejo URL. Never use real instance URLs in `src/` or `test/` code. + ## Commands ```bash diff --git a/docs/forgejo.md b/docs/forgejo.md index cfcf1cd..fe93c3d 100644 --- a/docs/forgejo.md +++ b/docs/forgejo.md @@ -36,9 +36,9 @@ on: jobs: claude-response: - runs-on: grunt # Your Forgejo runner label + runs-on: ubuntu-latest # Your Forgejo runner label steps: - - uses: https://git.wxs.ro/actions/claude-code-action@v1 # Or your mirror URL + - uses: https://your-forgejo.example.com/actions/claude-code-action@v1 # Or your mirror URL with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} platform: "forgejo" @@ -63,9 +63,9 @@ on: jobs: run-claude: - runs-on: grunt + runs-on: ubuntu-latest steps: - - uses: https://git.wxs.ro/actions/claude-code-action@v1 + - uses: https://your-forgejo.example.com/actions/claude-code-action@v1 with: prompt: ${{ github.event.inputs.task }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} @@ -260,7 +260,7 @@ curl -s "https://your-forgejo-instance/api/v1/users/claude" | jq '.id' #### 6. Configure the workflow ```yaml -- uses: https://git.wxs.ro/actions/claude-code-action@v1 +- uses: https://your-forgejo.example.com/actions/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} platform: "forgejo" @@ -357,7 +357,7 @@ New upstream features that use GraphQL or GitHub-specific APIs won't automatical ## E2E Test Results -All execution modes and trigger types have been validated end-to-end on a Forgejo instance (git.wxs.ro): +All execution modes and trigger types have been validated end-to-end on a Forgejo instance: | Mode | Trigger | Status | Details | | -------------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -367,7 +367,7 @@ All execution modes and trigger types have been validated end-to-end on a Forgej | **Tag mode** | `pull_request_review` | Validated | Claude responds to `@claude` in PR review body. Forgejo sends `pull_request_comment` instead of `pull_request_review` — the `@claude` check runs in `trigger.ts`, not the workflow `if:` condition | | **Tag mode** | `issues` with `labeled` action | Validated | Claude responds when the configured label is applied. 3 bugs fixed: workflow `if:` expression parser rejects `#` comments, `github.event.action` not populated in `if:` context, event action is `label_updated` not `labeled` | | **Tag mode** | `issues` with `assigned` action | Validated | Claude responds when assigned to an issue. 1 bug fixed: `assignee` field missing from top-level payload (nested under `issue.assignee` instead) | -| **Tag mode** | Code changes (branch + commits) | Validated | Claude creates branch, pushes commits, links branch in tracking comment. Bot identity (`claude `) correct. PR compare URL uses `expand=1` (Forgejo) instead of `quick_pull=1` (GitHub) | +| **Tag mode** | Code changes (branch + commits) | Validated | Claude creates branch, pushes commits, links branch in tracking comment. Bot identity correct. PR compare URL uses `expand=1` (Forgejo) instead of `quick_pull=1` (GitHub) | ### Tag Mode Verified Capabilities diff --git a/test/forgejo-comment-logic.test.ts b/test/forgejo-comment-logic.test.ts index 8466b5f..720d69d 100644 --- a/test/forgejo-comment-logic.test.ts +++ b/test/forgejo-comment-logic.test.ts @@ -17,7 +17,7 @@ import { // Test helpers // --------------------------------------------------------------------------- -const FORGEJO_SERVER = "https://git.wxs.ro"; +const FORGEJO_SERVER = "https://forgejo.example.com"; const GITHUB_SERVER = "https://github.com"; /** @@ -89,9 +89,9 @@ describe("createForgejoJobRunLink", () => { afterEach(() => setServerUrl(GITHUB_SERVER)); it("creates a job run link with Forgejo server URL", () => { - const result = createForgejoJobRunLink("wxs", "myrepo", "42"); + const result = createForgejoJobRunLink("myorg", "myrepo", "42"); expect(result).toBe( - "[View job run](https://git.wxs.ro/wxs/myrepo/actions/runs/42)", + "[View job run](https://forgejo.example.com/myorg/myrepo/actions/runs/42)", ); }); @@ -110,15 +110,15 @@ describe("createForgejoBranchLink", () => { afterEach(() => setServerUrl(GITHUB_SERVER)); it("creates a branch link with /tree/ path", () => { - const result = createForgejoBranchLink("wxs", "myrepo", "feature-branch"); + const result = createForgejoBranchLink("myorg", "myrepo", "feature-branch"); expect(result).toBe( - "\n[View branch](https://git.wxs.ro/wxs/myrepo/tree/feature-branch)", + "\n[View branch](https://forgejo.example.com/myorg/myrepo/tree/feature-branch)", ); }); it("handles branch names with slashes", () => { const result = createForgejoBranchLink( - "wxs", + "myorg", "myrepo", "claude/issue-5-20240101-1200", ); @@ -134,7 +134,7 @@ describe("createForgejoPrCompareUrl", () => { it("builds compare URL without quick_pull=1", () => { const result = createForgejoPrCompareUrl( FORGEJO_SERVER, - "wxs", + "myorg", "myrepo", "main", "feature-branch", @@ -143,7 +143,7 @@ describe("createForgejoPrCompareUrl", () => { ); expect(result).toBe( - `${FORGEJO_SERVER}/wxs/myrepo/compare/main...feature-branch?expand=1&title=Fix%20bug&body=Fixes%20issue%20%231`, + `${FORGEJO_SERVER}/myorg/myrepo/compare/main...feature-branch?expand=1&title=Fix%20bug&body=Fixes%20issue%20%231`, ); expect(result).not.toContain("quick_pull"); }); @@ -151,7 +151,7 @@ describe("createForgejoPrCompareUrl", () => { it("uses expand=1 parameter for Forgejo", () => { const result = createForgejoPrCompareUrl( FORGEJO_SERVER, - "wxs", + "myorg", "myrepo", "main", "feature", @@ -202,7 +202,7 @@ describe("createForgejoCommentBody", () => { it("uses Forgejo spinner instead of GitHub CDN image", () => { const result = createForgejoCommentBody( - "[View job run](https://git.wxs.ro/wxs/myrepo/actions/runs/1)", + "[View job run](https://forgejo.example.com/myorg/myrepo/actions/runs/1)", ); expect(result).toContain(FORGEJO_SPINNER_HTML); expect(result).not.toContain("github.com/user-attachments"); @@ -211,7 +211,7 @@ describe("createForgejoCommentBody", () => { it("includes working message and job run link", () => { const jobLink = - "[View job run](https://git.wxs.ro/wxs/myrepo/actions/runs/42)"; + "[View job run](https://forgejo.example.com/myorg/myrepo/actions/runs/42)"; const result = createForgejoCommentBody(jobLink); expect(result).toContain("Claude Code is working…"); expect(result).toContain("I'll analyze this and get back to you."); @@ -220,9 +220,9 @@ describe("createForgejoCommentBody", () => { it("includes branch link when provided", () => { const jobLink = - "[View job run](https://git.wxs.ro/wxs/myrepo/actions/runs/42)"; + "[View job run](https://forgejo.example.com/myorg/myrepo/actions/runs/42)"; const branchLink = - "\n[View branch](https://git.wxs.ro/wxs/myrepo/tree/feature)"; + "\n[View branch](https://forgejo.example.com/myorg/myrepo/tree/feature)"; const result = createForgejoCommentBody(jobLink, branchLink); expect(result).toContain(branchLink); }); @@ -235,10 +235,10 @@ describe("createForgejoCommentBody", () => { describe("extractOwnerRepoFromUrl", () => { it("extracts owner/repo from Forgejo job URL", () => { const result = extractOwnerRepoFromUrl( - "https://git.wxs.ro/wxs/myrepo/actions/runs/42", + "https://forgejo.example.com/myorg/myrepo/actions/runs/42", FORGEJO_SERVER, ); - expect(result).toEqual({ owner: "wxs", repo: "myrepo" }); + expect(result).toEqual({ owner: "myorg", repo: "myrepo" }); }); it("extracts owner/repo from GitHub job URL", () => { @@ -251,10 +251,10 @@ describe("extractOwnerRepoFromUrl", () => { it("extracts owner/repo from branch URL", () => { const result = extractOwnerRepoFromUrl( - "https://git.wxs.ro/wxs/myrepo/tree/feature-branch", + "https://forgejo.example.com/myorg/myrepo/tree/feature-branch", FORGEJO_SERVER, ); - expect(result).toEqual({ owner: "wxs", repo: "myrepo" }); + expect(result).toEqual({ owner: "myorg", repo: "myrepo" }); }); it("returns null when hosts don't match", () => { @@ -267,7 +267,7 @@ describe("extractOwnerRepoFromUrl", () => { it("returns null for URLs with less than 2 path segments", () => { const result = extractOwnerRepoFromUrl( - "https://git.wxs.ro/only-one-segment", + "https://forgejo.example.com/only-one-segment", FORGEJO_SERVER, ); expect(result).toBeNull(); @@ -299,9 +299,9 @@ describe("extractOwnerRepoFromUrl", () => { process.env.GITHUB_SERVER_URL = FORGEJO_SERVER; try { const result = extractOwnerRepoFromUrl( - "https://git.wxs.ro/wxs/myrepo/actions/runs/42", + "https://forgejo.example.com/myorg/myrepo/actions/runs/42", ); - expect(result).toEqual({ owner: "wxs", repo: "myrepo" }); + expect(result).toEqual({ owner: "myorg", repo: "myrepo" }); } finally { process.env.GITHUB_SERVER_URL = originalUrl; } @@ -317,7 +317,7 @@ describe("updateCommentBody", () => { currentBody: "Initial comment body", actionFailed: false, executionDetails: null, - jobUrl: "https://git.wxs.ro/wxs/myrepo/actions/runs/42", + jobUrl: "https://forgejo.example.com/myorg/myrepo/actions/runs/42", branchName: undefined, triggerUsername: undefined, }; @@ -418,7 +418,7 @@ describe("updateCommentBody", () => { // This is the critical fix: GitHub's version uses a hardcoded github.com regex // which fails for Forgejo URLs. Our version uses extractOwnerRepoFromUrl. expect(result).toContain( - "• [`claude/issue-5-20240101-1200`](https://git.wxs.ro/wxs/myrepo/tree/claude/issue-5-20240101-1200)", + "• [`claude/issue-5-20240101-1200`](https://forgejo.example.com/myorg/myrepo/tree/claude/issue-5-20240101-1200)", ); }); @@ -439,12 +439,12 @@ describe("updateCommentBody", () => { ...forgejoBaseInput, branchName: "feature-branch", branchLink: - "\n[View branch](https://git.wxs.ro/wxs/myrepo/tree/feature-branch)", + "\n[View branch](https://forgejo.example.com/myorg/myrepo/tree/feature-branch)", }; const result = updateCommentBody(input); expect(result).toContain( - "• [`feature-branch`](https://git.wxs.ro/wxs/myrepo/tree/feature-branch)", + "• [`feature-branch`](https://forgejo.example.com/myorg/myrepo/tree/feature-branch)", ); }); @@ -452,12 +452,12 @@ describe("updateCommentBody", () => { const input: CommentUpdateInput = { ...forgejoBaseInput, branchLink: - "\n[View branch](https://git.wxs.ro/wxs/myrepo/tree/auto-detected-branch)", + "\n[View branch](https://forgejo.example.com/myorg/myrepo/tree/auto-detected-branch)", }; const result = updateCommentBody(input); expect(result).toContain( - "• [`auto-detected-branch`](https://git.wxs.ro/wxs/myrepo/tree/auto-detected-branch)", + "• [`auto-detected-branch`](https://forgejo.example.com/myorg/myrepo/tree/auto-detected-branch)", ); }); }); @@ -467,12 +467,12 @@ describe("updateCommentBody", () => { const input: CommentUpdateInput = { ...forgejoBaseInput, prLink: - "\n[Create a PR](https://git.wxs.ro/wxs/myrepo/compare/main...feature)", + "\n[Create a PR](https://forgejo.example.com/myorg/myrepo/compare/main...feature)", }; const result = updateCommentBody(input); expect(result).toContain( - "• [Create PR ➔](https://git.wxs.ro/wxs/myrepo/compare/main...feature)", + "• [Create PR ➔](https://forgejo.example.com/myorg/myrepo/compare/main...feature)", ); }); @@ -480,19 +480,19 @@ describe("updateCommentBody", () => { const input: CommentUpdateInput = { ...forgejoBaseInput, currentBody: - "Some comment with [Create a PR](https://git.wxs.ro/wxs/myrepo/compare/main...fix)", + "Some comment with [Create a PR](https://forgejo.example.com/myorg/myrepo/compare/main...fix)", }; const result = updateCommentBody(input); expect(result).toContain( - "• [Create PR ➔](https://git.wxs.ro/wxs/myrepo/compare/main...fix)", + "• [Create PR ➔](https://forgejo.example.com/myorg/myrepo/compare/main...fix)", ); expect(result).not.toContain("[Create a PR]"); }); it("handles Forgejo PR URLs with expand=1 parameter", () => { const forgejoUrl = - "https://git.wxs.ro/wxs/myrepo/compare/main...feature?expand=1&title=Fix%20bug&body=Desc"; + "https://forgejo.example.com/myorg/myrepo/compare/main...feature?expand=1&title=Fix%20bug&body=Desc"; const input: CommentUpdateInput = { ...forgejoBaseInput, currentBody: `Done!\n\n[Create a PR](${forgejoUrl})`, @@ -550,7 +550,7 @@ describe("updateCommentBody", () => { actionFailed: false, branchName: "claude-branch-123", prLink: - "\n[Create a PR](https://git.wxs.ro/wxs/myrepo/compare/main...claude-branch-123)", + "\n[Create a PR](https://forgejo.example.com/myorg/myrepo/compare/main...claude-branch-123)", executionDetails: { duration_ms: 65000 }, triggerUsername: "trigger-user", }; @@ -563,7 +563,7 @@ describe("updateCommentBody", () => { ); expect(result).toContain("—— [View job]"); expect(result).toContain( - "• [`claude-branch-123`](https://git.wxs.ro/wxs/myrepo/tree/claude-branch-123)", + "• [`claude-branch-123`](https://forgejo.example.com/myorg/myrepo/tree/claude-branch-123)", ); expect(result).toContain("• [Create PR ➔]"); @@ -610,7 +610,7 @@ describe("updateCommentBody", () => { const input: CommentUpdateInput = { ...forgejoBaseInput, currentBody: - "Some work done\n[View job run](https://git.wxs.ro/wxs/myrepo/actions/runs/1)", + "Some work done\n[View job run](https://forgejo.example.com/myorg/myrepo/actions/runs/1)", triggerUsername: "user", }; @@ -623,7 +623,7 @@ describe("updateCommentBody", () => { const input: CommentUpdateInput = { ...forgejoBaseInput, currentBody: - "Some work done\n[View branch](https://git.wxs.ro/wxs/myrepo/tree/old-branch)", + "Some work done\n[View branch](https://forgejo.example.com/myorg/myrepo/tree/old-branch)", branchName: "new-branch", triggerUsername: "user", }; diff --git a/test/m7-integration.test.ts b/test/m7-integration.test.ts index eaeef97..4164a8f 100644 --- a/test/m7-integration.test.ts +++ b/test/m7-integration.test.ts @@ -70,7 +70,7 @@ describe("M7: Platform routing for mode handlers", () => { it('returns "forgejo" when GITHUB_SERVER_URL is not github.com', () => { delete process.env.PLATFORM; delete process.env.FORGEJO; - process.env.GITHUB_SERVER_URL = "https://git.wxs.ro"; + process.env.GITHUB_SERVER_URL = "https://forgejo.example.com"; expect(detectPlatform()).toBe("forgejo"); }); @@ -139,7 +139,7 @@ describe("M7: Forgejo PR compare URL format", () => { ); const url = createForgejoPrCompareUrl( - "https://git.wxs.ro", + "https://forgejo.example.com", "owner", "repo", "main", @@ -150,7 +150,7 @@ describe("M7: Forgejo PR compare URL format", () => { expect(url).toContain("expand=1"); expect(url).not.toContain("quick_pull=1"); - expect(url).toContain("git.wxs.ro"); + expect(url).toContain("forgejo.example.com"); expect(url).toContain("compare/main...claude/fix-bug"); }); }); @@ -167,10 +167,10 @@ describe("M7: unified updateCommentBody handles both platforms", () => { const result = updateCommentBody({ currentBody: - "Claude Code is working… ⏳\n\nI'll analyze this and get back to you.\n\n[View job run](https://git.wxs.ro/owner/repo/actions/runs/123)", + "Claude Code is working… ⏳\n\nI'll analyze this and get back to you.\n\n[View job run](https://forgejo.example.com/owner/repo/actions/runs/123)", actionFailed: false, executionDetails: { duration_ms: 30000 }, - jobUrl: "https://git.wxs.ro/owner/repo/actions/runs/123", + jobUrl: "https://forgejo.example.com/owner/repo/actions/runs/123", triggerUsername: "testuser", }); diff --git a/test/platform-detector.test.ts b/test/platform-detector.test.ts index 48e9d03..b39a619 100644 --- a/test/platform-detector.test.ts +++ b/test/platform-detector.test.ts @@ -75,7 +75,7 @@ describe("detectPlatform", () => { describe("GITHUB_SERVER_URL heuristic", () => { it("should return 'forgejo' for non-github.com server URLs", () => { - process.env.GITHUB_SERVER_URL = "https://git.wxs.ro"; + process.env.GITHUB_SERVER_URL = "https://forgejo.example.com"; expect(detectPlatform()).toBe("forgejo"); });