diff --git a/.github/workflows/todos.yaml b/.github/workflows/todos.yaml index 0c55f334c4..0a38895875 100644 --- a/.github/workflows/todos.yaml +++ b/.github/workflows/todos.yaml @@ -11,6 +11,98 @@ permissions: jobs: todos: - uses: devantler-tech/actions/.github/workflows/scan-for-todo-comments.yaml@0de1d284f879d62d6fe316d3997fd3ea8f092fb3 # v13.2.1 - secrets: - APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} + runs-on: ubuntu-latest + steps: + - name: 🛡️ Harden runner + uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + with: + egress-policy: audit + + - name: 📑 Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + # The shared workflow does not expose the scanner's ignore input. Keep + # its retry helper pinned here while the KSail-owned job applies the + # repository-specific third-party filter below. + - name: 📥 Checkout devantler-tech/actions + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: devantler-tech/actions + ref: 0de1d284f879d62d6fe316d3997fd3ea8f092fb3 # v13.2.1 + path: .devantler-tech-actions + persist-credentials: false + + - name: 🔑 Generate GitHub App token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + id: app-token + with: + client-id: ${{ vars.APP_CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + permission-organization-projects: write + + - name: 🧰 Preserve retry helper + shell: bash + run: | + retry_helper="${RUNNER_TEMP}/devantler-actions-retry.sh" + cp "${GITHUB_WORKSPACE}/.devantler-tech-actions/.scripts/retry.sh" "$retry_helper" + chmod +x "$retry_helper" + + # Re-sync after copying the helper so the scanner cannot inspect the + # checked-out actions repository as part of KSail's own sources. + - name: 📑 Re-sync KSail sources + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: 📝 Create issues from TODOs + shell: bash + env: + INPUT_REPO: ${{ github.repository }} + INPUT_BEFORE: ${{ github.event.before || github.base_ref }} + INPUT_COMMITS: ${{ toJSON(github.event.commits) }} + INPUT_DIFF_URL: ${{ github.event.pull_request.diff_url }} + INPUT_SHA: ${{ github.sha }} + INPUT_TOKEN: ${{ github.token }} + INPUT_CLOSE_ISSUES: "true" + INPUT_AUTO_P: "true" + INPUT_PROJECT: organization/devantler-tech/5 + INPUT_PROJECTS_SECRET: ${{ steps.app-token.outputs.token }} + INPUT_AUTO_ASSIGN: "true" + INPUT_ACTOR: ${{ github.actor }} + INPUT_GITHUB_URL: ${{ github.api_url }} + INPUT_GITHUB_SERVER_URL: ${{ github.server_url }} + INPUT_ESCAPE: "true" + INPUT_NO_STANDARD: "false" + INPUT_INSERT_ISSUE_URLS: "false" + INPUT_IGNORE: "^third_party/" + TODO_TO_ISSUE_IMAGE: ghcr.io/alstr/todo-to-issue-action:v5.1.15@sha256:a1794cab7e7a0306d6f74dd5249734f2c0b49e47d7549c6cdca4421bced67c9d + run: | + retry() { bash "${RUNNER_TEMP}/devantler-actions-retry.sh" "$@"; } + + retry docker run --rm \ + --workdir /github/workspace \ + --volume "$GITHUB_WORKSPACE:/github/workspace" \ + --env GITHUB_ACTIONS=true \ + --env GITHUB_WORKSPACE=/github/workspace \ + --env CI=true \ + --env INPUT_REPO \ + --env INPUT_BEFORE \ + --env INPUT_COMMITS \ + --env INPUT_DIFF_URL \ + --env INPUT_SHA \ + --env INPUT_TOKEN \ + --env INPUT_CLOSE_ISSUES \ + --env INPUT_AUTO_P \ + --env INPUT_PROJECT \ + --env INPUT_PROJECTS_SECRET \ + --env INPUT_AUTO_ASSIGN \ + --env INPUT_ACTOR \ + --env INPUT_GITHUB_URL \ + --env INPUT_GITHUB_SERVER_URL \ + --env INPUT_ESCAPE \ + --env INPUT_NO_STANDARD \ + --env INPUT_INSERT_ISSUE_URLS \ + --env INPUT_IGNORE \ + "$TODO_TO_ISSUE_IMAGE" diff --git a/internal/ciharness/todos_workflow_test.go b/internal/ciharness/todos_workflow_test.go new file mode 100644 index 0000000000..6ba43445db --- /dev/null +++ b/internal/ciharness/todos_workflow_test.go @@ -0,0 +1,71 @@ +package ciharness_test + +import ( + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +// todosWorkflow captures the local job surface that delivers scanner inputs. +// +//nolint:tagliatelle // GitHub Actions defines these external keys in kebab-case. +type todosWorkflow struct { + Jobs map[string]struct { + Uses string `yaml:"uses"` + RunsOn string `yaml:"runs-on"` + Steps []struct { + Env map[string]string `yaml:"env"` + Run string `yaml:"run"` + } `yaml:"steps"` + } `yaml:"jobs"` +} + +func TestTODOScannerExcludesOnlyVendoredSources(t *testing.T) { + t.Parallel() + + contents := readRepoFile(t, ".github/workflows/todos.yaml") + + var workflow todosWorkflow + require.NoError(t, yaml.Unmarshal(contents, &workflow)) + + job, found := workflow.Jobs["todos"] + require.True(t, found, "TODO workflow must define the todos job") + require.Empty(t, job.Uses, "TODO scanner inputs must be delivered by the KSail-owned job") + require.Equal(t, "ubuntu-latest", job.RunsOn) + + var ( + scanEnv map[string]string + scanRun string + ) + + for _, step := range job.Steps { + if _, configured := step.Env["INPUT_IGNORE"]; configured { + scanEnv = step.Env + scanRun = step.Run + + break + } + } + + require.NotNil(t, scanEnv, "scanner step must configure INPUT_IGNORE") + + ignorePattern := scanEnv["INPUT_IGNORE"] + assert.Equal(t, `^third_party/`, ignorePattern) + + compiled, err := regexp.Compile(ignorePattern) + require.NoError(t, err) + assert.True(t, compiled.MatchString("third_party/go-archive/archive/tar/reader.go")) + assert.False(t, compiled.MatchString("internal/maintenance/todos.go")) + + assert.Contains(t, strings.Join(strings.Fields(scanRun), " "), "--env INPUT_IGNORE") + assert.Equal( + t, + "ghcr.io/alstr/todo-to-issue-action:v5.1.15@sha256:"+ + "a1794cab7e7a0306d6f74dd5249734f2c0b49e47d7549c6cdca4421bced67c9d", + scanEnv["TODO_TO_ISSUE_IMAGE"], + ) +}