feature/shared-actions #8
15 changed files with 314 additions and 2 deletions
|
|
@ -10,6 +10,9 @@ Shared actions for Forgejo CI/CD pipelines.
|
|||
| [aikido-pr-scan](aikido-pr-scan) | Aikido PR scan |
|
||||
| [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 |
|
||||
| [terraform-validate](terraform-validate) | Validate Terraform configuration files using the official Terraform CLI |
|
||||
|
||||
|
||||
## Security
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ inputs:
|
|||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: ./actions/internal-aikido-full-scan
|
||||
- uses: ./.forgejo/actions/internal-aikido-full-scan
|
||||
with:
|
||||
apikey: ${{ inputs.apikey }}
|
||||
organization: ${{ forgejo.repository_owner }}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ inputs:
|
|||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: ./actions/internal-aikido-pr-scan
|
||||
- uses: ./.forgejo/actions/internal-aikido-pr-scan
|
||||
with:
|
||||
apikey: ${{ inputs.apikey }}
|
||||
organization: ${{ forgejo.repository_owner }}
|
||||
|
|
|
|||
30
pnpm-build/README.md
Normal file
30
pnpm-build/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# pnpm-build
|
||||
|
||||
Action for building and validating with PNPM.
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `working-directory` | No | `.` | Directory containing `package.json` |
|
||||
| `node-version` | No | `24` | Node.js version |
|
||||
| `pnpm-version` | No | `10.33` | pnpm version |
|
||||
| `jfrog-token` | No | `""` | JFrog npm auth token for the Artifactory registry |
|
||||
| `run-scripts` | No | `ci,typecheck,build` | Comma-separated list of `pnpm run` scripts to execute |
|
||||
| `frozen-lockfile` | No | `true` | Pass `--frozen-lockfile` to `pnpm install` |
|
||||
| `check-dedupe` | No | `true` | Run `pnpm dedupe --check` before install |
|
||||
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/pnpm-build@pnpm-build-v1
|
||||
with:
|
||||
working-directory: frontend
|
||||
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Configures the Artifactory npm registry authentication only if `jfrog-token` is provided.
|
||||
- Runs `pnpm dedupe --check` before install when `check-dedupe` is `true`.
|
||||
- Executes each script in `run-scripts` in order via `pnpm run`.
|
||||
81
pnpm-build/action.yml
Normal file
81
pnpm-build/action.yml
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
name: PNPM Build
|
||||
description: >
|
||||
Build and validate frontend using PNPM.
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: Directory containing package.json
|
||||
required: false
|
||||
default: "."
|
||||
node-version:
|
||||
description: Node.js version
|
||||
required: false
|
||||
default: "24"
|
||||
pnpm-version:
|
||||
description: pnpm version
|
||||
required: false
|
||||
default: "10.33"
|
||||
jfrog-token:
|
||||
description: JFrog npm auth token
|
||||
required: false
|
||||
default: ""
|
||||
run-scripts:
|
||||
description: Comma-separated list of pnpm run scripts
|
||||
required: false
|
||||
default: "ci,typecheck,build"
|
||||
frozen-lockfile:
|
||||
description: Pass --frozen-lockfile to pnpm install
|
||||
required: false
|
||||
default: "true"
|
||||
check-dedupe:
|
||||
description: Run pnpm dedupe --check
|
||||
required: false
|
||||
default: "true"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
|
||||
# actions/setup-node v6.4.0 — https://code.forgejo.org/actions/setup-node/commits/tag/v6.4.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
|
||||
# pnpm/action-setup v6.0.3 — https://code.forgejo.org/pnpm/action-setup/commits/tag/v6.0.3
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e
|
||||
with:
|
||||
version: ${{ inputs.pnpm-version }}
|
||||
|
||||
- name: Configure pnpm registry authentication
|
||||
if: ${{ inputs.jfrog-token != '' }}
|
||||
shell: bash
|
||||
env:
|
||||
JFROG_TOKEN: ${{ inputs.jfrog-token }}
|
||||
run: pnpm set //schmalz.jfrog.io/artifactory/api/npm/default-npm/:_authToken "$JFROG_TOKEN"
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
env:
|
||||
PNPM_VERSION: ${{ inputs.pnpm-version }}
|
||||
WORKING_DIR: ${{ inputs.working-directory }}
|
||||
RUN_SCRIPTS: ${{ inputs.run-scripts }}
|
||||
FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }}
|
||||
CHECK_DEDUPE: ${{ inputs.check-dedupe }}
|
||||
run: |
|
||||
if [ "${CHECK_DEDUPE}" = "true" ]; then
|
||||
pnpm --prefix="${WORKING_DIR}" dedupe --check
|
||||
fi
|
||||
|
||||
INSTALL_ARGS=""
|
||||
if [ "${FROZEN_LOCKFILE}" = "true" ]; then
|
||||
INSTALL_ARGS="--frozen-lockfile"
|
||||
fi
|
||||
pnpm --prefix="${WORKING_DIR}" install $INSTALL_ARGS
|
||||
|
||||
IFS=',' read -ra SCRIPTS <<< "${RUN_SCRIPTS}"
|
||||
for script in "${SCRIPTS[@]}"; do
|
||||
pnpm --prefix="${WORKING_DIR}" run "${script}"
|
||||
done
|
||||
39
publish-static-contents/README.md
Normal file
39
publish-static-contents/README.md
Normal 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: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-static-contents@publish-static-contents-v1
|
||||
with:
|
||||
s3_bucket_name: my-bucket
|
||||
cloudfront_distribution_ids: ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }}
|
||||
```
|
||||
|
||||
With versioned static assets:
|
||||
|
||||
```yaml
|
||||
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-static-contents@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.
|
||||
80
publish-static-contents/action.yml
Normal file
80
publish-static-contents/action.yml
Normal 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 '/*'
|
||||
27
terraform-validate/README.md
Normal file
27
terraform-validate/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# terraform-validate
|
||||
|
||||
Validate Terraform configuration files using the official Terraform CLI.
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `terraform-dir` | No | `terraform` | Directory containing `.tf` files |
|
||||
| `terraform-version` | No | `~1.15` | Terraform version to use |
|
||||
| `workspace` | No | `""` | Terraform workspace to use |
|
||||
| `jfrog-token` | No | `""` | JFrog Artifactory token for the Terraform provider registry (`TF_TOKEN_schmalz_jfrog_io`) |
|
||||
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/terraform-validate@terraform-validate-v1
|
||||
with:
|
||||
workspace: stage
|
||||
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Runs `terraform init -backend=false`, `terraform fmt -check -recursive`, and `terraform validate`.
|
||||
- Sets `TF_WORKSPACE` during validate if `workspace` is provided.
|
||||
- Sets `TF_TOKEN_schmalz_jfrog_io` on both `init` and `validate` steps if `jfrog-token` is provided.
|
||||
52
terraform-validate/action.yml
Normal file
52
terraform-validate/action.yml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
name: Terraform Validate
|
||||
description: >
|
||||
Validate Terraform configuration files using the official Terraform CLI.
|
||||
|
||||
inputs:
|
||||
terraform-dir:
|
||||
description: Directory containing .tf files
|
||||
required: false
|
||||
default: terraform
|
||||
terraform-version:
|
||||
description: Terraform version to use
|
||||
required: false
|
||||
default: "~1.15"
|
||||
workspace:
|
||||
description: Terraform workspace to use
|
||||
required: false
|
||||
default: ""
|
||||
jfrog-token:
|
||||
description: JFrog Artifactory token used for Terraform provider registry (sets TF_TOKEN_schmalz_jfrog_io)
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
|
||||
# hashicorp/setup-terraform v4.0.0 — https://github.com/hashicorp/setup-terraform/commits/v4.0.0/
|
||||
- name: Setup Terraform
|
||||
uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85
|
||||
with:
|
||||
terraform_version: ${{ inputs.terraform-version }}
|
||||
|
||||
- name: Terraform Init
|
||||
shell: bash
|
||||
env:
|
||||
TF_DIR: ${{ inputs.terraform-dir }}
|
||||
TF_TOKEN_schmalz_jfrog_io: ${{ inputs.jfrog-token }}
|
||||
run: terraform -chdir=${{ env.TF_DIR }} init -backend=false -no-color
|
||||
|
||||
- name: Terraform Format Check
|
||||
shell: bash
|
||||
env:
|
||||
TF_DIR: ${{ inputs.terraform-dir }}
|
||||
run: terraform -chdir=${{ env.TF_DIR }} fmt -check -recursive
|
||||
|
||||
- name: Terraform Validate
|
||||
shell: bash
|
||||
env:
|
||||
TF_DIR: ${{ inputs.terraform-dir }}
|
||||
TF_WORKSPACE: ${{ inputs.workspace }}
|
||||
TF_TOKEN_schmalz_jfrog_io: ${{ inputs.jfrog-token }}
|
||||
run: terraform -chdir=${{ env.TF_DIR }} validate
|
||||
Loading…
Add table
Add a link
Reference in a new issue