chore: anonymize internal URLs for public release

Replace git.wxs.ro with forgejo.example.com in tests and docs.
Add public repo guidelines to CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad MocanuandClaude Opus 4.6 committed 2026-02-27 16:36:34 +02:00
1 parent b01c498dd9
commit fba53fc8a0
5 files changed
+56 -48

No files matched your search

+8
View File
@@ -1,5 +1,13 @@
# CLAUDE.md # 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 ## Commands
```bash ```bash
+7 -7
View File
@@ -36,9 +36,9 @@ on:
jobs: jobs:
claude-response: claude-response:
runs-on: grunt # Your Forgejo runner label runs-on: ubuntu-latest # Your Forgejo runner label
steps: 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: with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
platform: "forgejo" platform: "forgejo"
@@ -63,9 +63,9 @@ on:
jobs: jobs:
run-claude: run-claude:
runs-on: grunt runs-on: ubuntu-latest
steps: steps:
- uses: https://git.wxs.ro/actions/claude-code-action@v1 - uses: https://your-forgejo.example.com/actions/claude-code-action@v1
with: with:
prompt: ${{ github.event.inputs.task }} prompt: ${{ github.event.inputs.task }}
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} 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 #### 6. Configure the workflow
```yaml ```yaml
- uses: https://git.wxs.ro/actions/claude-code-action@v1 - uses: https://your-forgejo.example.com/actions/claude-code-action@v1
with: with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
platform: "forgejo" platform: "forgejo"
@@ -357,7 +357,7 @@ New upstream features that use GraphQL or GitHub-specific APIs won't automatical
## E2E Test Results ## 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 | | 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** | `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 `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** | `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 <claude@wxs.ro>`) 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 ### Tag Mode Verified Capabilities
+35 -35
View File
@@ -17,7 +17,7 @@ import {
// Test helpers // Test helpers
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const FORGEJO_SERVER = "https://git.wxs.ro"; const FORGEJO_SERVER = "https://forgejo.example.com";
const GITHUB_SERVER = "https://github.com"; const GITHUB_SERVER = "https://github.com";
/** /**
@@ -89,9 +89,9 @@ describe("createForgejoJobRunLink", () => {
afterEach(() => setServerUrl(GITHUB_SERVER)); afterEach(() => setServerUrl(GITHUB_SERVER));
it("creates a job run link with Forgejo server URL", () => { 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( 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)); afterEach(() => setServerUrl(GITHUB_SERVER));
it("creates a branch link with /tree/ path", () => { 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( 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", () => { it("handles branch names with slashes", () => {
const result = createForgejoBranchLink( const result = createForgejoBranchLink(
"wxs", "myorg",
"myrepo", "myrepo",
"claude/issue-5-20240101-1200", "claude/issue-5-20240101-1200",
); );
@@ -134,7 +134,7 @@ describe("createForgejoPrCompareUrl", () => {
it("builds compare URL without quick_pull=1", () => { it("builds compare URL without quick_pull=1", () => {
const result = createForgejoPrCompareUrl( const result = createForgejoPrCompareUrl(
FORGEJO_SERVER, FORGEJO_SERVER,
"wxs", "myorg",
"myrepo", "myrepo",
"main", "main",
"feature-branch", "feature-branch",
@@ -143,7 +143,7 @@ describe("createForgejoPrCompareUrl", () => {
); );
expect(result).toBe( 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"); expect(result).not.toContain("quick_pull");
}); });
@@ -151,7 +151,7 @@ describe("createForgejoPrCompareUrl", () => {
it("uses expand=1 parameter for Forgejo", () => { it("uses expand=1 parameter for Forgejo", () => {
const result = createForgejoPrCompareUrl( const result = createForgejoPrCompareUrl(
FORGEJO_SERVER, FORGEJO_SERVER,
"wxs", "myorg",
"myrepo", "myrepo",
"main", "main",
"feature", "feature",
@@ -202,7 +202,7 @@ describe("createForgejoCommentBody", () => {
it("uses Forgejo spinner instead of GitHub CDN image", () => { it("uses Forgejo spinner instead of GitHub CDN image", () => {
const result = createForgejoCommentBody( 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).toContain(FORGEJO_SPINNER_HTML);
expect(result).not.toContain("github.com/user-attachments"); expect(result).not.toContain("github.com/user-attachments");
@@ -211,7 +211,7 @@ describe("createForgejoCommentBody", () => {
it("includes working message and job run link", () => { it("includes working message and job run link", () => {
const jobLink = 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); const result = createForgejoCommentBody(jobLink);
expect(result).toContain("Claude Code is working…"); expect(result).toContain("Claude Code is working…");
expect(result).toContain("I'll analyze this and get back to you."); expect(result).toContain("I'll analyze this and get back to you.");
@@ -220,9 +220,9 @@ describe("createForgejoCommentBody", () => {
it("includes branch link when provided", () => { it("includes branch link when provided", () => {
const jobLink = 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 = 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); const result = createForgejoCommentBody(jobLink, branchLink);
expect(result).toContain(branchLink); expect(result).toContain(branchLink);
}); });
@@ -235,10 +235,10 @@ describe("createForgejoCommentBody", () => {
describe("extractOwnerRepoFromUrl", () => { describe("extractOwnerRepoFromUrl", () => {
it("extracts owner/repo from Forgejo job URL", () => { it("extracts owner/repo from Forgejo job URL", () => {
const result = extractOwnerRepoFromUrl( const result = extractOwnerRepoFromUrl(
"https://git.wxs.ro/wxs/myrepo/actions/runs/42", "https://forgejo.example.com/myorg/myrepo/actions/runs/42",
FORGEJO_SERVER, FORGEJO_SERVER,
); );
expect(result).toEqual({ owner: "wxs", repo: "myrepo" }); expect(result).toEqual({ owner: "myorg", repo: "myrepo" });
}); });
it("extracts owner/repo from GitHub job URL", () => { it("extracts owner/repo from GitHub job URL", () => {
@@ -251,10 +251,10 @@ describe("extractOwnerRepoFromUrl", () => {
it("extracts owner/repo from branch URL", () => { it("extracts owner/repo from branch URL", () => {
const result = extractOwnerRepoFromUrl( const result = extractOwnerRepoFromUrl(
"https://git.wxs.ro/wxs/myrepo/tree/feature-branch", "https://forgejo.example.com/myorg/myrepo/tree/feature-branch",
FORGEJO_SERVER, FORGEJO_SERVER,
); );
expect(result).toEqual({ owner: "wxs", repo: "myrepo" }); expect(result).toEqual({ owner: "myorg", repo: "myrepo" });
}); });
it("returns null when hosts don't match", () => { 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", () => { it("returns null for URLs with less than 2 path segments", () => {
const result = extractOwnerRepoFromUrl( const result = extractOwnerRepoFromUrl(
"https://git.wxs.ro/only-one-segment", "https://forgejo.example.com/only-one-segment",
FORGEJO_SERVER, FORGEJO_SERVER,
); );
expect(result).toBeNull(); expect(result).toBeNull();
@@ -299,9 +299,9 @@ describe("extractOwnerRepoFromUrl", () => {
process.env.GITHUB_SERVER_URL = FORGEJO_SERVER; process.env.GITHUB_SERVER_URL = FORGEJO_SERVER;
try { try {
const result = extractOwnerRepoFromUrl( 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 { } finally {
process.env.GITHUB_SERVER_URL = originalUrl; process.env.GITHUB_SERVER_URL = originalUrl;
} }
@@ -317,7 +317,7 @@ describe("updateCommentBody", () => {
currentBody: "Initial comment body", currentBody: "Initial comment body",
actionFailed: false, actionFailed: false,
executionDetails: null, executionDetails: null,
jobUrl: "https://git.wxs.ro/wxs/myrepo/actions/runs/42", jobUrl: "https://forgejo.example.com/myorg/myrepo/actions/runs/42",
branchName: undefined, branchName: undefined,
triggerUsername: undefined, triggerUsername: undefined,
}; };
@@ -418,7 +418,7 @@ describe("updateCommentBody", () => {
// This is the critical fix: GitHub's version uses a hardcoded github.com regex // This is the critical fix: GitHub's version uses a hardcoded github.com regex
// which fails for Forgejo URLs. Our version uses extractOwnerRepoFromUrl. // which fails for Forgejo URLs. Our version uses extractOwnerRepoFromUrl.
expect(result).toContain( 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, ...forgejoBaseInput,
branchName: "feature-branch", branchName: "feature-branch",
branchLink: 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); const result = updateCommentBody(input);
expect(result).toContain( 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 = { const input: CommentUpdateInput = {
...forgejoBaseInput, ...forgejoBaseInput,
branchLink: 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); const result = updateCommentBody(input);
expect(result).toContain( 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 = { const input: CommentUpdateInput = {
...forgejoBaseInput, ...forgejoBaseInput,
prLink: 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); const result = updateCommentBody(input);
expect(result).toContain( 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 = { const input: CommentUpdateInput = {
...forgejoBaseInput, ...forgejoBaseInput,
currentBody: 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); const result = updateCommentBody(input);
expect(result).toContain( 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]"); expect(result).not.toContain("[Create a PR]");
}); });
it("handles Forgejo PR URLs with expand=1 parameter", () => { it("handles Forgejo PR URLs with expand=1 parameter", () => {
const forgejoUrl = 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 = { const input: CommentUpdateInput = {
...forgejoBaseInput, ...forgejoBaseInput,
currentBody: `Done!\n\n[Create a PR](${forgejoUrl})`, currentBody: `Done!\n\n[Create a PR](${forgejoUrl})`,
@@ -550,7 +550,7 @@ describe("updateCommentBody", () => {
actionFailed: false, actionFailed: false,
branchName: "claude-branch-123", branchName: "claude-branch-123",
prLink: 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 }, executionDetails: { duration_ms: 65000 },
triggerUsername: "trigger-user", triggerUsername: "trigger-user",
}; };
@@ -563,7 +563,7 @@ describe("updateCommentBody", () => {
); );
expect(result).toContain("—— [View job]"); expect(result).toContain("—— [View job]");
expect(result).toContain( 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 ➔]"); expect(result).toContain("• [Create PR ➔]");
@@ -610,7 +610,7 @@ describe("updateCommentBody", () => {
const input: CommentUpdateInput = { const input: CommentUpdateInput = {
...forgejoBaseInput, ...forgejoBaseInput,
currentBody: 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", triggerUsername: "user",
}; };
@@ -623,7 +623,7 @@ describe("updateCommentBody", () => {
const input: CommentUpdateInput = { const input: CommentUpdateInput = {
...forgejoBaseInput, ...forgejoBaseInput,
currentBody: 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", branchName: "new-branch",
triggerUsername: "user", triggerUsername: "user",
}; };
+5 -5
View File
@@ -70,7 +70,7 @@ describe("M7: Platform routing for mode handlers", () => {
it('returns "forgejo" when GITHUB_SERVER_URL is not github.com', () => { it('returns "forgejo" when GITHUB_SERVER_URL is not github.com', () => {
delete process.env.PLATFORM; delete process.env.PLATFORM;
delete process.env.FORGEJO; 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"); expect(detectPlatform()).toBe("forgejo");
}); });
@@ -139,7 +139,7 @@ describe("M7: Forgejo PR compare URL format", () => {
); );
const url = createForgejoPrCompareUrl( const url = createForgejoPrCompareUrl(
"https://git.wxs.ro", "https://forgejo.example.com",
"owner", "owner",
"repo", "repo",
"main", "main",
@@ -150,7 +150,7 @@ describe("M7: Forgejo PR compare URL format", () => {
expect(url).toContain("expand=1"); expect(url).toContain("expand=1");
expect(url).not.toContain("quick_pull=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"); expect(url).toContain("compare/main...claude/fix-bug");
}); });
}); });
@@ -167,10 +167,10 @@ describe("M7: unified updateCommentBody handles both platforms", () => {
const result = updateCommentBody({ const result = updateCommentBody({
currentBody: 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, actionFailed: false,
executionDetails: { duration_ms: 30000 }, 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", triggerUsername: "testuser",
}); });
+1 -1
View File
@@ -75,7 +75,7 @@ describe("detectPlatform", () => {
describe("GITHUB_SERVER_URL heuristic", () => { describe("GITHUB_SERVER_URL heuristic", () => {
it("should return 'forgejo' for non-github.com server URLs", () => { 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"); expect(detectPlatform()).toBe("forgejo");
}); });