Skip to content

Commit 93017d1

Browse files
Re-struct project (#8)
1 parent 465e01c commit 93017d1

50 files changed

Lines changed: 2585 additions & 1252 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 163 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,108 @@
11
---
2-
name: CloudFront Invalidation
3-
description: Creates CloudFront invalidation to clear cache for updated content
2+
name: 'CloudFront Invalidation'
3+
description: 'Create a CloudFront invalidation to clear cache'
44

55
inputs:
66
aws_access_key:
7-
description: AWS access key ID (optional if using OIDC)
7+
description: 'AWS access key ID'
88
required: false
99
aws_secret_key:
10-
description: AWS secret access key (optional if using OIDC)
10+
description: 'AWS secret access key'
1111
required: false
1212
aws_region:
13-
description: AWS region
13+
description: 'AWS region'
1414
required: true
1515
role_to_assume:
16-
description: AWS IAM role ARN to assume (for OIDC authentication)
16+
description: 'AWS IAM role ARN to assume'
1717
required: false
1818

1919
distribution_id:
20-
description: CloudFront distribution ID
20+
description: 'CloudFront distribution ID'
2121
required: true
2222
paths:
2323
description: >
24-
Paths to invalidate (space-separated,
25-
e.g. "/* /index.html /css/*")
24+
Space-separated paths to invalidate (e.g. "/* /index.html /css/*")
2625
required: false
2726
default: "/*"
2827
caller_reference:
2928
description: >
30-
Unique reference for this invalidation
31-
(auto-generated if not provided)
29+
Unique reference for this invalidation (auto-generated if not provided)
3230
required: false
31+
wait_for_completion:
32+
description: 'Wait until invalidation status becomes Completed'
33+
required: false
34+
default: 'false'
35+
36+
show_summary:
37+
description: 'Print summary in the job summary'
38+
required: false
39+
default: 'true'
40+
summary_limit:
41+
description: 'Max number of lines (paths) to show in summary'
42+
required: false
43+
default: '250'
3344

3445
outputs:
3546
invalidation_id:
36-
description: The ID of the created invalidation
47+
description: 'ID of the created invalidation'
3748
value: ${{ steps.invalidate.outputs.invalidation_id }}
3849
status:
39-
description: The status of the invalidation
50+
description: 'Status of the invalidation (InProgress/Completed)'
4051
value: ${{ steps.invalidate.outputs.status }}
52+
caller_reference:
53+
description: 'CallerReference used for this invalidation'
54+
value: ${{ steps.invalidate.outputs.caller_reference }}
4155

4256
runs:
4357
using: composite
4458
steps:
4559
- name: Validate inputs
4660
shell: bash
4761
run: |
48-
set -e
62+
set -euo pipefail
4963
50-
if ! command -v jq &> /dev/null; then
51-
echo "❌ jq is required but not found. Please install jq or use ubuntu-latest runner"
64+
if ! command -v aws >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
65+
echo "❌ AWS CLI and jq are required on the runner (use ubuntu-latest)"
5266
exit 1
5367
fi
5468
5569
if [[ ! "${{ inputs.distribution_id }}" =~ ^E[A-Z0-9]{13}$ ]]; then
56-
echo "❌ Invalid CloudFront distribution ID format: ${{ inputs.distribution_id }}"
57-
echo "Expected format: E + 13 alphanumeric characters (e.g., E1234567890ABC)"
70+
echo "❌ Invalid CloudFront distribution ID: ${{ inputs.distribution_id }}"
71+
echo "Expected pattern: ^E[A-Z0-9]{13}$ (e.g., E1234567890ABC)"
5872
exit 1
5973
fi
6074
6175
PATHS="${{ inputs.paths }}"
62-
if [ -z "$PATHS" ]; then
63-
echo "❌ Paths cannot be empty"
76+
if [[ -z "$PATHS" ]]; then
77+
echo "❌ 'paths' cannot be empty"
6478
exit 1
6579
fi
6680
67-
IFS=' ' read -ra PATHS_ARRAY <<< "$PATHS"
81+
read -r -a PATHS_ARRAY <<< "$PATHS"
6882
PATHS_COUNT=${#PATHS_ARRAY[@]}
69-
if [ $PATHS_COUNT -gt 1000 ]; then
70-
echo "❌ Too many paths ($PATHS_COUNT). CloudFront allows maximum 1000 paths per invalidation"
71-
echo "Consider using fewer paths or /* wildcard"
83+
84+
if (( PATHS_COUNT == 0 )); then
85+
echo "❌ No paths provided"
7286
exit 1
7387
fi
7488
75-
for path in "${PATHS_ARRAY[@]}"; do
76-
if [[ ! "$path" =~ ^/.* ]]; then
77-
echo "❌ Invalid path: $path (must start with /)"
89+
if (( PATHS_COUNT > 1000 )); then
90+
echo "❌ Too many paths ($PATHS_COUNT) — CloudFront allows up to 1000 per invalidation"
91+
echo " Consider using fewer paths or a wildcard like /*"
92+
exit 1
93+
fi
94+
95+
for p in "${PATHS_ARRAY[@]}"; do
96+
if [[ ! "$p" =~ ^/ ]]; then
97+
echo "❌ Invalid path: $p (must start with /)"
7898
exit 1
7999
fi
80100
done
81101
82-
echo "✅ Input validation passed ($PATHS_COUNT paths)"
102+
echo "✅ Input validation passed ($PATHS_COUNT path(s))"
83103
84104
- name: Configure AWS authentication
85-
uses: Mad-Pixels/github-workflows/internal/aws-auth@main
105+
uses: Mad-Pixels/github-workflows/internal/aws-auth@v1
86106
with:
87107
aws_access_key: ${{ inputs.aws_access_key }}
88108
aws_secret_key: ${{ inputs.aws_secret_key }}
@@ -93,61 +113,136 @@ runs:
93113
id: invalidate
94114
shell: bash
95115
run: |
96-
set -e
116+
set -euo pipefail
97117
98-
if [ -z "${{ inputs.caller_reference }}" ]; then
99-
TIMESTAMP=$(date +%s)
100-
SHORT_SHA="${{ github.sha }}"
101-
SHORT_SHA=${SHORT_SHA:0:8}
102-
CALLER_REF="gh-${TIMESTAMP}-${{ github.run_id }}-${SHORT_SHA}"
103-
else
118+
if [[ -n "${{ inputs.caller_reference }}" ]]; then
104119
CALLER_REF="${{ inputs.caller_reference }}"
120+
else
121+
TS=$(date +%s)
122+
SHA="${{ github.sha }}"; SHORT_SHA="${SHA:0:8}"
123+
CALLER_REF="gh-${TS}-${{ github.run_id }}-${SHORT_SHA}"
105124
fi
106125
107-
echo "🚀 Creating CloudFront invalidation..."
108-
echo "Distribution ID: ${{ inputs.distribution_id }}"
109-
echo "Caller Reference: ${CALLER_REF}"
110-
echo "Paths: ${{ inputs.paths }}"
111-
112-
IFS=' ' read -ra PATHS_ARRAY <<< "${{ inputs.paths }}"
126+
read -r -a PATHS_ARRAY <<< "${{ inputs.paths }}"
113127
PATHS_COUNT=${#PATHS_ARRAY[@]}
114-
115128
PATHS_JSON=$(printf '%s\n' "${PATHS_ARRAY[@]}" | jq -R . | jq -s .)
116-
echo "📝 Invalidating $PATHS_COUNT path(s)..."
117129
118130
INVALIDATION_BATCH=$(jq -n \
119131
--argjson paths "$PATHS_JSON" \
120-
--arg caller_ref "$CALLER_REF" \
121-
--argjson quantity "$PATHS_COUNT" \
122-
'{
123-
"Paths": {
124-
"Quantity": $quantity,
125-
"Items": $paths
126-
},
127-
"CallerReference": $caller_ref
128-
}')
129-
130-
INVALIDATION_RESPONSE=$(aws cloudfront create-invalidation \
132+
--arg caller "$CALLER_REF" \
133+
--argjson qty "$PATHS_COUNT" \
134+
'{Paths:{Quantity:$qty,Items:$paths},CallerReference:$caller}')
135+
136+
echo "🚀 Creating CloudFront invalidation"
137+
echo "• Distribution: ${{ inputs.distribution_id }}"
138+
echo "• CallerReference: $CALLER_REF"
139+
echo "• Paths ($PATHS_COUNT): ${{ inputs.paths }}"
140+
141+
RESP=$(aws cloudfront create-invalidation \
131142
--distribution-id "${{ inputs.distribution_id }}" \
132143
--invalidation-batch "$INVALIDATION_BATCH" \
133144
--output json \
134145
--no-cli-pager)
135146
136-
INVALIDATION_ID=$(echo "$INVALIDATION_RESPONSE" | jq -r '.Invalidation.Id')
137-
STATUS=$(echo "$INVALIDATION_RESPONSE" | jq -r '.Invalidation.Status')
147+
ID=$(echo "$RESP" | jq -r '.Invalidation.Id')
148+
STATUS=$(echo "$RESP" | jq -r '.Invalidation.Status')
138149
139-
echo "✅ CloudFront invalidation created successfully!"
140-
echo "Invalidation ID: $INVALIDATION_ID"
141-
echo "Status: $STATUS"
142-
echo "invalidation_id=$INVALIDATION_ID" >> $GITHUB_OUTPUT
143-
echo "status=$STATUS" >> $GITHUB_OUTPUT
150+
echo "invalidation_id=$ID" >> "$GITHUB_OUTPUT"
151+
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
152+
echo "caller_reference=$CALLER_REF" >> "$GITHUB_OUTPUT"
153+
154+
echo "✅ Invalidation created: $ID (status: $STATUS)"
155+
156+
- name: Wait for completion
157+
if: inputs.wait_for_completion == 'true'
158+
shell: bash
159+
run: |
160+
set -euo pipefail
161+
162+
DIST="${{ inputs.distribution_id }}"
163+
ID="${{ steps.invalidate.outputs.invalidation_id }}"
164+
165+
echo "⏳ Waiting for invalidation $ID to become Completed..."
166+
167+
ATTEMPTS=0
168+
MAX_ATTEMPTS=90
169+
while (( ATTEMPTS < MAX_ATTEMPTS )); do
170+
STATUS=$(aws cloudfront get-invalidation \
171+
--distribution-id "$DIST" \
172+
--id "$ID" \
173+
--output json \
174+
--no-cli-pager | jq -r '.Invalidation.Status')
175+
176+
echo " Attempt $((ATTEMPTS+1))/$MAX_ATTEMPTS — status: $STATUS"
177+
if [[ "$STATUS" == "Completed" ]]; then
178+
echo "✅ Invalidation completed"
179+
break
180+
fi
181+
182+
ATTEMPTS=$((ATTEMPTS+1))
183+
sleep 10
184+
done
185+
186+
if (( ATTEMPTS == MAX_ATTEMPTS )); then
187+
echo "⚠️ Timed out waiting for completion — current status: $STATUS"
188+
fi
144189
145190
- name: Summary
191+
if: always() && inputs.show_summary == 'true'
146192
shell: bash
147193
run: |
148-
echo "## 📊 CloudFront Invalidation Summary" >> $GITHUB_STEP_SUMMARY
149-
echo "- **Invalidation ID:** ${{ steps.invalidate.outputs.invalidation_id }}" >> $GITHUB_STEP_SUMMARY
150-
echo "- **Status:** ${{ steps.invalidate.outputs.status }}" >> $GITHUB_STEP_SUMMARY
151-
echo "- **Paths invalidated:** ${{ inputs.paths }}" >> $GITHUB_STEP_SUMMARY
152-
echo "" >> $GITHUB_STEP_SUMMARY
153-
echo "ℹ️ Invalidation started. It may take 10-15 minutes to complete." >> $GITHUB_STEP_SUMMARY
194+
set -euo pipefail
195+
196+
STATUS_ICON="❌"
197+
[[ -n "${{ steps.invalidate.outputs.invalidation_id }}" ]] && STATUS_ICON="✅"
198+
199+
DIST="${{ inputs.distribution_id }}"
200+
ID="${{ steps.invalidate.outputs.invalidation_id }}"
201+
CF_LINK=""
202+
if [[ -n "$DIST" && -n "$ID" ]]; then
203+
CF_LINK="https://console.aws.amazon.com/cloudfront/v4/home#/distributions/${DIST}/invalidations/${ID}"
204+
fi
205+
206+
LIMIT="${{ inputs.summary_limit }}"
207+
[[ "$LIMIT" =~ ^[0-9]+$ ]] || LIMIT="250"
208+
209+
PATHS_RAW='${{ inputs.paths }}'
210+
211+
set -f
212+
IFS=' ' read -r -a P_ARR <<< "$PATHS_RAW"
213+
set +f
214+
215+
TOTAL="${#P_ARR[@]}"
216+
SHOW="$LIMIT"; (( TOTAL < LIMIT )) && SHOW="$TOTAL"
217+
218+
{
219+
echo "## 📊 CloudFront Invalidation ${STATUS_ICON}"
220+
echo "- **Invalidation ID:** \`${ID:-N/A}\`"
221+
echo "- **Status:** \`${{ steps.invalidate.outputs.status || 'N/A' }}\`"
222+
echo "- **CallerReference:** \`${{ steps.invalidate.outputs.caller_reference || 'auto' }}\`"
223+
echo "- **Distribution:** \`${DIST}\`"
224+
if [[ -n "$CF_LINK" ]]; then
225+
echo "- **Console:** ${CF_LINK}"
226+
fi
227+
228+
echo ""
229+
if (( TOTAL > 0 )); then
230+
if (( TOTAL <= LIMIT )); then
231+
echo "### Paths"
232+
else
233+
echo "### Paths (first ${LIMIT} of ${TOTAL})"
234+
fi
235+
echo '```'
236+
for ((i=0;i<SHOW;i++)); do
237+
printf '%s\n' "${P_ARR[i]}"
238+
done
239+
echo '```'
240+
fi
241+
242+
echo ""
243+
if [[ "${{ inputs.wait_for_completion }}" == "true" ]]; then
244+
echo "⏱️ Waited for completion: **true**"
245+
else
246+
echo "⏱️ Waited for completion: **false** (status may change to *Completed* in ~10–15 minutes)"
247+
fi
248+
} >> "$GITHUB_STEP_SUMMARY"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Invalidate CloudFront Cache
3+
4+
on:
5+
workflow_dispatch:
6+
7+
jobs:
8+
invalidate:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
id-token: write
12+
contents: read
13+
steps:
14+
- name: Invalidate CloudFront Cache
15+
uses: Mad-Pixels/github-workflows/actions/cloudfront-invalidation@v1
16+
with:
17+
aws_region: us-east-1
18+
role_to_assume: arn:aws:iam::123456789012:role/GHA-OIDC
19+
distribution_id: E1234567890ABC
20+
paths: "/* /index.html /assets/*"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# ⚡️ CloudFront Invalidation
2+
Create a CloudFront invalidation
3+
4+
## ✅ Features
5+
- Create invalidations for one or many paths (supports wildcards)
6+
- Auto‑generated caller reference (or provide your own)
7+
8+
## 📖 Related Documentation
9+
- CloudFront Invalidation API: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html
10+
- AWS CLI cloudfront create‑invalidation: https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html
11+
- GitHub OIDC for AWS: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html
12+
13+
## 🚀 Prerequisites
14+
Your workflow must:
15+
- Run on `ubuntu-latest`
16+
- Have access to AWS credentials or an assumable IAM role
17+
- Have a valid CloudFront distribution ID
18+
19+
## 🔧 Quick Example
20+
```yaml
21+
name: Invalidate CloudFront Cache
22+
23+
on:
24+
workflow_dispatch:
25+
26+
jobs:
27+
invalidate:
28+
runs-on: ubuntu-latest
29+
permissions:
30+
id-token: write
31+
contents: read
32+
steps:
33+
- name: Create invalidation via OIDC
34+
uses: Mad-Pixels/github-workflows/actions/cloudfront-invalidation@v1
35+
with:
36+
aws_region: us-east-1
37+
role_to_assume: arn:aws:iam::123456789012:role/GHA-OIDC
38+
distribution_id: E1234567890ABC
39+
paths: "/* /index.html /assets/*"
40+
```
41+
42+
## 📥 Inputs
43+
| **Name** | **Required** | **Description** | **Default** |
44+
|--------------------|--------------|---------------------------------------------------------------------------------------------------------|-------------|
45+
| `aws_region` | ✅ Yes | AWS region (used by the CLI) | - |
46+
| `distribution_id` | ✅ Yes | CloudFront distribution ID (format: E + 13 alphanumeric chars, e.g. `E1234567890ABC`) | - |
47+
| `aws_access_key` | ❌ No | AWS access key ID (optional if using OIDC) | - |
48+
| `aws_secret_key` | ❌ No | AWS secret access key (optional if using OIDC) | - |
49+
| `role_to_assume` | ❌ No | AWS IAM role ARN to assume (OIDC) | - |
50+
| `paths` | ❌ No | Space‑separated list of paths to invalidate (must start with `/`; max 1000 entries; wildcards allowed) | `/*` |
51+
| `caller_reference` | ❌ No | Custom caller reference for idempotency (auto‑generated if not provided) | - |
52+
| `show_summary` | ❌ No | Print summary with task output in job summary | `true` |
53+
| `summary_limit` | ❌ No | Max number of output lines to show in summary | `250` |
54+
55+
## 📤 Outputs
56+
| **Name** | **Description** |
57+
|-------------------|-------------------------------------|
58+
| `invalidation_id` | ID of the created invalidation |
59+
| `status` | Status returned by CloudFront |
60+
| `caller_reference`| Reference used for this invalidation|
61+
62+
## 📋 Examples
63+
[View example →](./examples/base.yml)
64+

0 commit comments

Comments
 (0)