1010permissions :
1111 contents : read
1212 actions : read
13- checks : write
14- pull-requests : write
1513
1614jobs :
1715 # ── Layer 1: Fast Gate ─────────────────────────────────────────────
@@ -80,10 +78,47 @@ jobs:
8078 python-version : ' 3.x'
8179 - name : Fetch meta data
8280 run : python3 scripts/fetch_meta.py
81+ - name : Resolve changed-from baseline
82+ env :
83+ QUALITY_GATE_CHANGED_FROM : ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
84+ run : echo "QUALITY_GATE_CHANGED_FROM=$(bash scripts/resolve-changed-from.sh)" >> "$GITHUB_ENV"
8385 - name : Run golangci-lint
84- run : go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main
86+ run : go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev="$QUALITY_GATE_CHANGED_FROM"
8587 - name : Run errs/ lint guards (lintcheck)
86- run : go run -C lint . ..
88+ run : go run -C lint . --changed-from "$QUALITY_GATE_CHANGED_FROM" ..
89+
90+ deterministic-gate :
91+ needs : fast-gate
92+ runs-on : ubuntu-latest
93+ permissions :
94+ contents : read
95+ actions : read
96+ steps :
97+ - uses : actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
98+ with :
99+ fetch-depth : 0
100+ - uses : actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
101+ with :
102+ go-version-file : go.mod
103+ - uses : actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
104+ with :
105+ python-version : ' 3.x'
106+ - name : Fetch meta data
107+ run : python3 scripts/fetch_meta.py
108+ - name : Resolve changed-from baseline
109+ env :
110+ QUALITY_GATE_CHANGED_FROM : ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
111+ run : echo "QUALITY_GATE_CHANGED_FROM=$(bash scripts/resolve-changed-from.sh)" >> "$GITHUB_ENV"
112+ - name : Run CLI deterministic gate
113+ run : make quality-gate
114+ - name : Upload quality gate facts
115+ if : ${{ always() && github.event_name == 'pull_request' }}
116+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
117+ with :
118+ name : quality-gate-facts-${{ github.event.pull_request.base.sha }}-${{ github.event.pull_request.head.sha }}
119+ path : .tmp/quality-gate/facts.json
120+ if-no-files-found : error
121+ retention-days : 7
87122
88123 coverage :
89124 needs : fast-gate
@@ -103,6 +138,7 @@ jobs:
103138 packages=$(go list ./... | grep -v '^github.com/larksuite/cli/tests/cli_e2e$' | grep -v '^github.com/larksuite/cli/tests/cli_e2e/')
104139 go test -race -coverprofile=coverage.txt -covermode=atomic $packages
105140 - name : Upload coverage to Codecov
141+ if : ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
106142 uses : codecov/codecov-action@3f20e214133d0983f9a10f3d63b0faf9241a3daa # v6
107143 with :
108144 files : coverage.txt
@@ -184,7 +220,7 @@ jobs:
184220
185221 # ── Layer 3: E2E Gate ──────────────────────────────────────────────
186222 e2e-dry-run :
187- needs : [unit-test, lint]
223+ needs : [unit-test, lint, deterministic-gate ]
188224 runs-on : ubuntu-latest
189225 steps :
190226 - uses : actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
@@ -205,9 +241,12 @@ jobs:
205241 run : go test -v -count=1 -timeout=5m ./tests/cli_e2e/... -run 'DryRun|Regression'
206242
207243 e2e-live :
208- needs : [unit-test, lint]
244+ needs : [unit-test, lint, deterministic-gate ]
209245 if : ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
210246 runs-on : ubuntu-latest
247+ permissions :
248+ contents : read
249+ checks : write
211250 env :
212251 TEST_BOT1_APP_ID : ${{ secrets.TEST_BOT1_APP_ID }}
213252 TEST_BOT1_APP_SECRET : ${{ secrets.TEST_BOT1_APP_SECRET }}
@@ -254,6 +293,9 @@ jobs:
254293 # ── Layer 4: Security & Compliance (parallel with L2-L3) ──────────
255294 security :
256295 runs-on : ubuntu-latest
296+ permissions :
297+ contents : read
298+ pull-requests : read
257299 steps :
258300 - uses : actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
259301 with :
@@ -291,7 +333,7 @@ jobs:
291333 # ── Results Gate (single required check for branch protection) ─────
292334 results :
293335 if : ${{ always() }}
294- needs : [fast-gate, unit-test, lint, coverage, deadcode, e2e-dry-run, e2e-live, security, license-header]
336+ needs : [fast-gate, unit-test, lint, deterministic-gate, coverage, deadcode, e2e-dry-run, e2e-live, security, license-header]
295337 runs-on : ubuntu-latest
296338 steps :
297339 - name : Evaluate results
@@ -303,6 +345,7 @@ jobs:
303345 echo "| L1 | fast-gate | ${{ needs.fast-gate.result }} |" >> $GITHUB_STEP_SUMMARY
304346 echo "| L2 | unit-test | ${{ needs.unit-test.result }} |" >> $GITHUB_STEP_SUMMARY
305347 echo "| L2 | lint | ${{ needs.lint.result }} |" >> $GITHUB_STEP_SUMMARY
348+ echo "| L2 | deterministic-gate | ${{ needs.deterministic-gate.result }} |" >> $GITHUB_STEP_SUMMARY
306349 echo "| L2 | coverage | ${{ needs.coverage.result }} |" >> $GITHUB_STEP_SUMMARY
307350 echo "| L2 | deadcode | ${{ needs.deadcode.result }} |" >> $GITHUB_STEP_SUMMARY
308351 echo "| L3 | e2e-dry-run | ${{ needs.e2e-dry-run.result }} |" >> $GITHUB_STEP_SUMMARY
@@ -318,6 +361,7 @@ jobs:
318361 "${{ needs.fast-gate.result }}" \
319362 "${{ needs.unit-test.result }}" \
320363 "${{ needs.lint.result }}" \
364+ "${{ needs.deterministic-gate.result }}" \
321365 "${{ needs.coverage.result }}" \
322366 "${{ needs.deadcode.result }}" \
323367 "${{ needs.e2e-dry-run.result }}" \
0 commit comments