Skip to content

Commit 2bccb6e

Browse files
BUILD-9956 import code from ci-github-actions/cache
1 parent 1170c09 commit 2bccb6e

File tree

3 files changed

+122
-66
lines changed

3 files changed

+122
-66
lines changed

README.md

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,67 @@
1-
# S3 Cache Action
1+
# `gh-action_cache`
22

3-
A GitHub Action that provides branch-specific caching on AWS S3 with intelligent fallback to default branch cache entries.
3+
Adaptive cache action that automatically chooses the appropriate caching backend based on repository visibility and ownership.
44

5-
## Features
5+
- Automatically uses GitHub Actions cache for public repositories
6+
- Uses SonarSource S3 cache for private/internal SonarSource repositories
7+
- Seamless API compatibility with standard GitHub Actions cache
8+
- Supports all standard cache inputs and outputs
9+
- Automatic repository visibility detection
610

7-
- **Branch-specific caching**: Cache entries are prefixed with `GITHUB_HEAD_REF` for granular permissions
8-
- **Intelligent fallback**: Feature branches can fall back to default branch cache when no branch-specific cache exists
9-
- **S3 storage**: Leverages AWS S3 for reliable, scalable cache storage
10-
- **AWS Cognito authentication**: Secure authentication using GitHub Actions OIDC tokens
11-
- **Compatible with actions/cache**: Drop-in replacement with same interface
11+
## Requirements
1212

13-
## Usage
13+
- `jq`
1414

