|
| 1 | +name: Generated client guard |
| 2 | + |
| 3 | +# Fails a pull request that hand-edits the generated REST client in pkg/client. |
| 4 | +# |
| 5 | +# Why this shape, rather than regenerating and diffing (which is what |
| 6 | +# renderinc/sdk .github/workflows/generated-client-drift.yml does): |
| 7 | +# |
| 8 | +# Regenerating requires reading the private renderinc/api. This repo's |
| 9 | +# vars.APP_ID is app 2491349 (render-oss-copybara-sync), which has no |
| 10 | +# installation on renderinc/api, so a regen-and-diff check here could not |
| 11 | +# authenticate and would fail before it did any work. This check therefore |
| 12 | +# needs no access to api at all: no schema, no generator, no Go toolchain. |
| 13 | +# It is a checkout and a git diff, so it runs in seconds. |
| 14 | +# |
| 15 | +# That is sufficient because the regen automation in api keeps the committed |
| 16 | +# client current: the client cannot fall behind the schema without a PR being |
| 17 | +# opened here. So the question left for this repo to answer is not "is the |
| 18 | +# client current" but "did a human hand-edit generated code", and answering |
| 19 | +# that needs no schema. |
| 20 | + |
| 21 | +on: |
| 22 | + pull_request: |
| 23 | + branches: [main] |
| 24 | + |
| 25 | +concurrency: |
| 26 | + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} |
| 27 | + cancel-in-progress: true |
| 28 | + |
| 29 | +# contents:read is all this needs. It deliberately does not use |
| 30 | +# dorny/paths-filter, which reads the PR's changed files through the API and so |
| 31 | +# also needs pull-requests:read. |
| 32 | +permissions: |
| 33 | + contents: read |
| 34 | + |
| 35 | +jobs: |
| 36 | + guard: |
| 37 | + name: Check generated client files were not hand-edited |
| 38 | + runs-on: ubuntu-latest |
| 39 | + steps: |
| 40 | + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
| 41 | + with: |
| 42 | + # Full history so the PR's merge base resolves. |
| 43 | + fetch-depth: 0 |
| 44 | + |
| 45 | + - name: Check for hand-edited generated files |
| 46 | + env: |
| 47 | + PR_AUTHOR: ${{ github.event.pull_request.user.login }} |
| 48 | + BASE_SHA: ${{ github.event.pull_request.base.sha }} |
| 49 | + HEAD_SHA: ${{ github.event.pull_request.head.sha }} |
| 50 | + HAS_OVERRIDE: ${{ contains(github.event.pull_request.labels.*.name, 'manual-regen') }} |
| 51 | + run: | |
| 52 | + set -o errexit -o nounset -o pipefail |
| 53 | +
|
| 54 | + # The regen automation's PRs are expected to change generated files. |
| 55 | + # |
| 56 | + # Identified by author rather than by head branch (auto/regen-cli): |
| 57 | + # the head branch name is chosen by whoever opens the PR, so a human |
| 58 | + # could silently bypass this guard by naming their branch |
| 59 | + # auto/regen-cli. The author is set by GitHub from the identity that |
| 60 | + # opened the PR and cannot be spoofed by a contributor. The failure |
| 61 | + # modes also favour the author: if the automation's branch name |
| 62 | + # changes, an author check still passes its PRs, whereas a branch |
| 63 | + # check would start failing them. |
| 64 | + if [[ "$PR_AUTHOR" == "render-github-action-bot[bot]" ]]; then |
| 65 | + echo "PR authored by the regen automation ($PR_AUTHOR); generated client changes are expected." |
| 66 | + exit 0 |
| 67 | + fi |
| 68 | +
|
| 69 | + # Generated files are exactly *_gen.go: that is the suffix the |
| 70 | + # generator writes and the only thing clean_generated_go_files |
| 71 | + # deletes. pkg/client also holds handwritten code (client.go, |
| 72 | + # cursorparams.go, oauth/, version/), so scoping this to the whole |
| 73 | + # directory would block legitimate edits to those files. |
| 74 | + # |
| 75 | + # Three-dot diff, so generated changes that landed on main after this |
| 76 | + # PR branched are not attributed to this PR. |
| 77 | + all_changed="$(git diff --name-only "$BASE_SHA...$HEAD_SHA")" |
| 78 | + changed="$(printf '%s\n' "$all_changed" | grep -E '^pkg/client/.*_gen\.go$' || true)" |
| 79 | +
|
| 80 | + if [[ -z "$changed" ]]; then |
| 81 | + echo "No generated client files changed." |
| 82 | + exit 0 |
| 83 | + fi |
| 84 | +
|
| 85 | + if [[ "$HAS_OVERRIDE" == "true" ]]; then |
| 86 | + echo "Generated client files changed, allowed by the 'manual-regen' label:" |
| 87 | + printf '%s\n' "$changed" |
| 88 | + exit 0 |
| 89 | + fi |
| 90 | +
|
| 91 | + echo "::error::This PR changes generated client files. Regenerate them instead of editing them by hand." |
| 92 | + echo |
| 93 | + echo "Generated client files changed by this PR:" |
| 94 | + printf '%s\n' "$changed" | sed 's/^/ /' |
| 95 | + echo |
| 96 | + cat <<'MSG' |
| 97 | + These files are written by public-api-schema/generate-cli.sh in |
| 98 | + renderinc/api and are overwritten on its next run, so hand edits to |
| 99 | + them are lost. |
| 100 | +
|
| 101 | + To change them, change the OpenAPI schema in renderinc/api and |
| 102 | + regenerate: |
| 103 | +
|
| 104 | + export RENDER_API_PATH=/path/to/api |
| 105 | + cd "$RENDER_API_PATH/public-api-schema" && ./generate-cli.sh |
| 106 | +
|
| 107 | + If you ran the generator yourself and this PR is that catch-up regen, |
| 108 | + add the 'manual-regen' label to this PR. |
| 109 | + MSG |
| 110 | + exit 1 |
0 commit comments