Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,71 @@ on:
pull_request:
workflow_dispatch:

permissions:
contents: read
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
permissions: {}

jobs:

pre-commit:
runs-on: ubuntu-latest
permissions:
contents: read # for actions/checkout to fetch code
steps:

- uses: actions/checkout@v6
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: 'stable'
check-latest: true

- uses: pre-commit/action@v3.0.1
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1

actionlint:
runs-on: ubuntu-latest
permissions:
contents: read # for actions/checkout to fetch code
checks: write # for reviewdog github-pr-check reporter to report findings
steps:

- uses: actions/checkout@v6
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false

- uses: reviewdog/action-actionlint@v1
- uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1

golangci-lint:
runs-on: ubuntu-latest
permissions:
contents: read # for actions/checkout to fetch code
steps:

- uses: actions/checkout@v6
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: 'stable'
check-latest: true

- uses: golangci/golangci-lint-action@v9.2.1
- uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: latest
args: --verbose

test:
runs-on: ubuntu-latest
permissions:
contents: read # for actions/checkout to fetch code
steps:

- uses: actions/checkout@v6
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: 'stable'
check-latest: true
Expand Down
34 changes: 28 additions & 6 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
@@ -1,33 +1,51 @@
---
name: E2E Tests

# Trigger design (intentional — do not "fix" by running e2e on PR events):
#
# The e2e matrix is slow, so it must not run on every push to a PR. It runs
# once per PR, via manual workflow_dispatch by a maintainer, when the PR is
# ready to merge.
#
# To gate merging on that manual run, every PR event sets the "e2e" commit
# status to pending (set-pending job), and the dispatched run reports the
# result back (report-status job) so the merge check can pass.
#
# pull_request_target is only used because posting a commit status requires
# a write-capable GITHUB_TOKEN, also for fork PRs. Since that token is
# privileged, the set-pending job must never check out or execute PR code.

on:
workflow_dispatch:
pull_request_target:

permissions:
contents: read
statuses: write
permissions: {}

jobs:

set-pending:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
name: Set pending e2e status
permissions:
statuses: write # for gh api to set the commit status
steps:
- name: Set pending e2e status
run: |
gh api "repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }}" \
gh api "repos/${REPO}/statuses/${HEAD_SHA}" \
-f state="pending" \
-f context="e2e" \
-f description="E2E tests must be triggered manually via workflow_dispatch"
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}

e2e:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: read # for actions/checkout to fetch code
timeout-minutes: ${{ matrix.timeout || 5 }}

strategy:
Expand Down Expand Up @@ -64,9 +82,11 @@ jobs:

steps:

- uses: actions/checkout@v6
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: 'stable'
check-latest: true
Expand All @@ -82,6 +102,8 @@ jobs:
needs: [e2e]
runs-on: ubuntu-latest
name: Report e2e status
permissions:
statuses: write # for gh api to set the commit status
steps:
- name: Report e2e status
run: |
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
push:
branches: ['main']

permissions: {}

jobs:
publish:
runs-on: ubuntu-latest
Expand All @@ -15,22 +17,23 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false

- name: Setup Go
uses: actions/setup-go@v6
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: 'stable'
check-latest: true

- name: Install Cosign
uses: sigstore/cosign-installer@v4.1.2
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2

- name: Prepare Ko
uses: ko-build/setup-ko@v0.9
uses: ko-build/setup-ko@61b4d1d396f5b2e7d6bb6fefdce3dc38d1a13445 # v0.10

- name: Compute build metadata
env:
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
push:
tags: ['v*']

permissions: {}

jobs:
release:
runs-on: ubuntu-latest
Expand All @@ -15,22 +17,23 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false

- name: Setup Go
uses: actions/setup-go@v6
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: 'stable'
check-latest: true

- name: Install Cosign
uses: sigstore/cosign-installer@v4.1.2
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2

- name: Prepare Ko
uses: ko-build/setup-ko@v0.9
uses: ko-build/setup-ko@61b4d1d396f5b2e7d6bb6fefdce3dc38d1a13445 # v0.10

- name: Compute build metadata
env:
Expand Down
19 changes: 19 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ Tests use `httptest.NewServer` for real local HTTP servers — no mocks for the
- `newTestProxyWithRetries(t, mirrors, retries)` — same, but also sets `retryBaseDelay = 0` so retry tests run instantly.
- `newTestApp(pp)` — builds an Echo app with the same middleware stack as production (`RequestID` → error handler → `Recover` → `Cache` → `ForwardProxy`).

## E2E Tests in CI

E2e tests (`.github/workflows/e2e.yaml`) run a full distro container matrix and are
expensive, so they don't run on every push. They run once per pull request, triggered
manually by a maintainer before merging:

```bash
gh workflow run e2e.yaml --ref <branch>
```

or via the Actions UI ("E2E Tests" → "Run workflow").

Merge gating: each PR event sets a pending `e2e` commit status, and the dispatched run
reports the result back as the `e2e` status used as a merge check.

Security constraint of the implementation: the status-setting job runs under
`pull_request_target` with a privileged token and therefore never checks out or
executes PR code.

## External Tests

Tests in `proxy_test.go` that hit httpbin.org are skipped by default. Enable with:
Expand Down
Loading