mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
fix(ci): qualify grouped release action pin consumers
The grouped action upgrade leaves signing, network and publication consumer assertions on superseded pins. Align those contracts and check every consumer against reviewed immutable upstream manifests, retaining exact dispatch and release trust boundaries without claiming hosted execution. Change-source: pulse-maintainer
This commit is contained in:
@@ -462,7 +462,7 @@ jobs:
|
||||
- name: Submit SignPath Authenticode request
|
||||
if: ${{ inputs.windows_signing_backend == 'signpath' }}
|
||||
id: signpath
|
||||
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2
|
||||
uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
|
||||
@@ -719,7 +719,7 @@ jobs:
|
||||
sudo apt-get install -y zip unzip
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: 'v3.15.2'
|
||||
|
||||
@@ -937,7 +937,7 @@ jobs:
|
||||
# downloaded asset without access to GitHub's attestation API.
|
||||
- name: Attest complete release candidate
|
||||
id: attest_release_candidate
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-checksums: ${{ runner.temp }}/release-candidate-subjects.sha256
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
|
||||
- name: Attest hosted qualification subjects
|
||||
id: attest_secure_runtime
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-checksums: ${{ runner.temp }}/secure-runtime-qualification/secure-runtime-compiler-subjects.sha256
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: v3.15.2
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
python3 scripts/write_github_output.py release_tag "${release_tag}"
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: v3.15.2
|
||||
|
||||
|
||||
@@ -96,13 +96,13 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
||||
@@ -118,19 +118,19 @@ jobs:
|
||||
--source-sha "${EXPECTED_SOURCE_SHA}"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
- name: Assemble and push Pulse server image (multi-arch)
|
||||
if: matrix.image == 'server'
|
||||
id: build_server_image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
target: runtime_prebuilt
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
|
||||
- name: Attest Pulse server image on Docker Hub
|
||||
if: matrix.image == 'server'
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: docker.io/rcourtman/pulse
|
||||
subject-digest: ${{ steps.build_server_image.outputs.digest }}
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
|
||||
- name: Attest Pulse server image on GHCR
|
||||
if: matrix.image == 'server'
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: ghcr.io/${{ github.repository_owner }}/pulse
|
||||
subject-digest: ${{ steps.build_server_image.outputs.digest }}
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
- name: Assemble and push Pulse control-plane image (multi-arch)
|
||||
if: matrix.image == 'control-plane'
|
||||
id: build_control_plane_image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
file: deploy/provider-msp/Dockerfile.control-plane
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
|
||||
- name: Attest Pulse control-plane image on Docker Hub
|
||||
if: matrix.image == 'control-plane'
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: docker.io/rcourtman/pulse-control-plane
|
||||
subject-digest: ${{ steps.build_control_plane_image.outputs.digest }}
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
|
||||
- name: Attest Pulse control-plane image on GHCR
|
||||
if: matrix.image == 'control-plane'
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: ghcr.io/${{ github.repository_owner }}/pulse-control-plane
|
||||
subject-digest: ${{ steps.build_control_plane_image.outputs.digest }}
|
||||
@@ -249,16 +249,16 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
echo "is_prerelease=$IS_PRERELEASE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: v3.15.2
|
||||
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
# chart push authenticated through Helm, and authenticate the OCI
|
||||
# attestation client explicitly before it publishes provenance.
|
||||
- name: Authenticate OCI attestation client with GHCR
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -203,7 +203,7 @@ jobs:
|
||||
echo "chart_digest=${digests[0]}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Attest exact-version OCI chart
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: ghcr.io/${{ github.repository_owner }}/pulse-chart/pulse
|
||||
subject-digest: ${{ steps.push.outputs.chart_digest }}
|
||||
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
'
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: v3.15.2
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
|
||||
- name: Connect to the governed demo network
|
||||
id: tailscale
|
||||
uses: tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4
|
||||
uses: tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3
|
||||
with:
|
||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||
|
||||
@@ -179,7 +179,7 @@ jobs:
|
||||
echo "[OK] GitHub's stored RC packet still matches the immutable source-run manifest."
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: v3.15.2
|
||||
|
||||
@@ -196,16 +196,16 @@ jobs:
|
||||
"${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
||||
@@ -44,13 +44,13 @@ jobs:
|
||||
|
||||
- name: Set up Helm
|
||||
if: ${{ github.event.schedule != '17 */6 * * *' }}
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: v3.15.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: ${{ github.event.schedule != '17 */6 * * *' }}
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
|
||||
- name: Resolve the advertised stable release
|
||||
id: release
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Submit SignPath test-signing request
|
||||
id: signpath
|
||||
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2
|
||||
uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
|
||||
|
||||
@@ -363,7 +363,7 @@ jobs:
|
||||
|
||||
- name: Tailscale
|
||||
id: tailscale
|
||||
uses: tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4
|
||||
uses: tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3
|
||||
with:
|
||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||
|
||||
@@ -5407,3 +5407,22 @@ Retained provider delivery verification establishes archive SHA256
|
||||
a valid pinned SSH signature and the extracted request expression with absent
|
||||
and synthetic email. It does not establish installed onboarding, server acceptance
|
||||
or legal identity. See the qualification evidence below.
|
||||
|
||||
### Pinned release action consumer compatibility
|
||||
|
||||
The grouped release actions use immutable revisions recorded in
|
||||
`scripts/release_control/action_consumer_manifests.json`: Attest 4.2.2,
|
||||
Helm setup 5.0.1, Docker build/push 7.3.0, login 4.6.0, Buildx/QEMU
|
||||
setup 4.3.0, SignPath v2 and Tailscale 4.1.3. The snapshot records the
|
||||
upstream action manifest hash, accepted inputs/outputs and Node 24 entry points.
|
||||
It is reviewed input, not runtime attestation or permission to publish.
|
||||
|
||||
`test_reviewed_action_manifests_cover_all_release_consumers` checks every
|
||||
workflow consumer against those upstream inputs and immutable revisions.
|
||||
Helm remains 3.15.2; attestation keeps checksum subjects; signing keeps the
|
||||
existing organisation/token interface. Existing signing, checksum, Docker
|
||||
promotion and exact-dispatch contract tests remain required. Action upgrades
|
||||
must not alter permissions, environments, source identity, rollback, signing
|
||||
backend selection or release qualification. Local contract success does not
|
||||
establish hosted action execution, signature acceptance, image publication or
|
||||
production deployment.
|
||||
|
||||
@@ -157,7 +157,7 @@ func TestSecurityScanRevalidatesLatestStableDelivery(t *testing.T) {
|
||||
`github.event_name != 'workflow_run' || !contains(github.event.workflow_run.display_title, '-')`,
|
||||
"release-continuity:",
|
||||
"Latest stable release continuity",
|
||||
"docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5",
|
||||
"docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e",
|
||||
`"repos/${REPOSITORY}/releases/latest"`,
|
||||
`scripts/release_control/release_continuity.py release`,
|
||||
"release-diagnostic.json",
|
||||
@@ -1747,7 +1747,7 @@ func TestReleaseCandidateRequiresPlatformNativeAgentSigning(t *testing.T) {
|
||||
`sign-windows-agent:`,
|
||||
`collect-windows-signing:`,
|
||||
`windows_signing_backend:`,
|
||||
`signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2`,
|
||||
`signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2`,
|
||||
`github-artifact-id: ${{ steps.upload-unsigned-windows.outputs.artifact-id }}`,
|
||||
`wait-for-completion: false`,
|
||||
`windows-signing-request.json`,
|
||||
@@ -1852,7 +1852,7 @@ func TestReleaseWorkflowsUseSecretSafeAttestedImageBuilds(t *testing.T) {
|
||||
`does not trust the configured release signing key.`,
|
||||
`id-token: write`,
|
||||
`attestations: write`,
|
||||
`uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4`,
|
||||
`uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2`,
|
||||
}
|
||||
containerJob := workflowJobBlock(t, string(qualifierWorkflowBytes), "qualify")
|
||||
for _, needle := range []string{
|
||||
@@ -2272,7 +2272,7 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
|
||||
workflow := string(workflowBytes) + "\n" + string(profileBytes)
|
||||
required := []string{
|
||||
`- name: Tailscale`,
|
||||
`uses: tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4`,
|
||||
`uses: tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3`,
|
||||
`oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}`,
|
||||
`oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}`,
|
||||
`tags: tag:infra`,
|
||||
@@ -3189,7 +3189,7 @@ func TestPublishHelmChartReachableViaWorkflowCall(t *testing.T) {
|
||||
`name: Verify public GHCR chart identity and provenance`,
|
||||
`helm registry logout ghcr.io || true`,
|
||||
`name: Authenticate OCI attestation client with GHCR`,
|
||||
`uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0`,
|
||||
`uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0`,
|
||||
`registry: ghcr.io`,
|
||||
`username: ${{ github.actor }}`,
|
||||
`password: ${{ github.token }}`,
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
{
|
||||
"actions/attest": {
|
||||
"sha": "1e69f48acb82d1966a394da916b4c1698aa569d6",
|
||||
"manifest_sha256": "9e4a1b808433f9ec87120b534e11fc35469a039bdbdc62b019441444c9ad0449",
|
||||
"inputs": [
|
||||
"create-storage-record",
|
||||
"github-token",
|
||||
"predicate",
|
||||
"predicate-path",
|
||||
"predicate-type",
|
||||
"push-to-registry",
|
||||
"sbom-path",
|
||||
"show-summary",
|
||||
"subject-checksums",
|
||||
"subject-digest",
|
||||
"subject-name",
|
||||
"subject-path",
|
||||
"subject-version"
|
||||
],
|
||||
"outputs": [
|
||||
"attestation-id",
|
||||
"attestation-url",
|
||||
"bundle-path",
|
||||
"storage-record-ids"
|
||||
],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"azure/setup-helm": {
|
||||
"sha": "9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310",
|
||||
"manifest_sha256": "a80a29bbe87c77663a47a75d4163cf54f0bd24ae2485b868dd81d6758ec29647",
|
||||
"inputs": [
|
||||
"downloadBaseURL",
|
||||
"token",
|
||||
"version"
|
||||
],
|
||||
"outputs": [
|
||||
"helm-path"
|
||||
],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "lib/index.js"
|
||||
}
|
||||
},
|
||||
"docker/build-push-action": {
|
||||
"sha": "53b7df96c91f9c12dcc8a07bcb9ccacbed38856a",
|
||||
"manifest_sha256": "5c1ade75252eabe541b96cc39a2165a07c8850f9bcd3f9b75f5394fbdfb215fd",
|
||||
"inputs": [
|
||||
"add-hosts",
|
||||
"allow",
|
||||
"annotations",
|
||||
"attests",
|
||||
"build-args",
|
||||
"build-contexts",
|
||||
"builder",
|
||||
"cache-from",
|
||||
"cache-to",
|
||||
"call",
|
||||
"cgroup-parent",
|
||||
"context",
|
||||
"file",
|
||||
"github-token",
|
||||
"labels",
|
||||
"load",
|
||||
"network",
|
||||
"no-cache",
|
||||
"no-cache-filters",
|
||||
"outputs",
|
||||
"platforms",
|
||||
"provenance",
|
||||
"pull",
|
||||
"push",
|
||||
"sbom",
|
||||
"secret-envs",
|
||||
"secret-files",
|
||||
"secrets",
|
||||
"shm-size",
|
||||
"ssh",
|
||||
"tags",
|
||||
"target",
|
||||
"ulimit"
|
||||
],
|
||||
"outputs": [
|
||||
"digest",
|
||||
"imageid",
|
||||
"metadata"
|
||||
],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "dist/index.cjs",
|
||||
"post": "dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"docker/login-action": {
|
||||
"sha": "dbcb813823bdd20940b903addbd779551569679f",
|
||||
"manifest_sha256": "e65e6bdc57fc10adcee036a393229aae6b9a9f451b69528f4ff50d4b0e93f156",
|
||||
"inputs": [
|
||||
"ecr",
|
||||
"logout",
|
||||
"password",
|
||||
"registry",
|
||||
"registry-auth",
|
||||
"scope",
|
||||
"username"
|
||||
],
|
||||
"outputs": [],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "dist/index.cjs",
|
||||
"post": "dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"docker/setup-buildx-action": {
|
||||
"sha": "37fe631027851001ddb9b187196cc803df7f5f0e",
|
||||
"manifest_sha256": "9c55229011a0566758962f36c74309c8ab06f8905a5997591a6a46319401ab38",
|
||||
"inputs": [
|
||||
"append",
|
||||
"buildkitd-config",
|
||||
"buildkitd-config-inline",
|
||||
"buildkitd-flags",
|
||||
"cache-binary",
|
||||
"cleanup",
|
||||
"driver",
|
||||
"driver-opts",
|
||||
"endpoint",
|
||||
"keep-state",
|
||||
"name",
|
||||
"platforms",
|
||||
"use",
|
||||
"version"
|
||||
],
|
||||
"outputs": [
|
||||
"driver",
|
||||
"endpoint",
|
||||
"flags",
|
||||
"name",
|
||||
"nodes",
|
||||
"platforms",
|
||||
"status"
|
||||
],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "dist/index.cjs",
|
||||
"post": "dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"docker/setup-qemu-action": {
|
||||
"sha": "1f40c72289eff860ee54a304f1438e3cff362e0a",
|
||||
"manifest_sha256": "9c74c218c37b889976b0af59861f3fceaec87bd12da00bf6dc37ded77d8954f8",
|
||||
"inputs": [
|
||||
"cache-image",
|
||||
"image",
|
||||
"platforms",
|
||||
"reset"
|
||||
],
|
||||
"outputs": [
|
||||
"platforms"
|
||||
],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "dist/index.cjs",
|
||||
"post": "dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"signpath/github-action-submit-signing-request": {
|
||||
"sha": "c92b958760219087e01f8d67a1669ed57afe2627",
|
||||
"manifest_sha256": "d0b52fdfa234d87c1ff4d21c06a91d2b6e0dd45e1a8866ad3b58cbdeb9aef326",
|
||||
"inputs": [
|
||||
"api-token",
|
||||
"artifact-configuration-slug",
|
||||
"connector-url",
|
||||
"download-signed-artifact-timeout-in-seconds",
|
||||
"github-artifact-id",
|
||||
"github-token",
|
||||
"organization-id",
|
||||
"output-artifact-directory",
|
||||
"parameters",
|
||||
"project-slug",
|
||||
"service-unavailable-timeout-in-seconds",
|
||||
"signing-policy-slug",
|
||||
"skip-decompress",
|
||||
"wait-for-completion",
|
||||
"wait-for-completion-timeout-in-seconds"
|
||||
],
|
||||
"outputs": [
|
||||
"signed-artifact-download-url",
|
||||
"signing-request-id",
|
||||
"signing-request-web-url",
|
||||
"signpath-api-url"
|
||||
],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "index.js"
|
||||
}
|
||||
},
|
||||
"tailscale/github-action": {
|
||||
"sha": "780049a30b6ff5c378a9e7b389d15ece7a204888",
|
||||
"manifest_sha256": "d63e612f47dad1520586efec4b79f2c7f0e0aadabeeca53709aa616202e43c6c",
|
||||
"inputs": [
|
||||
"args",
|
||||
"audience",
|
||||
"authkey",
|
||||
"hostname",
|
||||
"oauth-client-id",
|
||||
"oauth-secret",
|
||||
"ping",
|
||||
"retry",
|
||||
"sha256sum",
|
||||
"statedir",
|
||||
"tags",
|
||||
"tailscaled-args",
|
||||
"timeout",
|
||||
"use-cache",
|
||||
"version"
|
||||
],
|
||||
"outputs": [],
|
||||
"runs": {
|
||||
"using": "node24",
|
||||
"main": "dist/index.js",
|
||||
"post": "dist/logout/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,37 @@ from release_promotion_policy_support import (
|
||||
|
||||
|
||||
class ReleasePromotionPolicySupportTest(unittest.TestCase):
|
||||
def test_action_inputs_do_not_satisfy_dispatch_requirements(self) -> None:
|
||||
# Signing action inputs are nested consumer data, not callable workflow
|
||||
# inputs. A pin upgrade must not make this dispatch guard accept them.
|
||||
content = """
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
type: string
|
||||
jobs:
|
||||
sign:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627
|
||||
with:
|
||||
organization-id: fixture
|
||||
api-token: fixture
|
||||
expected_head_sha: fixture
|
||||
"""
|
||||
self.assertEqual(parse_workflow_dispatch_inputs(content), ("version",))
|
||||
with patch("release_promotion_policy_support.branch_workflow_text", return_value=content), patch(
|
||||
"release_promotion_policy_support.origin_default_branch", return_value="main"
|
||||
):
|
||||
self.assertEqual(
|
||||
missing_workflow_dispatch_inputs(
|
||||
workflow_path=".github/workflows/signpath-test-signing.yml",
|
||||
required_inputs=("version", "expected_head_sha"),
|
||||
),
|
||||
("main", ("expected_head_sha",)),
|
||||
)
|
||||
|
||||
def test_parse_workflow_dispatch_inputs_reads_top_level_inputs(self) -> None:
|
||||
content = """
|
||||
name: Example
|
||||
|
||||
@@ -302,6 +302,40 @@ STAGED_GOVERNANCE_INPUT_ERRORS = (
|
||||
|
||||
|
||||
class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
|
||||
def test_reviewed_action_manifests_cover_all_release_consumers(self) -> None:
|
||||
# Snapshot is derived from each immutable upstream action.yml, not from
|
||||
# our consumers: unknown inputs therefore fail rather than being blessed.
|
||||
manifests = read_json("scripts/release_control/action_consumer_manifests.json")
|
||||
seen = set()
|
||||
for path in (REPO_ROOT / ".github/workflows").glob("*.yml"):
|
||||
workflow = yaml.safe_load(read(str(path.relative_to(REPO_ROOT))))
|
||||
for job in workflow.get("jobs", {}).values():
|
||||
for step in job.get("steps", []):
|
||||
action, _, revision = step.get("uses", "").partition("@")
|
||||
if action not in manifests:
|
||||
continue
|
||||
with self.subTest(workflow=path.name, action=action):
|
||||
manifest = manifests[action]
|
||||
self.assertEqual(revision, manifest["sha"])
|
||||
self.assertEqual(manifest["runs"]["using"], "node24")
|
||||
self.assertRegex(manifest["manifest_sha256"], r"^[0-9a-f]{64}$")
|
||||
self.assertLessEqual(set(step.get("with", {})), set(manifest["inputs"]))
|
||||
seen.add(action)
|
||||
if action == "azure/setup-helm":
|
||||
self.assertEqual(str(step["with"]["version"]).lstrip("v"), "3.15.2")
|
||||
if action == "actions/attest":
|
||||
if path.name in {"publish-docker.yml", "publish-helm-chart.yml"}:
|
||||
self.assertIn("subject-name", step["with"])
|
||||
self.assertIn("subject-digest", step["with"])
|
||||
self.assertTrue(step["with"]["push-to-registry"])
|
||||
else:
|
||||
self.assertIn("subject-checksums", step["with"])
|
||||
if action == "signpath/github-action-submit-signing-request":
|
||||
self.assertIn("api-token", step["with"])
|
||||
self.assertIn("organization-id", step["with"])
|
||||
self.assertEqual(seen, set(manifests))
|
||||
|
||||
def setUp(self) -> None:
|
||||
if USE_STAGED_GOVERNANCE and not REQUIRES_STAGED_GOVERNANCE_INPUTS:
|
||||
self.skipTest("staged slice does not touch the promotion-proof surface")
|
||||
@@ -1546,7 +1580,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
workflow,
|
||||
)
|
||||
self.assertIn(
|
||||
"signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2",
|
||||
"signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2",
|
||||
workflow,
|
||||
)
|
||||
self.assertIn("signedArtifactsPublished = $false", workflow)
|
||||
@@ -1924,7 +1958,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("windows_signing_backend: signpath", content)
|
||||
self.assertIn('if [[ "$REQUIRE_WINDOWS_SIGNING" == "true" ]]', candidate_workflow)
|
||||
self.assertIn("inputs.require_windows_signing", candidate_workflow)
|
||||
self.assertIn("signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2", candidate_workflow)
|
||||
self.assertIn("signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2", candidate_workflow)
|
||||
self.assertIn("github-artifact-id: ${{ steps.upload-unsigned-windows.outputs.artifact-id }}", candidate_workflow)
|
||||
self.assertIn("windows-signing-evidence.json", candidate_workflow)
|
||||
for signpath_setting in (
|
||||
@@ -1959,7 +1993,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("bash .github/scripts/setup-demo-ssh.sh", update_demo_workflow)
|
||||
self.assertIn("bash .github/scripts/check-demo-reachability.sh", update_demo_workflow)
|
||||
self.assertIn("ping: ${{ secrets.DEMO_SERVER_HOST }}", update_demo_workflow)
|
||||
self.assertIn("tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4", update_demo_workflow)
|
||||
self.assertIn("tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3", update_demo_workflow)
|
||||
self.assertIn("uses: ./.github/workflows/update-demo-server.yml", deploy_demo_workflow)
|
||||
self.assertIn("verify_only: true", deploy_demo_workflow)
|
||||
self.assertIn('MAX_SSH_SETUP_ATTEMPTS="${DEMO_SSH_SETUP_ATTEMPTS:-3}"', demo_ssh_helper)
|
||||
@@ -2003,7 +2037,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("id-token: write", candidate_workflow)
|
||||
self.assertIn("attestations: write", candidate_workflow)
|
||||
self.assertIn(
|
||||
"uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4",
|
||||
"uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2",
|
||||
candidate_workflow,
|
||||
)
|
||||
self.assertIn(
|
||||
@@ -2318,7 +2352,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("fail-fast: false", publish)
|
||||
self.assertIn("if: matrix.image == 'server'", publish)
|
||||
self.assertIn("if: matrix.image == 'control-plane'", publish)
|
||||
self.assertIn("uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4", publish)
|
||||
self.assertIn("uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2", publish)
|
||||
self.assertIn("subject-name: docker.io/rcourtman/pulse", publish)
|
||||
self.assertIn("subject-name: ghcr.io/${{ github.repository_owner }}/pulse", publish)
|
||||
# pulse-agent ships as release-asset binaries, not as a Docker image
|
||||
@@ -2362,7 +2396,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("tag: latest", dry_run_workflow)
|
||||
self.assertIn("verify_only: true", dry_run_workflow)
|
||||
self.assertIn("Verify Current Stable Demo Path (No Mutation)", dry_run_workflow)
|
||||
self.assertIn("tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4", demo)
|
||||
self.assertIn("tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3", demo)
|
||||
self.assertIn("oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}", demo)
|
||||
self.assertIn("oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}", demo)
|
||||
self.assertIn("ping: ${{ secrets.DEMO_SERVER_HOST }}", demo)
|
||||
|
||||
Reference in New Issue
Block a user