diff --git a/.github/workflows/check-cache-migration.yml b/.github/workflows/check-cache-migration.yml new file mode 100644 index 0000000..edef6f4 --- /dev/null +++ b/.github/workflows/check-cache-migration.yml @@ -0,0 +1,178 @@ +--- +name: Check cache migration (GitHub → S3) + +on: + workflow_dispatch: + inputs: + environment: + description: Cache environment to check + required: true + default: prod + type: choice + options: + - prod + - dev + +jobs: + check-migration: + runs-on: ubuntu-latest + name: Compare GitHub cache vs S3 + permissions: + id-token: write + contents: read + actions: write # required to list GitHub cache entries and set the opt-out repository variable + + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Setup S3 cache credentials + id: aws-auth + uses: SonarSource/gh-action_cache/credential-setup@v1 + with: + environment: ${{ inputs.environment }} + + - name: List GitHub cache entries + id: gh-caches + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + # Fetch all GitHub cache entries (paginated, up to 10 000) + PAGE=1 + PER_PAGE=100 + ALL_ENTRIES="[]" + while true; do + RESPONSE=$(curl -s -f \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/caches?per_page=${PER_PAGE}&page=${PAGE}") + ENTRIES=$(echo "$RESPONSE" | jq '.actions_caches') + COUNT=$(echo "$ENTRIES" | jq 'length') + ALL_ENTRIES=$(echo "$ALL_ENTRIES $ENTRIES" | jq -s 'add') + if [[ "$COUNT" -lt "$PER_PAGE" ]]; then + break + fi + PAGE=$((PAGE + 1)) + done + + # Filter: include only target branches, exclude unwanted key patterns + FILTERED=$(echo "$ALL_ENTRIES" | jq '[ + .[] | + select( + (.ref | test("^refs/heads/(main|master|branch-.+|dogfood-on-.+|feature/long/.+)$")) and + (.key | test("^(build-number-|mise-)") | not) + ) | + { ref: .ref, key: .key } + ]') + + TOTAL=$(echo "$ALL_ENTRIES" | jq 'length') + INCLUDED=$(echo "$FILTERED" | jq 'length') + echo "Total GitHub cache entries: $TOTAL" + echo "Included for migration check: $INCLUDED" + + # Write to file for next step + echo "$FILTERED" > /tmp/gh_caches.json + echo "included-count=$INCLUDED" >> "$GITHUB_OUTPUT" + + - name: List S3 cache objects + id: s3-objects + shell: bash + env: + AWS_ACCESS_KEY_ID: ${{ steps.aws-auth.outputs.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ steps.aws-auth.outputs.AWS_SECRET_ACCESS_KEY }} + AWS_SESSION_TOKEN: ${{ steps.aws-auth.outputs.AWS_SESSION_TOKEN }} + AWS_DEFAULT_REGION: eu-central-1 + S3_BUCKET: sonarsource-s3-cache-${{ inputs.environment }}-bucket + run: | + # List all S3 objects with pagination handled internally by the CLI + aws s3 ls "s3://$S3_BUCKET/" --recursive \ + | awk '{print $4}' \ + | sort > /tmp/s3_keys.txt + + S3_COUNT=$(wc -l < /tmp/s3_keys.txt) + echo "Total S3 cache objects: $S3_COUNT" + echo "s3-count=$S3_COUNT" >> "$GITHUB_OUTPUT" + + - name: Compare and report + id: compare + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + INCLUDED=$(jq 'length' /tmp/gh_caches.json) + MIGRATED=0 + MISSING=() + + while IFS= read -r ENTRY; do + REF=$(echo "$ENTRY" | jq -r '.ref') + KEY=$(echo "$ENTRY" | jq -r '.key') + # S3 keys use full ref for push events (refs/heads/main/{key}) and short branch name for PR events (feat/someBranch/{key}). + # Check both forms since we cannot know which was used at save time. + REF_SHORT="${REF#refs/heads/}" + S3_KEY_FULL="${REF}/${KEY}" + S3_KEY_SHORT="${REF_SHORT}/${KEY}" + if grep -qxF "$S3_KEY_FULL" /tmp/s3_keys.txt || grep -qxF "$S3_KEY_SHORT" /tmp/s3_keys.txt; then + MIGRATED=$((MIGRATED + 1)) + else + MISSING+=("$S3_KEY_SHORT") + fi + done < <(jq -c '.[]' /tmp/gh_caches.json) + + echo "" + echo "=========================================" + echo " Migration status: $MIGRATED / $INCLUDED" + echo "=========================================" + + if [[ "${#MISSING[@]}" -gt 0 ]]; then + echo "" + echo "Missing in S3 (${#MISSING[@]} entries):" + printf ' %s\n' "${MISSING[@]}" + fi + + if [[ "$MIGRATED" -eq "$INCLUDED" && "$INCLUDED" -gt 0 ]]; then + echo "" + echo "All included GitHub cache entries are present in S3." + echo "migration-complete=true" >> "$GITHUB_OUTPUT" + else + echo "migration-complete=false" >> "$GITHUB_OUTPUT" + fi + + - name: Set CACHE_IMPORT_GITHUB=false (migration complete) + if: steps.compare.outputs.migration-complete == 'true' + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + VARIABLE_NAME="CACHE_IMPORT_GITHUB" + + # Check if variable already exists + STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/variables/${VARIABLE_NAME}") + + if [[ "$STATUS" == "200" ]]; then + # Update existing variable + curl -s -f -X PATCH \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/variables/${VARIABLE_NAME}" \ + -d '{"name":"'"$VARIABLE_NAME"'","value":"false"}' + echo "Updated repository variable $VARIABLE_NAME=false" + else + # Create new variable + curl -s -f -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/variables" \ + -d '{"name":"'"$VARIABLE_NAME"'","value":"false"}' + echo "Created repository variable $VARIABLE_NAME=false" + fi + + echo "" + echo "Migration complete — CACHE_IMPORT_GITHUB set to false" + echo "Import fallback will be disabled on next workflow runs." + echo "To re-enable migration mode, delete or set CACHE_IMPORT_GITHUB=true in repository variables." diff --git a/.github/workflows/test-cache-migration-gh2s3.yml b/.github/workflows/test-cache-migration-gh2s3.yml new file mode 100644 index 0000000..297bfaa --- /dev/null +++ b/.github/workflows/test-cache-migration-gh2s3.yml @@ -0,0 +1,147 @@ +--- +name: Test cache migration (GitHub → S3) + +on: + push: + branches: [ master ] + pull_request: + workflow_dispatch: + +jobs: + # Prerequisite: provision GitHub cache entries used by the test scenarios + provision-github-cache: + runs-on: github-ubuntu-latest-s + name: "Provision GitHub cache entries" + permissions: + contents: read + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Save GitHub cache entry (for import scenario) + run: mkdir -p ~/.cache/test-migration && echo "github-content" > ~/.cache/test-migration/test-file.txt + - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.cache/test-migration + key: test-migration-gh + - name: Save GitHub cache entry (for S3-hit scenario, to confirm it is NOT restored) + run: echo "github-content" > ~/.cache/test-migration/test-file.txt + - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.cache/test-migration + key: test-migration-s3hit + + # Prerequisite: provision an S3 cache entry for the S3-hit scenario + provision-s3-cache: + runs-on: github-ubuntu-latest-s + name: "Provision S3 cache entry" + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Create S3 cache content + run: mkdir -p ~/.cache/test-migration && echo "s3-content" > ~/.cache/test-migration/test-file.txt + - name: Save to S3 cache + uses: ./ + with: + path: ~/.cache/test-migration + key: test-migration-s3hit + environment: dev + backend: s3 + + # Scenario 1: S3 backend with import mode active (default behavior after migration begins) + test-s3-import-enabled: + needs: provision-github-cache + runs-on: github-ubuntu-latest-s + name: "S3 + import enabled (migration fallback)" + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Cache with migration fallback + id: cache + uses: ./ + with: + path: ~/.cache/test-migration + key: test-migration-gh + environment: dev + backend: s3 + # import-github-cache defaults to true — no need to set it explicitly + - name: Verify import succeeded + run: | + [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]] || { echo "ERROR: cache-hit is not true"; exit 1; } + [[ "$(cat ~/.cache/test-migration/test-file.txt)" == "github-content" ]] || { echo "ERROR: unexpected content, not restored from GitHub"; exit 1; } + rm -rf ~/.cache/test-migration # prevent saving to S3 so other scenarios don't find it + + # Scenario 2: S3 backend with import mode explicitly disabled + test-s3-import-disabled: + needs: provision-github-cache + runs-on: github-ubuntu-latest-s + name: "S3 + import disabled (migration complete)" + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Cache without migration fallback + id: cache + uses: ./ + with: + path: ~/.cache/test-migration + key: test-migration-gh + environment: dev + backend: s3 + import-github-cache: 'false' + - name: Verify no import from GitHub + run: | + [[ "${{ steps.cache.outputs.cache-hit }}" != "true" ]] || { echo "ERROR: cache-hit is true but import should be disabled"; exit 1; } + test ! -f ~/.cache/test-migration/test-file.txt || { echo "ERROR: cache content was restored but import should be disabled"; exit 1; } + + # Scenario 3: S3 backend with import disabled via env var (repo variable pattern) + test-s3-import-disabled-via-env: + needs: provision-github-cache + runs-on: github-ubuntu-latest-s + name: "S3 + import disabled via env var" + permissions: + id-token: write + contents: read + env: + CACHE_IMPORT_GITHUB: 'false' + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Cache without migration fallback (via env) + id: cache + uses: ./ + with: + path: ~/.cache/test-migration + key: test-migration-gh + environment: dev + backend: s3 + - name: Verify no import from GitHub + run: | + [[ "${{ steps.cache.outputs.cache-hit }}" != "true" ]] || { echo "ERROR: cache-hit is true but import should be disabled"; exit 1; } + test ! -f ~/.cache/test-migration/test-file.txt || { echo "ERROR: cache content was restored but import should be disabled"; exit 1; } + + # Scenario 4: S3 hit — GitHub import step must be skipped entirely + test-s3-hit-skips-github-import: + needs: [ provision-github-cache, provision-s3-cache ] + runs-on: github-ubuntu-latest-s + name: "S3 hit → no GitHub import" + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Cache (S3 hit expected, import enabled) + id: cache + uses: ./ + with: + path: ~/.cache/test-migration + key: test-migration-s3hit + environment: dev + backend: s3 + # import-github-cache defaults to true — S3 hit should prevent GitHub from being tried + - name: Verify S3 content restored, not GitHub + run: | + [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]] || { echo "ERROR: expected S3 hit"; exit 1; } + [[ "$(cat ~/.cache/test-migration/test-file.txt)" == "s3-content" ]] || { echo "ERROR: unexpected content — GitHub import may have overridden S3"; exit 1; } diff --git a/README.md b/README.md index 9e53316..db10dd1 100644 --- a/README.md +++ b/README.md @@ -60,21 +60,29 @@ These must be committed since GitHub Actions runs them directly. restore-keys: cache-${{ runner.os }}- ``` +### Input Environment Variables + +| Environment Variable | Description | +|-----------------------|-----------------------------------------------------------------------------------| +| `CACHE_BACKEND` | Force specific backend: `github` or `s3` (overrides auto-detection) | +| `CACHE_IMPORT_GITHUB` | Disable GitHub cache fallback for S3 backend (migration mode) when set to `false` | + ## Inputs -| Input | Description | Required | Default | -|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------| -| `path` | Files, directories, and wildcard patterns to cache | Yes | | -| `key` | Explicit key for restoring and saving cache | Yes | | -| `restore-keys` | Ordered list of prefix-matched keys for fallback | No | | -| `fallback-to-default-branch` | Automatically add a fallback restore key pointing to the default branch cache (S3 backend only). Disable if you want strict branch isolation. | No | `false` | -| `fallback-branch` | Optional maintenance branch for fallback restore keys (pattern: `branch-*`, S3 backend only). If not set, the repository default branch is used. | No | | -| `environment` | Environment to use (dev or prod, S3 backend only) | No | `prod` | -| `upload-chunk-size` | Chunk size for large file uploads (bytes) | No | | -| `enableCrossOsArchive` | Enable cross-OS cache compatibility | No | `false` | -| `fail-on-cache-miss` | Fail workflow if cache entry not found | No | `false` | -| `lookup-only` | Only check cache existence without downloading | No | `false` | -| `backend` | Force specific backend: `github` or `s3`. Takes priority over `CACHE_BACKEND` env var and auto-detection. | No | | +| Input | Description | Required | Default | +|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------| +| `path` | Files, directories, and wildcard patterns to cache | Yes | | +| `key` | Explicit key for restoring and saving cache | Yes | | +| `restore-keys` | Ordered list of prefix-matched keys for fallback | No | | +| `fallback-to-default-branch` | Automatically add a fallback restore key pointing to the default branch cache (S3 backend only). Disable if you want strict branch isolation. | No | `false` | +| `fallback-branch` | Optional maintenance branch for fallback restore keys (pattern: `branch-*`, S3 backend only). If not set, the repository default branch is used. | No | | +| `environment` | Environment to use (dev or prod, S3 backend only) | No | `prod` | +| `upload-chunk-size` | Chunk size for large file uploads (bytes) | No | | +| `enableCrossOsArchive` | Enable cross-OS cache compatibility | No | `false` | +| `fail-on-cache-miss` | Fail workflow if cache entry not found | No | `false` | +| `lookup-only` | Only check cache existence without downloading | No | `false` | +| `backend` | Force specific backend: `github` or `s3`. Takes priority over `CACHE_BACKEND` env var and auto-detection. | No | | +| `import-github-cache` | Import GitHub cache to S3 when no S3 cache exists (migration mode, S3 backend only). Takes priority over `CACHE_IMPORT_GITHUB` env var. | No | `true` | ## Backend Selection @@ -84,16 +92,75 @@ The cache backend is determined in the following priority order: 2. **`CACHE_BACKEND` environment variable** — set at the job or workflow level (`github` or `s3`) 3. **Repository visibility** — `github` for public repos, `s3` for private/internal repos -The `CACHE_BACKEND` env var is useful when the cache action is called indirectly through a composite action and you cannot set the `backend` -input directly: +The `CACHE_BACKEND` env var is useful when the cache action is called indirectly through a composite action, and you cannot set the +`backend` input directly, or when you want to enforce the same backend for all cache steps in a workflow without modifying each step: + +```yaml +env: + CACHE_BACKEND: s3 # forces S3 for all cache steps, including those in reusable actions +``` + +## Migration Mode (GitHub cache → S3) + +When switching from GitHub Actions cache to S3, existing cache entries live only in GitHub and would need to be rebuilt from scratch. +Migration mode bridges this gap: when using the S3 backend and no S3 cache exists, the action automatically falls back to restore +from GitHub Actions cache using the original key. The S3 post-job step then saves the restored content to S3, pre-provisioning it +for subsequent runs. + +Migration mode is **enabled by default** for S3 backend. Once all relevant entries have been migrated to S3, disable it to avoid +the overhead of the GitHub fallback attempt on every cache miss. + +**Disable resolution order** (first match wins): + +1. **`import-github-cache: 'false'`** — action input in the step +2. **`CACHE_IMPORT_GITHUB=false`** — environment variable at job or workflow level (can be sourced from a repository variable) +3. Default: `true` + +**Disabling via repository variable** (recommended for gradual rollout): ```yaml jobs: build: env: - CACHE_BACKEND: s3 # forces S3 for all cache steps, including those in reusable actions + CACHE_IMPORT_GITHUB: ${{ vars.CACHE_IMPORT_GITHUB }} steps: - - uses: SonarSource/some-other-action@v1 # internally calls gh-action_cache + - uses: SonarSource/gh-action_cache@v1 +``` + +Set the `CACHE_IMPORT_GITHUB` repository variable to `false` once migration is complete — no workflow changes needed. + +**Behavior with `fail-on-cache-miss`:** when migration mode is active, the S3 miss does not immediately fail the job. +The action tries the GitHub fallback first and only fails if both S3 and GitHub report a cache miss. + +### Checking migration progress + +`gh-action_cache` ships a sample workflow at `.github/workflows/check-cache-migration.yml` that you must **copy into +each repository** using the S3 backend. It compares GitHub cache entries against S3 objects for that repository and +automatically disables the import fallback once migration is complete. + +**What it checks:** branches matching `main`, `master`, `branch-*`, `dogfood-on-*`, `feature/long/*`, excluding +transient keys (`build-number-*`, `mise-*`). + +**When all included entries are present in S3**, the workflow automatically sets `CACHE_IMPORT_GITHUB=false` as a +repository variable, disabling the import fallback for all subsequent runs if the environment variable is set with +`${{ vars.CACHE_IMPORT_GITHUB }}` in the workflow. + +**Trigger manually via GitHub CLI:** + +```bash +# Check prod environment (default) +gh workflow run check-cache-migration.yml + +# Check dev environment +gh workflow run check-cache-migration.yml -f environment=dev +``` + +To re-enable migration mode after it has been automatically disabled, delete or reset the repository variable: + +```bash +gh variable delete CACHE_IMPORT_GITHUB +# or +gh variable set CACHE_IMPORT_GITHUB --body "true" ``` ## Outputs @@ -288,13 +355,13 @@ jobs: ### Modes of Operation -| Scenario | Branch | Key | Dry-run | -|----------|--------|-----|---------| -| List all cache entries | _(empty)_ | _(empty)_ | n/a | -| Preview what would be deleted | `feature/my-branch` | _(optional)_ | `true` | -| Delete cache for a branch | `feature/my-branch` | _(optional)_ | `false` | -| Delete key for given branch | `feature/my-branch` | `sccache-Linux-` | `false` | -| Delete key across all branches | _(empty)_ | `sccache-Linux-` | `false` | +| Scenario | Branch | Key | Dry-run | +|--------------------------------|---------------------|------------------|---------| +| List all cache entries | _(empty)_ | _(empty)_ | n/a | +| Preview what would be deleted | `feature/my-branch` | _(optional)_ | `true` | +| Delete cache for a branch | `feature/my-branch` | _(optional)_ | `false` | +| Delete key for given branch | `feature/my-branch` | `sccache-Linux-` | `false` | +| Delete key across all branches | _(empty)_ | `sccache-Linux-` | `false` | ### Running via GitHub CLI diff --git a/action.yml b/action.yml index 0520a53..a75ac5c 100644 --- a/action.yml +++ b/action.yml @@ -39,11 +39,16 @@ inputs: description: > Force cache backend ('github' or 's3'). If not set, falls back to the CACHE_BACKEND environment variable if defined, then automatically determined based on repository visibility. + import-github-cache: + description: > + Import GitHub cache to S3 when no S3 cache exists (migration mode). Enabled by default when using S3 backend. + Set to 'false' to disable, or control via the CACHE_IMPORT_GITHUB environment variable. + default: '' outputs: cache-hit: description: A boolean value to indicate an exact match was found for the primary key - value: ${{ steps.github-cache.outputs.cache-hit || steps.s3-cache.outputs.cache-hit }} + value: ${{ steps.github-cache.outputs.cache-hit || steps.s3-cache.outputs.cache-hit || steps.github-import.outputs.cache-hit }} runs: using: composite @@ -121,6 +126,26 @@ runs: GITHUB_REPOSITORY: ${{ github.repository }} run: $ACTION_PATH_CACHE/scripts/prepare-keys.sh + - name: Determine GitHub cache import mode + if: steps.cache-backend.outputs.cache-backend == 's3' + id: import-mode + shell: bash + env: + INPUT_IMPORT_GITHUB_CACHE: ${{ inputs.import-github-cache }} + run: | + # Resolution order: input → CACHE_IMPORT_GITHUB env var → default true + if [[ -n "$INPUT_IMPORT_GITHUB_CACHE" ]]; then + IMPORT_GITHUB="$INPUT_IMPORT_GITHUB_CACHE" + echo "Using import mode from input: $IMPORT_GITHUB" + elif [[ -n "${CACHE_IMPORT_GITHUB:-}" ]]; then + IMPORT_GITHUB="$CACHE_IMPORT_GITHUB" + echo "Using import mode from CACHE_IMPORT_GITHUB environment variable: $IMPORT_GITHUB" + else + IMPORT_GITHUB="true" + echo "Using default import mode: $IMPORT_GITHUB" + fi + echo "import-github=$IMPORT_GITHUB" >> "$GITHUB_OUTPUT" + - name: Cache on S3 if: steps.cache-backend.outputs.cache-backend == 's3' uses: runs-on/cache@50350ad4242587b6c8c2baa2e740b1bc11285ff4 # v4.3.0 @@ -142,9 +167,39 @@ runs: restore-keys: ${{ steps.prepare-keys.outputs.branch-restore-keys }} upload-chunk-size: ${{ inputs.upload-chunk-size }} enableCrossOsArchive: ${{ inputs.enableCrossOsArchive }} - fail-on-cache-miss: ${{ inputs.fail-on-cache-miss }} + # When import mode is active, suppress fail-on-cache-miss here: a subsequent step handles it after also attempting the GitHub cache + # fallback. + fail-on-cache-miss: ${{ steps.import-mode.outputs.import-github == 'true' && 'false' || inputs.fail-on-cache-miss }} lookup-only: ${{ inputs.lookup-only }} + - name: Import GitHub cache to S3 (migration fallback) + if: >- + steps.cache-backend.outputs.cache-backend == 's3' && + steps.s3-cache.outputs.cache-hit != 'true' && + steps.import-mode.outputs.import-github == 'true' + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + id: github-import + with: + path: ${{ inputs.path }} + key: ${{ inputs.key }} + restore-keys: ${{ inputs.restore-keys }} + lookup-only: ${{ inputs.lookup-only }} + fail-on-cache-miss: false + + - name: Enforce fail-on-cache-miss after GitHub import fallback + if: >- + steps.cache-backend.outputs.cache-backend == 's3' && + steps.import-mode.outputs.import-github == 'true' && + inputs.fail-on-cache-miss == 'true' && + steps.s3-cache.outputs.cache-hit != 'true' && + steps.github-import.outputs.cache-matched-key == '' + shell: bash + env: + CACHE_KEY: ${{ inputs.key }} + run: | + echo "::error::Cache miss: no cache found in S3 or GitHub for key '${CACHE_KEY}'" + exit 1 + - name: Credential guard for S3 cache save if: steps.cache-backend.outputs.cache-backend == 's3' uses: SonarSource/gh-action_cache/credential-guard@v1