mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
ad1cfd33c3
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
469 lines
22 KiB
YAML
469 lines
22 KiB
YAML
name: Security Scan
|
|
|
|
# Build and Test audits dependencies on every push, but a quiet week with no
|
|
# pushes means no run — and newly disclosed vulnerabilities or delivery drift
|
|
# land against unchanged code. The six-hour schedule cheaply verifies that the
|
|
# advertised stable release remains locked and bound to its activation marker.
|
|
# The weekly schedule re-scans every dependency and performs the full public
|
|
# delivery verification. A failed scheduled run emails the repository owner.
|
|
on:
|
|
schedule:
|
|
- cron: '17 */6 * * *' # six-hour stable release lock and activation watch
|
|
- cron: '30 5 * * 1' # weekly, Monday 05:30 UTC, before the 06:00 triage run
|
|
workflow_dispatch:
|
|
workflow_run:
|
|
workflows: [Release Convergence]
|
|
types: [completed]
|
|
branches: [main]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
release-continuity:
|
|
name: Latest stable release continuity
|
|
# Stable convergence gets an immediate independent read-back. Prerelease
|
|
# convergence is excluded because this job intentionally follows the
|
|
# advertised stable channel. The six-hour schedule stops after release and
|
|
# activation identity resolution; weekly, manual, and convergence runs
|
|
# continue through every artifact and customer-facing delivery surface.
|
|
if: ${{ github.event_name != 'workflow_run' || !contains(github.event.workflow_run.display_title, '-') }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Checkout release verification controls
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Go
|
|
if: ${{ github.event.schedule != '17 */6 * * *' }}
|
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
|
with:
|
|
go-version-file: go.mod
|
|
|
|
- name: Set up Helm
|
|
if: ${{ github.event.schedule != '17 */6 * * *' }}
|
|
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@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
|
|
|
- name: Resolve the advertised stable release
|
|
id: release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p release-continuity-evidence
|
|
release_json=release-continuity-evidence/release.json
|
|
|
|
if ! gh api \
|
|
-H 'Accept: application/vnd.github+json' \
|
|
-H 'X-GitHub-Api-Version: 2026-03-10' \
|
|
"repos/${REPOSITORY}/releases/latest" > "${release_json}"; then
|
|
python3 scripts/release_control/release_continuity.py release \
|
|
--release-json "${release_json}" \
|
|
--diagnostic release-continuity-evidence/release-diagnostic.json \
|
|
--github-output "${GITHUB_OUTPUT}" || true
|
|
exit 1
|
|
fi
|
|
|
|
python3 scripts/release_control/release_continuity.py release \
|
|
--release-json "${release_json}" \
|
|
--diagnostic release-continuity-evidence/release-diagnostic.json \
|
|
--github-output "${GITHUB_OUTPUT}"
|
|
|
|
- name: Reject orphaned stable publication tags
|
|
id: frontier
|
|
if: ${{ !cancelled() && steps.release.outputs.referenceable == 'true' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
run: |
|
|
set -euo pipefail
|
|
evidence=release-continuity-evidence
|
|
gh api --paginate --slurp \
|
|
-H 'Accept: application/vnd.github+json' \
|
|
-H 'X-GitHub-Api-Version: 2026-03-10' \
|
|
"repos/${REPOSITORY}/git/matching-refs/tags/v?per_page=100" \
|
|
> "${evidence}/stable-refs.json"
|
|
gh api --paginate --slurp \
|
|
-H 'Accept: application/vnd.github+json' \
|
|
-H 'X-GitHub-Api-Version: 2026-03-10' \
|
|
"repos/${REPOSITORY}/releases?per_page=100" \
|
|
> "${evidence}/releases.json"
|
|
|
|
fetch_registry_tags() {
|
|
local registry_name="$1" auth_url="$2" registry_url="$3" image="$4" output="$5"
|
|
local token
|
|
token="$(curl -fsSL --retry 4 --retry-delay 2 --retry-all-errors "${auth_url}")"
|
|
token="$(jq -er '.token | select(type == "string" and length > 0)' <<<"${token}")"
|
|
curl -fsSL --retry 4 --retry-delay 2 --retry-all-errors \
|
|
-H "Authorization: Bearer ${token}" \
|
|
"${registry_url}/v2/${image}/tags/list?n=10000" \
|
|
| jq --arg name "${registry_name}/${image}" '.name = $name' > "${output}"
|
|
}
|
|
owner="${GITHUB_REPOSITORY_OWNER,,}"
|
|
for image in pulse pulse-control-plane; do
|
|
file_image="${image//-/_}"
|
|
fetch_registry_tags \
|
|
ghcr.io \
|
|
"https://ghcr.io/token?service=ghcr.io&scope=repository:${owner}/${image}:pull" \
|
|
https://ghcr.io "${owner}/${image}" \
|
|
"${evidence}/ghcr-${file_image}-tags.json"
|
|
done
|
|
# The server image is public on both registries. The control-plane
|
|
# image is publicly enumerable only on GHCR, so an unauthenticated
|
|
# continuity audit must not depend on Docker Hub credentials.
|
|
fetch_registry_tags \
|
|
docker.io \
|
|
"https://auth.docker.io/token?service=registry.docker.io&scope=repository:rcourtman/pulse:pull" \
|
|
https://registry-1.docker.io rcourtman/pulse \
|
|
"${evidence}/docker-pulse-tags.json"
|
|
python3 scripts/release_control/release_continuity.py frontier \
|
|
--release-json "${evidence}/release.json" \
|
|
--stable-refs-json "${evidence}/stable-refs.json" \
|
|
--releases-json "${evidence}/releases.json" \
|
|
--registry-tags-json "${evidence}/ghcr-pulse-tags.json" \
|
|
--registry-tags-json "${evidence}/docker-pulse-tags.json" \
|
|
--registry-tags-json "${evidence}/ghcr-pulse_control_plane-tags.json" \
|
|
--diagnostic "${evidence}/frontier-diagnostic.json"
|
|
|
|
- name: Bind the release activation marker
|
|
id: activation
|
|
# A structurally valid tag/id/SHA remains safe lookup data even when
|
|
# release trust fails. Inspect its marker so one defect cannot hide a
|
|
# second; all public-surface verification below still requires both
|
|
# release and activation admission to succeed.
|
|
if: ${{ !cancelled() && steps.release.outputs.referenceable == 'true' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
release_json=release-continuity-evidence/release.json
|
|
marker=release-continuity-evidence/release-activation.json
|
|
|
|
if ! gh release download "${TAG}" \
|
|
--repo "${REPOSITORY}" \
|
|
--pattern release-activation.json \
|
|
--dir release-continuity-evidence; then
|
|
python3 scripts/release_control/release_continuity.py activation \
|
|
--release-json "${release_json}" \
|
|
--activation-json "${marker}" \
|
|
--diagnostic release-continuity-evidence/activation-diagnostic.json \
|
|
--github-output "${GITHUB_OUTPUT}" || true
|
|
exit 1
|
|
fi
|
|
|
|
python3 scripts/release_control/release_continuity.py activation \
|
|
--release-json "${release_json}" \
|
|
--activation-json "${marker}" \
|
|
--diagnostic release-continuity-evidence/activation-diagnostic.json \
|
|
--github-output "${GITHUB_OUTPUT}"
|
|
|
|
- name: Verify immutable release and build provenance
|
|
id: packet
|
|
if: ${{ always() && steps.release.outcome == 'success' && steps.frontier.outcome == 'success' && steps.activation.outcome == 'success' && github.event.schedule != '17 */6 * * *' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
RELEASE_ID: ${{ steps.release.outputs.release_id }}
|
|
SOURCE_SHA: ${{ steps.release.outputs.source_sha }}
|
|
run: >-
|
|
./scripts/verify-github-release-integrity.sh
|
|
"${TAG}" "${REPOSITORY}" "${RELEASE_ID}" "${SOURCE_SHA}"
|
|
|
|
- name: Authenticate every published release asset
|
|
id: assets
|
|
if: ${{ always() && steps.release.outcome == 'success' && steps.frontier.outcome == 'success' && steps.activation.outcome == 'success' && github.event.schedule != '17 */6 * * *' }}
|
|
env:
|
|
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
run: ./scripts/validate-published-release.sh "${TAG}" "${REPOSITORY}"
|
|
|
|
- name: Verify exact-version container identities
|
|
id: containers
|
|
if: ${{ always() && steps.release.outcome == 'success' && steps.frontier.outcome == 'success' && steps.activation.outcome == 'success' && github.event.schedule != '17 */6 * * *' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
SOURCE_SHA: ${{ steps.release.outputs.source_sha }}
|
|
EXPECTED_SERVER_DIGEST: ${{ steps.activation.outputs.server_image_digest }}
|
|
EXPECTED_CONTROL_PLANE_DIGEST: ${{ steps.activation.outputs.control_plane_image_digest }}
|
|
run: |
|
|
set -euo pipefail
|
|
proof="$(./scripts/verify-release-container-images.sh "${TAG}" "${SOURCE_SHA}" "${REPOSITORY}")"
|
|
printf '%s\n' "${proof}"
|
|
server_digest="$(awk -F= '$1 == "server_digest" {print $2}' <<<"${proof}")"
|
|
control_plane_digest="$(awk -F= '$1 == "control_plane_digest" {print $2}' <<<"${proof}")"
|
|
if [ "${server_digest}" != "${EXPECTED_SERVER_DIGEST}" ] || \
|
|
[ "${control_plane_digest}" != "${EXPECTED_CONTROL_PLANE_DIGEST}" ]; then
|
|
echo "::error::Exact-version container identities moved from the committed activation marker."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Verify stable container discovery aliases
|
|
id: aliases
|
|
if: ${{ always() && steps.release.outcome == 'success' && steps.frontier.outcome == 'success' && steps.activation.outcome == 'success' && github.event.schedule != '17 */6 * * *' }}
|
|
env:
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
EXPECTED_SERVER_DIGEST: ${{ steps.activation.outputs.server_image_digest }}
|
|
EXPECTED_CONTROL_PLANE_DIGEST: ${{ steps.activation.outputs.control_plane_image_digest }}
|
|
REGISTRY_OWNER: ${{ github.repository_owner }}
|
|
run: >-
|
|
./scripts/verify-stable-container-aliases.sh
|
|
"${TAG}" "${EXPECTED_SERVER_DIGEST}" "${EXPECTED_CONTROL_PLANE_DIGEST}"
|
|
"${REGISTRY_OWNER}"
|
|
|
|
- name: Verify exact-version Helm identity
|
|
id: helm
|
|
if: ${{ always() && steps.release.outcome == 'success' && steps.frontier.outcome == 'success' && steps.activation.outcome == 'success' && github.event.schedule != '17 */6 * * *' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
SOURCE_SHA: ${{ steps.release.outputs.source_sha }}
|
|
EXPECTED_HELM_DIGEST: ${{ steps.activation.outputs.helm_chart_digest }}
|
|
run: >-
|
|
./scripts/verify-release-helm-chart.sh
|
|
"${TAG}" "${SOURCE_SHA}" "${REPOSITORY}" "${EXPECTED_HELM_DIGEST}"
|
|
|
|
- name: Record continuity evidence
|
|
if: ${{ always() }}
|
|
env:
|
|
REPOSITORY: ${{ github.repository }}
|
|
RUN_ID: ${{ github.run_id }}
|
|
RUN_ATTEMPT: ${{ github.run_attempt }}
|
|
TRIGGER_EVENT: ${{ github.event_name }}
|
|
TRIGGER_SCHEDULE: ${{ github.event.schedule }}
|
|
CONVERGENCE_RUN_ID: ${{ github.event.workflow_run.id }}
|
|
CONVERGENCE_RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
|
|
CONVERGENCE_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
|
CONVERGENCE_DISPLAY_TITLE: ${{ github.event.workflow_run.display_title }}
|
|
TAG: ${{ steps.release.outputs.tag }}
|
|
RELEASE_ID: ${{ steps.release.outputs.release_id }}
|
|
SOURCE_SHA: ${{ steps.release.outputs.source_sha }}
|
|
ACTIVATION_SHA256: ${{ steps.activation.outputs.activation_sha256 }}
|
|
SERVER_IMAGE_DIGEST: ${{ steps.activation.outputs.server_image_digest }}
|
|
CONTROL_PLANE_IMAGE_DIGEST: ${{ steps.activation.outputs.control_plane_image_digest }}
|
|
HELM_CHART_DIGEST: ${{ steps.activation.outputs.helm_chart_digest }}
|
|
RELEASE_RESULT: ${{ steps.release.outcome }}
|
|
FRONTIER_RESULT: ${{ steps.frontier.outcome }}
|
|
ACTIVATION_RESULT: ${{ steps.activation.outcome }}
|
|
PACKET_RESULT: ${{ steps.packet.outcome }}
|
|
ASSET_RESULT: ${{ steps.assets.outcome }}
|
|
CONTAINER_RESULT: ${{ steps.containers.outcome }}
|
|
ALIAS_RESULT: ${{ steps.aliases.outcome }}
|
|
HELM_RESULT: ${{ steps.helm.outcome }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p release-continuity-evidence
|
|
checked_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
mode=full
|
|
if [ "${TRIGGER_SCHEDULE}" = '17 */6 * * *' ]; then
|
|
mode=release_lock
|
|
fi
|
|
for diagnostic in release frontier activation; do
|
|
path="release-continuity-evidence/${diagnostic}-diagnostic.json"
|
|
if [ ! -s "${path}" ]; then
|
|
jq -n \
|
|
--arg check "${diagnostic}" \
|
|
'{schema_version: 1, check: $check, status: "not_run", identity: {}, violations: []}' \
|
|
> "${path}"
|
|
fi
|
|
done
|
|
jq -n \
|
|
--slurpfile release_diagnostic release-continuity-evidence/release-diagnostic.json \
|
|
--slurpfile frontier_diagnostic release-continuity-evidence/frontier-diagnostic.json \
|
|
--slurpfile activation_diagnostic release-continuity-evidence/activation-diagnostic.json \
|
|
--arg schema_version "1" \
|
|
--arg checked_at "${checked_at}" \
|
|
--arg repository "${REPOSITORY}" \
|
|
--arg run_id "${RUN_ID}" \
|
|
--arg run_attempt "${RUN_ATTEMPT}" \
|
|
--arg trigger_event "${TRIGGER_EVENT}" \
|
|
--arg trigger_schedule "${TRIGGER_SCHEDULE}" \
|
|
--arg mode "${mode}" \
|
|
--arg convergence_run_id "${CONVERGENCE_RUN_ID}" \
|
|
--arg convergence_run_attempt "${CONVERGENCE_RUN_ATTEMPT}" \
|
|
--arg convergence_conclusion "${CONVERGENCE_CONCLUSION}" \
|
|
--arg convergence_display_title "${CONVERGENCE_DISPLAY_TITLE}" \
|
|
--arg tag "${TAG}" \
|
|
--arg release_id "${RELEASE_ID}" \
|
|
--arg source_sha "${SOURCE_SHA}" \
|
|
--arg activation_sha256 "${ACTIVATION_SHA256}" \
|
|
--arg server_image_digest "${SERVER_IMAGE_DIGEST}" \
|
|
--arg control_plane_image_digest "${CONTROL_PLANE_IMAGE_DIGEST}" \
|
|
--arg helm_chart_digest "${HELM_CHART_DIGEST}" \
|
|
--arg release_result "${RELEASE_RESULT}" \
|
|
--arg frontier_result "${FRONTIER_RESULT}" \
|
|
--arg activation_result "${ACTIVATION_RESULT}" \
|
|
--arg packet_result "${PACKET_RESULT}" \
|
|
--arg asset_result "${ASSET_RESULT}" \
|
|
--arg container_result "${CONTAINER_RESULT}" \
|
|
--arg alias_result "${ALIAS_RESULT}" \
|
|
--arg helm_result "${HELM_RESULT}" \
|
|
'{
|
|
schema_version: ($schema_version | tonumber),
|
|
checked_at: $checked_at,
|
|
repository: $repository,
|
|
workflow_run: {id: $run_id, attempt: $run_attempt},
|
|
trigger: {
|
|
event: $trigger_event,
|
|
schedule: $trigger_schedule,
|
|
mode: $mode,
|
|
release_convergence_run: {
|
|
id: $convergence_run_id,
|
|
attempt: $convergence_run_attempt,
|
|
conclusion: $convergence_conclusion,
|
|
display_title: $convergence_display_title
|
|
}
|
|
},
|
|
release: {
|
|
tag: $tag,
|
|
id: $release_id,
|
|
source_sha: $source_sha,
|
|
activation_sha256: $activation_sha256,
|
|
server_image_digest: $server_image_digest,
|
|
control_plane_image_digest: $control_plane_image_digest,
|
|
helm_chart_digest: $helm_chart_digest
|
|
},
|
|
diagnostics: {
|
|
release_identity: $release_diagnostic[0],
|
|
stable_publication_frontier: $frontier_diagnostic[0],
|
|
activation_binding: $activation_diagnostic[0]
|
|
},
|
|
checks: {
|
|
release_resolution: $release_result,
|
|
stable_publication_frontier: $frontier_result,
|
|
activation_binding: $activation_result,
|
|
immutable_packet_and_provenance: $packet_result,
|
|
authenticated_assets: $asset_result,
|
|
container_identities: $container_result,
|
|
stable_container_aliases: $alias_result,
|
|
helm_identity: $helm_result
|
|
}
|
|
}' > release-continuity-evidence/continuity-evidence.json
|
|
cat release-continuity-evidence/continuity-evidence.json >> "${GITHUB_STEP_SUMMARY}"
|
|
|
|
- name: Retain continuity evidence
|
|
if: ${{ always() }}
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: release-continuity-${{ steps.release.outputs.tag || 'unresolved' }}-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: release-continuity-evidence
|
|
if-no-files-found: error
|
|
retention-days: 90
|
|
|
|
container-lifecycle:
|
|
name: Container trust and support window
|
|
if: ${{ github.event_name != 'workflow_run' && github.event.schedule != '17 */6 * * *' }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
|
with:
|
|
go-version-file: go.mod
|
|
|
|
- name: Check governed image pins and lifecycle runway
|
|
run: >-
|
|
go test ./scripts/installtests
|
|
-run '^(TestGovernedContainerBaseSupportWindow|TestEveryDockerfilePinsExternalBases|TestNodeToolchainParity|TestIntegrationContainersUseGovernedImmutableBases|TestDockerBuildUsesCanonicalReleaseLdflags|TestProviderMSPControlPlaneDockerfileBuildsReleaseLicenseBinary)$'
|
|
|
|
govulncheck:
|
|
name: Go Vulnerability Scan (${{ matrix.directory }})
|
|
if: ${{ github.event_name != 'workflow_run' && github.event.schedule != '17 */6 * * *' }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 45
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
directory:
|
|
- '.'
|
|
- 'tests/integration/mock-github-server'
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
|
with:
|
|
go-version-file: go.mod
|
|
|
|
- name: Install govulncheck
|
|
run: go install golang.org/x/vuln/cmd/govulncheck@v1.7.0
|
|
|
|
- name: Scan Go module for reachable vulnerabilities
|
|
working-directory: ${{ matrix.directory }}
|
|
run: govulncheck ./...
|
|
|
|
npm-audit:
|
|
name: npm Dependency Audit (${{ matrix.name }})
|
|
if: ${{ github.event_name != 'workflow_run' && github.event.schedule != '17 */6 * * *' }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 20
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: root tooling
|
|
directory: '.'
|
|
- name: product frontend
|
|
directory: 'frontend-modern'
|
|
- name: account frontend
|
|
directory: 'internal/cloudcp/portal/frontend'
|
|
- name: integration tests
|
|
directory: 'tests/integration'
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Audit complete dependency graph
|
|
id: audit-complete
|
|
continue-on-error: true
|
|
working-directory: ${{ matrix.directory }}
|
|
run: bash "$GITHUB_WORKSPACE/scripts/npm-audit-retry.sh" all --package-lock-only
|
|
|
|
- name: Audit production dependencies
|
|
id: audit-production
|
|
continue-on-error: true
|
|
working-directory: ${{ matrix.directory }}
|
|
run: bash "$GITHUB_WORKSPACE/scripts/npm-audit-retry.sh" production --package-lock-only
|
|
|
|
- name: Require dependency audits
|
|
if: ${{ !cancelled() }}
|
|
env:
|
|
COMPLETE_AUDIT_RESULT: ${{ steps.audit-complete.outcome }}
|
|
PRODUCTION_AUDIT_RESULT: ${{ steps.audit-production.outcome }}
|
|
run: |
|
|
if [ "${COMPLETE_AUDIT_RESULT}" != success ] || \
|
|
[ "${PRODUCTION_AUDIT_RESULT}" != success ]; then
|
|
echo "::error::One or more dependency audits failed."
|
|
exit 1
|
|
fi
|