15-
Recommended usage is to use with the
16-
[`SonarSource/ci-github-actions/cache`](https://github.com/SonarSource/ci-github-actions?tab=readme-ov-file#cache) wrapper.
15+
## Usage
1716

1817
```yaml
19-
- uses: SonarSource/ci-github-actions/cache@master
18+
- uses: SonarSource/gh-action_cache@v1
2019
with:
2120
path: |
22-
~/.npm
23-
~/.cache
24-
key: node-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
25-
restore-keys: |
26-
node-${{ runner.os }}
21+
~/.cache/pip
22+
~/.cache/maven
23+
key: cache-${{ runner.os }}-${{ hashFiles('**/requirements.txt', '**/pom.xml') }}
24+
restore-keys: cache-${{ runner.os }}-
2725
```
2826
29-
## How Restore Keys Work
27+
## Inputs
28+
29+
| Input | Description | Required | Default |
30+
|------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------|---------|
31+
| `path` | Files, directories, and wildcard patterns to cache | Yes | |
32+
| `key` | Explicit key for restoring and saving cache | Yes | |
33+
| `restore-keys` | Ordered list of prefix-matched keys for fallback | No | |
34+
| `fallback-branch` | Optional maintenance branch for fallback restore keys (pattern: `branch-*`). If not set, the repository default branch is used. | No | |
35+
| `environment` | Environment to use (dev or prod, S3 cache only) | No | `prod` |
36+
| `upload-chunk-size` | Chunk size for large file uploads (bytes) | No | |
37+
| `enableCrossOsArchive` | Enable cross-OS cache compatibility | No | `false` |
38+
| `fail-on-cache-miss` | Fail workflow if cache entry not found | No | `false` |
39+
| `lookup-only` | Only check cache existence without downloading | No | `false` |
40+
41+
## Outputs
42+
43+
| Output | Description |
44+
|-------------|------------------------------------------------|
45+
| `cache-hit` | Boolean indicating exact match for primary key |
46+
47+
## S3 Cache Action
48+
49+
A GitHub Action that provides branch-specific caching on AWS S3 with intelligent fallback to default branch cache entries.
50+
51+
### Features
52+
53+
- **Branch-specific caching**: Cache entries are prefixed with `GITHUB_HEAD_REF` for granular permissions
54+
- **Intelligent fallback**: Feature branches can fall back to default branch cache when no branch-specific cache exists
55+
- **S3 storage**: Leverages AWS S3 for reliable, scalable cache storage
56+
- **AWS Cognito authentication**: Secure authentication using GitHub Actions OIDC tokens
57+
- **Compatible with actions/cache**: Drop-in replacement with same interface
58+
59+
### How Restore Keys Work
3060

3161
**Important**: This action's restore key behavior differs from the standard GitHub cache action.
3262
To enable fallback to default branch caches, you **must** use the `restore-keys` property.
3363

34-
### Cache Key Resolution Order
64+
#### Cache Key Resolution Order
3565

3666
When you provide `restore-keys`, the action searches for cache entries in this order:
3767

@@ -41,15 +71,14 @@ When you provide `restore-keys`, the action searches for cache entries in this o
4171
- `refs/heads/${DEFAULT_BRANCH}/${restore-key}` (for each restore key, where `DEFAULT_BRANCH` is dynamically obtained from the
4272
repository)
4373

44-
### Example
74+
#### Example
4575

4676
```yaml
47-
- uses: SonarSource/ci-github-actions/cache@master
77+
- uses: SonarSource/gh-action_cache@v1
4878
with:
4979
path: ~/.npm
5080
key: node-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
51-
restore-keys: |
52-
node-${{ runner.os }}
81+
restore-keys: node-${{ runner.os }}-
5382
```
5483

5584
For a feature branch `feature/new-ui`, this will search for:
@@ -58,33 +87,13 @@ For a feature branch `feature/new-ui`, this will search for:
5887
2. `feature/new-ui/node-linux` (branch-specific partial match)
5988
3. `refs/heads/main/node-linux` (default branch fallback, assuming `main` is the repository's default branch)
6089

61-
### Key Differences from Standard Cache Action
90+
#### Key Differences from Standard Cache Action
6291

6392
- **Fallback requires restore-keys**: Without `restore-keys`, the action only looks for branch-specific cache entries
6493
- **Dynamic default branch detection**: The action detects your default branch using the GitHub API and uses it for fallback
6594
- **Branch isolation**: Each branch maintains its own cache namespace, preventing cross-branch cache pollution
6695

67-
## Inputs
68-
69-
| Input | Description | Required | Default |
70-
|------------------------|----------------------------------------------------|----------|---------|
71-
| `path` | Files, directories, and wildcard patterns to cache | Yes | |
72-
| `key` | Explicit key for restoring and saving cache | Yes | |
73-
| `restore-keys` | Ordered list of prefix-matched keys for fallback | No | |
74-
| `fallback-branch` | Optional maintenance branch for fallback restore keys (pattern: `branch-*`). If not set, the repository default branch is used. | No | |
75-
| `environment` | Environment to use (dev or prod) | No | `prod` |
76-
| `upload-chunk-size` | Chunk size for large file uploads (bytes) | No | |
77-
| `enableCrossOsArchive` | Enable cross-OS cache compatibility | No | `false` |
78-
| `fail-on-cache-miss` | Fail workflow if cache entry not found | No | `false` |
79-
| `lookup-only` | Only check cache existence without downloading | No | `false` |
80-
81-
## Outputs
82-
83-
| Output | Description |
84-
|-------------|------------------------------------------------|
85-
| `cache-hit` | Boolean indicating exact match for primary key |
86-
87-
## Environment Configuration
96+
### Environment Configuration
8897

8998
The action supports two environments:
9099

@@ -93,8 +102,13 @@ The action supports two environments:
93102

94103
Each environment has its own preconfigured S3 bucket and AWS Cognito pool for isolation and security.
95104

96-
## Security
105+
### Security
97106

98107
- Uses GitHub Actions OIDC tokens for secure authentication
99108
- No long-lived AWS credentials required
100109
- Branch-specific paths provide isolation between branches
110+
111+
### Cleanup Policy
112+
113+
The AWS S3 bucket lifecycle rules apply to delete the old files. The content from default branches expires in 60 days and for feature
114+
branches in 30 days.

action.yml

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ inputs:
1414
upload-chunk-size:
1515
description: The chunk size used to split up large files during upload, in bytes
1616
enableCrossOsArchive:
17-
description: An optional boolean when enabled, allows windows runners to save or restore caches that can be restored or saved respectively on other platforms
17+
description: When enabled, allows to save or restore caches that can be restored or saved respectively on other platforms
1818
default: false
1919
fail-on-cache-miss:
2020
description: Fail the workflow if cache entry is not found
@@ -31,12 +31,53 @@ inputs:
3131
outputs:
3232
cache-hit:
3333
description: A boolean value to indicate an exact match was found for the primary key
34-
value: ${{ steps.cache.outputs.cache-hit }}
34+
value: ${{ steps.github-cache.outputs.cache-hit || steps.s3-cache.outputs.cache-hit }}
3535

3636
runs:
3737
using: composite
3838
steps:
39+
- name: Determine repository visibility
40+
id: repo-visibility
41+
shell: bash
42+
env:
43+
GITHUB_TOKEN: ${{ github.token }}
44+
REPO_VISIBILITY: ${{ github.event.repository.visibility }}
45+
run: |
46+
# If visibility is not available in the event, try to get it from the API
47+
if [[ -z "$REPO_VISIBILITY" || "$REPO_VISIBILITY" = "null" ]]; then
48+
REPO_VISIBILITY=$(curl -s -H "Authorization: token ${{ github.token }}" \
49+
"https://api.github.com/repos/${{ github.repository }}" | \
50+
jq -r '.visibility // "private"')
51+
fi
52+
echo "Repository visibility: $REPO_VISIBILITY"
53+
54+
if [[ "$REPO_VISIBILITY" == "public" ]]; then
55+
CACHE_BACKEND="github"
56+
echo "Using GitHub cache for public repository"
57+
else
58+
CACHE_BACKEND="s3"
59+
echo "Using S3 cache for private/internal repository"
60+
fi
61+
62+
echo "cache-backend=$CACHE_BACKEND" >> "$GITHUB_OUTPUT"
63+
echo "repo-visibility=$REPO_VISIBILITY" >> "$GITHUB_OUTPUT"
64+
65+
- name: Cache with GitHub Actions (public repos)
66+
if: steps.repo-visibility.outputs.cache-backend == 'github'
67+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
68+
id: github-cache
69+
with:
70+
path: ${{ inputs.path }}
71+
key: ${{ inputs.key }}
72+
restore-keys: ${{ inputs.restore-keys }}
73+
upload-chunk-size: ${{ inputs.upload-chunk-size }}
74+
enableCrossOsArchive: ${{ inputs.enableCrossOsArchive }}
75+
fail-on-cache-miss: ${{ inputs.fail-on-cache-miss }}
76+
lookup-only: ${{ inputs.lookup-only }}
77+
78+
# Cache with S3 (private/internal repos)
3979
- name: Authenticate to AWS
80+
if: steps.repo-visibility.outputs.cache-backend == 's3'
4081
id: aws-auth
4182
shell: bash
4283
env:
@@ -72,31 +113,30 @@ runs:
72113
AWS_ACCESS_KEY_ID=$(echo "$awsCredentials" | jq -r ".Credentials.AccessKeyId")
73114
AWS_SECRET_ACCESS_KEY=$(echo "$awsCredentials" | jq -r ".Credentials.SecretKey")
74115
AWS_SESSION_TOKEN=$(echo "$awsCredentials" | jq -r ".Credentials.SessionToken")
75-
76-
echo "::add-mask::$AWS_ACCESS_KEY_ID"
77-
echo "::add-mask::$AWS_SECRET_ACCESS_KEY"
78-
echo "::add-mask::$AWS_SESSION_TOKEN"
79-
80116
if [[ "$AWS_ACCESS_KEY_ID" == "null" || -z "$AWS_ACCESS_KEY_ID" ]]; then
81117
echo "::error::Failed to obtain AWS Access Key ID"
82118
exit 1
83119
fi
84-
85120
if [[ "$AWS_SECRET_ACCESS_KEY" == "null" || -z "$AWS_SECRET_ACCESS_KEY" ]]; then
86121
echo "::error::Failed to obtain AWS Secret Access Key"
87122
exit 1
88123
fi
89-
90124
if [[ "$AWS_SESSION_TOKEN" == "null" || -z "$AWS_SESSION_TOKEN" ]]; then
91125
echo "::error::Failed to obtain AWS Session Token"
92126
exit 1
93127
fi
128+
echo "::add-mask::$AWS_ACCESS_KEY_ID"
129+
echo "::add-mask::$AWS_SECRET_ACCESS_KEY"
130+
echo "::add-mask::$AWS_SESSION_TOKEN"
94131
95-
echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> "$GITHUB_OUTPUT"
96-
echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> "$GITHUB_OUTPUT"
97-
echo "AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" >> "$GITHUB_OUTPUT"
132+
{
133+
echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID"
134+
echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY"
135+
echo "AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN"
136+
} >> "$GITHUB_OUTPUT"
98137
99138
- name: Prepare cache keys
139+
if: steps.repo-visibility.outputs.cache-backend == 's3'
100140
shell: bash
101141
id: prepare-keys
102142
env:
@@ -105,11 +145,12 @@ runs:
105145
INPUT_FALLBACK_BRANCH: ${{ inputs.fallback-branch }}
106146
GITHUB_TOKEN: ${{ github.token }}
107147
GITHUB_REPOSITORY: ${{ github.repository }}
108-
run: "$GITHUB_ACTION_PATH/scripts/prepare-keys.sh"
148+
run: $GITHUB_ACTION_PATH/scripts/prepare-keys.sh
109149

110150
- name: Cache on S3
151+
if: steps.repo-visibility.outputs.cache-backend == 's3'
111152
uses: runs-on/cache@50350ad4242587b6c8c2baa2e740b1bc11285ff4 # v4.3.0
112-
id: cache
153+
id: s3-cache
113154
env:
114155
RUNS_ON_S3_BUCKET_CACHE: sonarsource-s3-cache-${{ inputs.environment }}-bucket
115156
AWS_DEFAULT_REGION: eu-central-1

scripts/prepare-keys.sh

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ BRANCH_KEY="${BRANCH_NAME}/${INPUT_KEY}"
2222
echo "branch-key=${BRANCH_KEY}" >> "$GITHUB_OUTPUT"
2323

2424
# Process restore keys: keep branch-specific keys and add fallback to default branch
25-
if [ -n "${INPUT_RESTORE_KEYS:-}" ]; then
25+
if [[ -n "${INPUT_RESTORE_KEYS:-}" ]]; then
2626
RESTORE_KEYS=""
27-
2827
# First, add branch-specific restore keys
2928
while IFS= read -r line; do
3029
if [ -n "$line" ]; then
@@ -52,7 +51,7 @@ if [ -n "${INPUT_RESTORE_KEYS:-}" ]; then
5251
main|master|branch-*)
5352
# Add fallback branch restore keys
5453
while IFS= read -r line; do
55-
if [ -n "$line" ]; then
54+
if [[ -n "$line" ]]; then
5655
RESTORE_KEYS="${RESTORE_KEYS}"$'\n'"refs/heads/${FALLBACK_BRANCH}/${line}"
5756
fi
5857
done <<< "$INPUT_RESTORE_KEYS"
@@ -65,7 +64,9 @@ if [ -n "${INPUT_RESTORE_KEYS:-}" ]; then
6564
echo "::warning::Unable to determine fallback branch; skipping fallback restore keys."
6665
fi
6766

68-
echo "branch-restore-keys<<EOF" >> "$GITHUB_OUTPUT"
69-
echo "$RESTORE_KEYS" >> "$GITHUB_OUTPUT"
70-
echo "EOF" >> "$GITHUB_OUTPUT"
67+
{
68+
echo "branch-restore-keys<<EOF"
69+
echo "$RESTORE_KEYS"
70+
echo "EOF"
71+
} >> "$GITHUB_OUTPUT"
7172
fi

0 commit comments

Comments
 (0)