The downstream job in test-provider-ci.yml is the only thing in our pipeline that validates a template change against real provider CI. It currently reports success while skipping every verification step.
Across the last 12 test-provider-ci runs, 24 of 24 downstream jobs skipped all verification and reported green.
Mechanism
.github/workflows/test-provider-ci.yml:78-87:
- name: Await PR opened for ${{ matrix.repo }}
id: pr_opened
timeout-minutes: 1
continue-on-error: true # Fall through if a PR wasn't created.
run: |
until gh search prs --repo ${{ matrix.repo }} --match body "...from commit ${{ github.sha }}." --json url | grep url; do sleep 30; done;
echo "pull_request_created=true" >> "${GITHUB_OUTPUT}"
gh search prs queries the search index, which lags minutes behind for a just-created PR. The PRs REST API does not.
timeout-minutes: 1 kills the step before the index catches up.
- The line setting
pull_request_created=true sits after the loop, so it never executes.
continue-on-error: true turns the timeout into a green step.
- The eleven following steps are gated on
if: steps.pr_opened.outputs.pull_request_created == 'true' and all skip.
Concretely, on #2408 the deploy job logged Created pull request #1713 and pull-request-operation = created, and the gate then logged The action 'Await PR opened for pulumi/pulumi-provider-boilerplate' has timed out after 1 minutes. That PR was never checked.
Why the workaround exists
#926: with no diff, no PR is opened and the until loop hangs forever. The timeout and continue-on-error were added to unblock that. The problem is they make two cases indistinguishable:
| Situation |
Should |
Does |
| No PR created (no diff) |
pass quietly |
pass |
| PR created, not yet indexed |
wait, then gate on checks |
pass |
A fix must keep #926 working, so continue-on-error cannot simply be deleted.
Suggested fix
Find the PR by head branch instead of by search. The branch name is deterministic and known to both jobs (update-workflows.yml:133): update-github-actions-workflows-${{ github.run_number }}.
branch="update-github-actions-workflows-${{ github.run_number }}"
number=$(gh pr list --repo "${{ matrix.repo }}" --head "$branch" --json number --jq '.[0].number // empty')
gh pr list uses the PRs REST API, which is read-your-writes consistent, so an empty result genuinely means no PR was created rather than "the index is slow". Then drop continue-on-error and branch explicitly: empty means skip, non-empty means gate on checks and fail on failure.
Note update-workflows.yml:32-35 already exposes a pull_request_created workflow_call output that the downstream job does not consume. It is not directly usable as-is because deploy is a matrix job and Actions does not provide per-leg outputs, but it is an alternative if deploy is split.
Separately, timeout-minutes: 10 on Await required checks succeed is too short once the gate actually fires. Provider CI on these repos runs 20-30 minutes.
The required-checks design itself is sound and should not be changed: sentinel is a real aggregator (needs: [test, prerequisites, lint], test needs build_sdks), so a build_sdks failure correctly prevents the Sentinel status from posting.
Verifying a fix
The hard part is proving a gate that is supposed to fail. Worth testing both directions: a deliberately broken native template must turn downstream red, a benign change must pass, and a no-diff change must skip quickly without hanging (#926). The clearest signal is whether Find PR number and Await required checks succeed actually run rather than showing skipped.
The
downstreamjob intest-provider-ci.ymlis the only thing in our pipeline that validates a template change against real provider CI. It currently reports success while skipping every verification step.Across the last 12
test-provider-ciruns, 24 of 24 downstream jobs skipped all verification and reported green.Mechanism
.github/workflows/test-provider-ci.yml:78-87:gh search prsqueries the search index, which lags minutes behind for a just-created PR. The PRs REST API does not.timeout-minutes: 1kills the step before the index catches up.pull_request_created=truesits after the loop, so it never executes.continue-on-error: trueturns the timeout into a green step.if: steps.pr_opened.outputs.pull_request_created == 'true'and all skip.Concretely, on #2408 the deploy job logged
Created pull request #1713andpull-request-operation = created, and the gate then loggedThe action 'Await PR opened for pulumi/pulumi-provider-boilerplate' has timed out after 1 minutes.That PR was never checked.Why the workaround exists
#926: with no diff, no PR is opened and the
untilloop hangs forever. The timeout andcontinue-on-errorwere added to unblock that. The problem is they make two cases indistinguishable:A fix must keep #926 working, so
continue-on-errorcannot simply be deleted.Suggested fix
Find the PR by head branch instead of by search. The branch name is deterministic and known to both jobs (
update-workflows.yml:133):update-github-actions-workflows-${{ github.run_number }}.gh pr listuses the PRs REST API, which is read-your-writes consistent, so an empty result genuinely means no PR was created rather than "the index is slow". Then dropcontinue-on-errorand branch explicitly: empty means skip, non-empty means gate on checks and fail on failure.Note
update-workflows.yml:32-35already exposes apull_request_createdworkflow_call output that the downstream job does not consume. It is not directly usable as-is becausedeployis a matrix job and Actions does not provide per-leg outputs, but it is an alternative ifdeployis split.Separately,
timeout-minutes: 10onAwait required checks succeedis too short once the gate actually fires. Provider CI on these repos runs 20-30 minutes.The required-checks design itself is sound and should not be changed:
sentinelis a real aggregator (needs: [test, prerequisites, lint],testneedsbuild_sdks), so abuild_sdksfailure correctly prevents theSentinelstatus from posting.Verifying a fix
The hard part is proving a gate that is supposed to fail. Worth testing both directions: a deliberately broken native template must turn
downstreamred, a benign change must pass, and a no-diff change must skip quickly without hanging (#926). The clearest signal is whetherFind PR numberandAwait required checks succeedactually run rather than showingskipped.