feat: add publish-static-contents action

This commit is contained in:
Michael.Seele@schmalz.de 2026-04-30 13:43:57 +02:00
parent 8271a9b093
commit 0e441e6161
3 changed files with 120 additions and 0 deletions

View file

@ -11,6 +11,7 @@ Shared actions for Forgejo CI/CD pipelines.
| [aws-configure](aws-configure) | Authenticate with AWS via OIDC |
| [checkout](checkout) | Action for checking out a repository |
| [pnpm-build](pnpm-build) | Action for building and validating with PNPM |
| [publish-static-contents](publish-static-contents) | Syncs frontend assets to S3 and invalidates a CloudFront distribution |
## Security

View file

@ -0,0 +1,39 @@
# publish-static-contents
Syncs frontend assets to S3 and invalidates a CloudFront distribution. Optionally manages versioned static builds by cleaning up old versions.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `dist_dir` | No | `frontend/dist` | Path to the frontend dist directory |
| `s3_bucket_name` | Yes | — | Name of the S3 bucket to sync assets to |
| `cloudfront_distribution_ids` | No | `''` | Space-separated list of CloudFront distribution IDs to invalidate |
| `versioned_static_prefix` | No | `''` | S3 prefix under which versioned builds are stored (e.g. `_static`). When set, old versions older than 7 days are deleted, keeping at least the 2 newest |
## Usage
```yaml
- uses: schmalz/shared-actions/.forgejo/actions/publish-static-contents@v1
with:
s3_bucket_name: my-bucket
cloudfront_distribution_ids: ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }}
```
With versioned static assets:
```yaml
- uses: schmalz/shared-actions/.forgejo/actions/publish-static-contents@v1
with:
dist_dir: frontend/dist
s3_bucket_name: my-bucket
cloudfront_distribution_ids: ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }}
versioned_static_prefix: _static
```
## Notes
- When `versioned_static_prefix` is set, all assets are synced with `Cache-Control: public, max-age=31536000, immutable`.
- `index.html` is always synced separately with `Cache-Control: no-cache, max-age=300` so browsers pick up new deployments quickly.
- Old versioned builds are pruned: any version folder older than 7 days is deleted, with at least the 2 newest versions always retained.
- CloudFront invalidation is skipped when `cloudfront_distribution_ids` is empty.

View file

@ -0,0 +1,80 @@
name: Publish to CloudFront
description: Syncs frontend assets to S3 and invalidates the CloudFront distribution
inputs:
dist_dir:
description: Path to the frontend dist directory
required: false
default: frontend/dist
s3_bucket_name:
description: Name of the S3 bucket to sync assets to
required: true
cloudfront_distribution_ids:
description: Space-separated list of CloudFront distribution IDs to invalidate
required: false
default: ''
versioned_static_prefix:
description: S3 prefix under which versioned builds are stored (e.g. _static). When set, old versions older than 7 days are deleted, keeping at least the 2 newest.
required: false
default: ''
runs:
using: composite
steps:
- name: Publish frontend assets
shell: bash
env:
INPUT_DIST_DIR: ${{ inputs.dist_dir }}
INPUT_S3_BUCKET_NAME: ${{ inputs.s3_bucket_name }}
INPUT_VERSIONED_STATIC_PREFIX: ${{ inputs.versioned_static_prefix }}
run: |
CACHE_CONTROL_ARG=""
if [[ -n "${INPUT_VERSIONED_STATIC_PREFIX}" ]]; then
CACHE_CONTROL_ARG="--cache-control 'public, max-age=31536000, immutable'"
fi
EXCLUDE_INDEX_ARG=""
if [[ -n "${INPUT_VERSIONED_STATIC_PREFIX}" && -f "${INPUT_DIST_DIR}/index.html" ]]; then
EXCLUDE_INDEX_ARG="--exclude index.html"
fi
aws s3 sync "${INPUT_DIST_DIR}" "s3://${INPUT_S3_BUCKET_NAME}" $CACHE_CONTROL_ARG $EXCLUDE_INDEX_ARG
- name: Publish index.html without immutable cache
if: ${{ inputs.versioned_static_prefix != '' }}
shell: bash
run: |
if [[ -f "${INPUT_DIST_DIR}/index.html" ]]; then
aws s3 cp "${INPUT_DIST_DIR}/index.html" "s3://${INPUT_S3_BUCKET_NAME}/index.html" \
--cache-control "no-cache, max-age=300" \
--metadata-directive REPLACE
fi
- name: Clean up old versioned static builds
if: ${{ inputs.versioned_static_prefix != '' }}
shell: bash
env:
INPUT_S3_BUCKET_NAME: ${{ inputs.s3_bucket_name }}
INPUT_VERSIONED_STATIC_PREFIX: ${{ inputs.versioned_static_prefix }}
run: |
aws s3 ls "s3://$INPUT_S3_BUCKET_NAME/$INPUT_VERSIONED_STATIC_PREFIX/" \
| grep -oP '(?<=PRE )[0-9]+' \
| sort --stable --reverse \
| tail -n +3 \
| while read version; do
now=$(($(date +%s%N)/1000000))
diff=$(($now-$version))
# delete if older than 7 days
if [ $diff -gt 604800000 ]; then
echo "Deleting $version"
aws s3 rm --recursive "s3://$INPUT_S3_BUCKET_NAME/$INPUT_VERSIONED_STATIC_PREFIX/$version"
fi
done
- name: Invalidate CloudFront
if: ${{ inputs.cloudfront_distribution_ids != '' }}
shell: bash
env:
INPUT_CLOUDFRONT_DISTRIBUTION_IDS: ${{ inputs.cloudfront_distribution_ids }}
run: |
echo "$INPUT_CLOUDFRONT_DISTRIBUTION_IDS" \
| tr ' ' '\n' \
| xargs -I% aws cloudfront create-invalidation --distribution-id % --paths '/*'