Compare commits
5 commits
main
...
wip/shared
| Author | SHA1 | Date | |
|---|---|---|---|
| dd0a4f28dd | |||
| 76ebe0ec65 | |||
| a09a422251 | |||
| 553786f000 | |||
| ffffecd074 |
39 changed files with 3005 additions and 1 deletions
23
.forgejo/workflows/validate-shared-actions.yml
Normal file
23
.forgejo/workflows/validate-shared-actions.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
name: validate-shared-actions
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-shared-actions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Validate shared action metadata
|
||||||
|
uses: mpalmer/action-validator@v0.9.0
|
||||||
|
with:
|
||||||
|
version: 0.9.0
|
||||||
|
patterns: |
|
||||||
|
:(glob)**/action.yml
|
||||||
|
:(glob)**/action.yaml
|
||||||
1280
2026-04-17-forgejo-shared-actions-migration.md
Normal file
1280
2026-04-17-forgejo-shared-actions-migration.md
Normal file
File diff suppressed because it is too large
Load diff
19
README.md
19
README.md
|
|
@ -6,7 +6,24 @@ Shared composite actions for Forgejo CI/CD pipelines.
|
||||||
|
|
||||||
| Action | Description |
|
| Action | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| | |
|
| [aikido-full-scan](.github/actions/aikido-full-scan) | Run a full Aikido security scan (for nightly/scheduled runs) |
|
||||||
|
| [aikido-pr-scan](.github/actions/aikido-pr-scan) | Run Aikido security scan on a PR in gating mode (fails on new vulnerabilities) |
|
||||||
|
| [aws-configure](.github/actions/aws-configure) | Authenticate with AWS via OIDC |
|
||||||
|
| [aws-lambda-update](.github/actions/aws-lambda-update) | Update Lambda function alias to a new version, optionally wait for provisioned concurrency |
|
||||||
|
| [aws-s3-sync](.github/actions/aws-s3-sync) | Sync build artifacts to S3, clean up old versioned assets, optionally invalidate CloudFront |
|
||||||
|
| [cloudfront-invalidate](.github/actions/cloudfront-invalidate) | Invalidate one or more CloudFront distributions |
|
||||||
|
| [docker-build-push](.github/actions/docker-build-push) | Build Docker image and push to JFrog with semver tags (major, minor, patch) |
|
||||||
|
| [helm-deploy](.github/actions/helm-deploy) | Deploy a service to Kubernetes via Helm over SSH |
|
||||||
|
| [maven-build](.github/actions/maven-build) | Run Maven build — verify-only (PRs) or package+jib push (deploy) |
|
||||||
|
| [playwright-e2e](.github/actions/playwright-e2e) | Run Playwright E2E tests with optional sharding, upload results to S3 |
|
||||||
|
| [pnpm-build](.github/actions/pnpm-build) | Set up pnpm, authenticate JFrog npm registry, install deps, run scripts |
|
||||||
|
| [publish-npm-package](.github/actions/publish-npm-package) | Build and publish npm package to JFrog Artifactory |
|
||||||
|
| [publish-rust-crate](.github/actions/publish-rust-crate) | Build, test, and publish Rust crate to JFrog Cargo registry |
|
||||||
|
| [rust-build](.github/actions/rust-build) | Run Rust CI — fmt, clippy, tests, optional cross-compilation |
|
||||||
|
| [secrets-inject](.github/actions/secrets-inject) | Append a secrets file to a Java .properties file |
|
||||||
|
| [terraform-apply](.github/actions/terraform-apply) | Full Terraform init + workspace + apply + output capture |
|
||||||
|
| [terraform-module-publish](.github/actions/terraform-module-publish) | Zip a Terraform module and publish to JFrog Artifactory |
|
||||||
|
| [terraform-validate](.github/actions/terraform-validate) | Validate Terraform code without backend (for PR checks) |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
||||||
44
aikido-full-scan/README.md
Normal file
44
aikido-full-scan/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# aikido-full-scan
|
||||||
|
|
||||||
|
Run a full Aikido security scan — intended for nightly or scheduled runs.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `aikido-api-key` | Yes | | Aikido CI API key (`AIK_CI_xxx`) |
|
||||||
|
| `branch-name` | Yes | | Branch to scan (`dev` for applications, `main` for libraries) |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aikido-full-scan@v1
|
||||||
|
with:
|
||||||
|
aikido-api-key: ${{ secrets.AIKIDO_API_KEY }}
|
||||||
|
branch-name: dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nightly schedule example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
aikido-scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aikido-full-scan@v1
|
||||||
|
with:
|
||||||
|
aikido-api-key: ${{ secrets.AIKIDO_API_KEY }}
|
||||||
|
branch-name: dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Uses the `aikidosecurity/local-scanner` Docker image to scan inside the CI runner.
|
||||||
|
- Performs a full scan (all scan types: code, dependencies, IaC, secrets) and uploads results to the Aikido dashboard.
|
||||||
|
- Repository name is auto-detected from Forgejo environment variables.
|
||||||
|
- Does not run in gating mode — the scan reports findings but does not fail the workflow unless the scanner itself errors.
|
||||||
29
aikido-full-scan/action.yml
Normal file
29
aikido-full-scan/action.yml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
name: aikido-full-scan
|
||||||
|
description: Run a full Aikido security scan (for nightly/scheduled runs)
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
aikido-api-key:
|
||||||
|
description: 'Aikido CI API key (AIK_CI_xxx)'
|
||||||
|
required: true
|
||||||
|
branch-name:
|
||||||
|
description: 'Branch to scan (e.g., dev for applications, main for libraries)'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Aikido full scan
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
AIKIDO_API_KEY: ${{ inputs.aikido-api-key }}
|
||||||
|
INPUT_BRANCH_NAME: ${{ inputs.branch-name }}
|
||||||
|
run: |
|
||||||
|
REPO_NAME="${GITHUB_REPOSITORY##*/}"
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
-v "$GITHUB_WORKSPACE:/repo" \
|
||||||
|
-e AIKIDO_API_KEY \
|
||||||
|
aikidosecurity/local-scanner:latest \
|
||||||
|
scan /repo \
|
||||||
|
--repositoryname "$REPO_NAME" \
|
||||||
|
--branchname "$INPUT_BRANCH_NAME"
|
||||||
26
aikido-pr-scan/README.md
Normal file
26
aikido-pr-scan/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# aikido-pr-scan
|
||||||
|
|
||||||
|
Run Aikido security scan on a PR in gating mode — fails if new vulnerabilities are introduced.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `aikido-api-key` | Yes | | Aikido CI API key (`AIK_CI_xxx`) |
|
||||||
|
| `fail-on` | No | `high` | Minimum severity to fail on (`low`, `medium`, `high`, `critical`) |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aikido-pr-scan@v1
|
||||||
|
with:
|
||||||
|
aikido-api-key: ${{ secrets.AIKIDO_API_KEY }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Uses the `aikidosecurity/local-scanner` Docker image to scan inside the CI runner.
|
||||||
|
- Runs in PR gating mode: only detects **new** vulnerabilities introduced by the PR (diff-based).
|
||||||
|
- Repository name and branch are auto-detected from Forgejo environment variables.
|
||||||
|
- The API key is passed via environment variable, never interpolated in shell commands.
|
||||||
|
- Base and head commit IDs are derived from `GITHUB_BASE_SHA` and `GITHUB_SHA`.
|
||||||
37
aikido-pr-scan/action.yml
Normal file
37
aikido-pr-scan/action.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
name: aikido-pr-scan
|
||||||
|
description: Run Aikido security scan on a PR in gating mode (fails on new vulnerabilities)
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
aikido-api-key:
|
||||||
|
description: 'Aikido CI API key (AIK_CI_xxx)'
|
||||||
|
required: true
|
||||||
|
fail-on:
|
||||||
|
description: 'Minimum severity to fail on (low, medium, high, critical)'
|
||||||
|
required: false
|
||||||
|
default: 'high'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Aikido PR scan
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
AIKIDO_API_KEY: ${{ inputs.aikido-api-key }}
|
||||||
|
INPUT_FAIL_ON: ${{ inputs.fail-on }}
|
||||||
|
run: |
|
||||||
|
REPO_NAME="${GITHUB_REPOSITORY##*/}"
|
||||||
|
BRANCH_NAME="${GITHUB_HEAD_REF}"
|
||||||
|
BASE_COMMIT="${GITHUB_BASE_SHA:-$(git rev-parse HEAD~1)}"
|
||||||
|
HEAD_COMMIT="${GITHUB_SHA}"
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
-v "$GITHUB_WORKSPACE:/repo" \
|
||||||
|
-e AIKIDO_API_KEY \
|
||||||
|
aikidosecurity/local-scanner:latest \
|
||||||
|
scan /repo \
|
||||||
|
--repositoryname "$REPO_NAME" \
|
||||||
|
--branchname "$BRANCH_NAME" \
|
||||||
|
--gating-mode pr \
|
||||||
|
--fail-on "$INPUT_FAIL_ON" \
|
||||||
|
--base-commit-id "$BASE_COMMIT" \
|
||||||
|
--head-commit-id "$HEAD_COMMIT"
|
||||||
25
aws-configure/README.md
Normal file
25
aws-configure/README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# aws-configure
|
||||||
|
|
||||||
|
Authenticate with AWS via OIDC and export credentials to the environment.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `role-arn` | Yes | | Full IAM role ARN |
|
||||||
|
| `aws-profile` | No | `default` | Profile name written to `~/.aws/config` |
|
||||||
|
| `region` | No | `eu-central-1` | AWS region |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
with:
|
||||||
|
role-arn: arn:aws:iam::123456789012:role/my-role
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Requires `enable-openid-connect: true` on the Forgejo runner job.
|
||||||
|
- Credentials are exported via `$FORGEJO_ENV` so subsequent steps can use them.
|
||||||
|
- When `aws-profile` is not `default`, a named AWS CLI profile is also configured.
|
||||||
45
aws-configure/action.yml
Normal file
45
aws-configure/action.yml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
name: aws-configure
|
||||||
|
description: Authenticate with AWS via OIDC
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
role-arn:
|
||||||
|
description: Full IAM role ARN
|
||||||
|
required: true
|
||||||
|
aws-profile:
|
||||||
|
description: Profile name written to ~/.aws/config
|
||||||
|
required: false
|
||||||
|
default: default
|
||||||
|
region:
|
||||||
|
description: AWS region
|
||||||
|
required: false
|
||||||
|
default: eu-central-1
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
OIDC_TOKEN=$(curl -sf \
|
||||||
|
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
|
||||||
|
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq -r .value)
|
||||||
|
|
||||||
|
CREDS=$(aws sts assume-role-with-web-identity \
|
||||||
|
--role-arn "$INPUT_ROLE_ARN" \
|
||||||
|
--role-session-name forgejo-ci \
|
||||||
|
--web-identity-token "$OIDC_TOKEN" \
|
||||||
|
--region "$INPUT_REGION" \
|
||||||
|
--query 'Credentials' --output json)
|
||||||
|
|
||||||
|
mkdir -p ~/.aws
|
||||||
|
|
||||||
|
echo "AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r .AccessKeyId)" >> $FORGEJO_ENV
|
||||||
|
echo "AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r .SecretAccessKey)" >> $FORGEJO_ENV
|
||||||
|
echo "AWS_SESSION_TOKEN=$(echo $CREDS | jq -r .SessionToken)" >> $FORGEJO_ENV
|
||||||
|
echo "AWS_DEFAULT_REGION=$INPUT_REGION" >> $FORGEJO_ENV
|
||||||
|
|
||||||
|
if [ "$INPUT_AWS_PROFILE" != "default" ]; then
|
||||||
|
aws configure set aws_access_key_id "$(echo $CREDS | jq -r .AccessKeyId)" --profile "$INPUT_AWS_PROFILE"
|
||||||
|
aws configure set aws_secret_access_key "$(echo $CREDS | jq -r .SecretAccessKey)" --profile "$INPUT_AWS_PROFILE"
|
||||||
|
aws configure set aws_session_token "$(echo $CREDS | jq -r .SessionToken)" --profile "$INPUT_AWS_PROFILE"
|
||||||
|
aws configure set region "$INPUT_REGION" --profile "$INPUT_AWS_PROFILE"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
50
aws-lambda-update/README.md
Normal file
50
aws-lambda-update/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# aws-lambda-update
|
||||||
|
|
||||||
|
Update Lambda function alias to a new version, optionally wait for provisioned concurrency.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `function-name` | Yes | | Lambda function name |
|
||||||
|
| `function-version` | Yes | | Lambda version number |
|
||||||
|
| `alias-name` | Yes | | Alias name |
|
||||||
|
| `aws-role-arn` | Yes | | IAM role via OIDC |
|
||||||
|
| `wait-provisioned-concurrency` | No | `false` | Poll until provisioned concurrency is READY |
|
||||||
|
| `aws-profile` | No | `default` | AWS CLI profile name |
|
||||||
|
| `region` | No | `eu-central-1` | AWS region |
|
||||||
|
| `lambda-alias-updates-json` | No | `""` | JSON array of `{function_name, version, alias_name}` objects for batch updates |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-lambda-update@v1
|
||||||
|
with:
|
||||||
|
function-name: my-function
|
||||||
|
function-version: "42"
|
||||||
|
alias-name: live
|
||||||
|
aws-role-arn: ${{ secrets.AWS_ROLE_ARN }}
|
||||||
|
wait-provisioned-concurrency: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Batch update
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-lambda-update@v1
|
||||||
|
with:
|
||||||
|
function-name: unused
|
||||||
|
function-version: "0"
|
||||||
|
alias-name: unused
|
||||||
|
aws-role-arn: ${{ secrets.AWS_ROLE_ARN }}
|
||||||
|
lambda-alias-updates-json: |
|
||||||
|
[
|
||||||
|
{"function_name": "fn-a", "version": "3", "alias_name": "live"},
|
||||||
|
{"function_name": "fn-b", "version": "7", "alias_name": "live"}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- When `lambda-alias-updates-json` is set, the single-alias inputs (`function-name`, `function-version`, `alias-name`) are ignored.
|
||||||
|
- Provisioned concurrency polling checks every 5 seconds and fails the step if status becomes `FAILED`.
|
||||||
|
- Uses `aws-configure` internally for OIDC authentication.
|
||||||
95
aws-lambda-update/action.yml
Normal file
95
aws-lambda-update/action.yml
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
name: aws-lambda-update
|
||||||
|
description: Update Lambda function alias to a new version, optionally wait for provisioned concurrency
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
function-name:
|
||||||
|
description: Lambda function name
|
||||||
|
required: true
|
||||||
|
function-version:
|
||||||
|
description: Lambda version number
|
||||||
|
required: true
|
||||||
|
alias-name:
|
||||||
|
description: Alias name
|
||||||
|
required: true
|
||||||
|
wait-provisioned-concurrency:
|
||||||
|
description: Poll until provisioned concurrency is READY
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
aws-role-arn:
|
||||||
|
description: IAM role via OIDC
|
||||||
|
required: true
|
||||||
|
aws-profile:
|
||||||
|
description: AWS CLI profile name
|
||||||
|
required: false
|
||||||
|
default: default
|
||||||
|
region:
|
||||||
|
description: AWS region
|
||||||
|
required: false
|
||||||
|
default: eu-central-1
|
||||||
|
lambda-alias-updates-json:
|
||||||
|
description: JSON array of {function_name, version, alias_name} objects; when set, applies all entries instead of single alias
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
with:
|
||||||
|
role-arn: ${{ inputs.aws-role-arn }}
|
||||||
|
aws-profile: ${{ inputs.aws-profile }}
|
||||||
|
region: ${{ inputs.region }}
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
AWS_PROFILE="${{ inputs.aws-profile }}"
|
||||||
|
WAIT_PC="${{ inputs.wait-provisioned-concurrency }}"
|
||||||
|
|
||||||
|
update_alias() {
|
||||||
|
local fn="$1" ver="$2" alias="$3"
|
||||||
|
echo "Updating alias '$alias' on '$fn' to version $ver"
|
||||||
|
aws lambda update-alias \
|
||||||
|
--function-name "$fn" \
|
||||||
|
--name "$alias" \
|
||||||
|
--function-version "$ver" \
|
||||||
|
--profile "$AWS_PROFILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_provisioned_concurrency() {
|
||||||
|
local fn="$1" alias="$2"
|
||||||
|
echo "Waiting for provisioned concurrency on '$fn' alias '$alias'..."
|
||||||
|
while true; do
|
||||||
|
STATUS=$(aws lambda get-provisioned-concurrency-config \
|
||||||
|
--function-name "$fn" \
|
||||||
|
--qualifier "$alias" \
|
||||||
|
--profile "$AWS_PROFILE" \
|
||||||
|
--query 'Status' --output text 2>/dev/null || echo "NOT_FOUND")
|
||||||
|
echo " Status: $STATUS"
|
||||||
|
if [ "$STATUS" = "READY" ]; then
|
||||||
|
break
|
||||||
|
elif [ "$STATUS" = "FAILED" ]; then
|
||||||
|
echo "ERROR: Provisioned concurrency failed for '$fn' alias '$alias'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
UPDATES_JSON='${{ inputs.lambda-alias-updates-json }}'
|
||||||
|
|
||||||
|
if [ -n "$UPDATES_JSON" ]; then
|
||||||
|
echo "$UPDATES_JSON" | jq -c '.[]' | while read -r entry; do
|
||||||
|
FN=$(echo "$entry" | jq -r '.function_name')
|
||||||
|
VER=$(echo "$entry" | jq -r '.version')
|
||||||
|
ALIAS=$(echo "$entry" | jq -r '.alias_name')
|
||||||
|
update_alias "$FN" "$VER" "$ALIAS"
|
||||||
|
if [ "$WAIT_PC" = "true" ]; then
|
||||||
|
wait_provisioned_concurrency "$FN" "$ALIAS"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
update_alias "${{ inputs.function-name }}" "${{ inputs.function-version }}" "${{ inputs.alias-name }}"
|
||||||
|
if [ "$WAIT_PC" = "true" ]; then
|
||||||
|
wait_provisioned_concurrency "${{ inputs.function-name }}" "${{ inputs.alias-name }}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
31
aws-s3-sync/README.md
Normal file
31
aws-s3-sync/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# aws-s3-sync
|
||||||
|
|
||||||
|
Sync build artifacts to S3, clean up old versioned assets, and optionally invalidate CloudFront.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `source-dir` | Yes | | Local path to sync |
|
||||||
|
| `s3-bucket` | Yes | | Target S3 bucket name |
|
||||||
|
| `aws-role-arn` | Yes | | IAM role ARN for OIDC authentication |
|
||||||
|
| `cache-control` | No | `public, max-age=31536000, immutable` | HTTP `Cache-Control` header |
|
||||||
|
| `aws-profile` | No | `default` | AWS CLI profile name |
|
||||||
|
| `cloudfront-distribution-ids-file` | No | `""` | Path to file with space-separated CloudFront distribution IDs |
|
||||||
|
| `retention-days` | No | `7` | Delete `_static/<timestamp>` prefixes older than this many days (`0` to skip) |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-s3-sync@v1
|
||||||
|
with:
|
||||||
|
source-dir: dist/
|
||||||
|
s3-bucket: my-app-bucket
|
||||||
|
aws-role-arn: arn:aws:iam::123456789012:role/my-role
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Requires `enable-openid-connect: true` on the job.
|
||||||
|
- Old assets under `_static/` are cleaned up based on `retention-days` by parsing timestamp-named prefixes.
|
||||||
|
- CloudFront invalidation uses `/*` path and is triggered only when `cloudfront-distribution-ids-file` points to an existing file.
|
||||||
71
aws-s3-sync/action.yml
Normal file
71
aws-s3-sync/action.yml
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
name: aws-s3-sync
|
||||||
|
description: Sync build artifacts to S3, clean up old versioned assets, optionally invalidate CloudFront
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
source-dir:
|
||||||
|
description: Local path to sync
|
||||||
|
required: true
|
||||||
|
s3-bucket:
|
||||||
|
description: Target S3 bucket name
|
||||||
|
required: true
|
||||||
|
cache-control:
|
||||||
|
description: HTTP Cache-Control header
|
||||||
|
required: false
|
||||||
|
default: "public, max-age=31536000, immutable"
|
||||||
|
aws-role-arn:
|
||||||
|
description: IAM role via OIDC
|
||||||
|
required: true
|
||||||
|
aws-profile:
|
||||||
|
description: AWS CLI profile name
|
||||||
|
required: false
|
||||||
|
default: default
|
||||||
|
cloudfront-distribution-ids-file:
|
||||||
|
description: Path to file with space-separated CF distribution IDs. If set, creates invalidations.
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
retention-days:
|
||||||
|
description: Delete versioned _static/<timestamp> prefixes older than this many days (0 = skip)
|
||||||
|
required: false
|
||||||
|
default: "7"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
with:
|
||||||
|
role-arn: ${{ inputs.aws-role-arn }}
|
||||||
|
aws-profile: ${{ inputs.aws-profile }}
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
SOURCE_DIR="${{ inputs.source-dir }}"
|
||||||
|
S3_BUCKET="${{ inputs.s3-bucket }}"
|
||||||
|
CACHE_CONTROL="${{ inputs.cache-control }}"
|
||||||
|
AWS_PROFILE="${{ inputs.aws-profile }}"
|
||||||
|
RETENTION_DAYS="${{ inputs.retention-days }}"
|
||||||
|
CF_IDS_FILE="${{ inputs.cloudfront-distribution-ids-file }}"
|
||||||
|
|
||||||
|
# Sync
|
||||||
|
aws s3 sync "$SOURCE_DIR" "s3://$S3_BUCKET" \
|
||||||
|
--cache-control "$CACHE_CONTROL" \
|
||||||
|
--profile "$AWS_PROFILE"
|
||||||
|
|
||||||
|
# Cleanup old _static/ prefixes if retention-days > 0
|
||||||
|
if [ "$RETENTION_DAYS" -gt 0 ]; then
|
||||||
|
CUTOFF=$(date -d "-${RETENTION_DAYS} days" +%s)
|
||||||
|
aws s3 ls "s3://$S3_BUCKET/_static/" --profile "$AWS_PROFILE" | while read -r line; do
|
||||||
|
PREFIX=$(echo "$line" | awk '{print $2}')
|
||||||
|
# Extract timestamp from prefix name, compare to cutoff, delete if old
|
||||||
|
TIMESTAMP=$(echo "$PREFIX" | tr -d '/')
|
||||||
|
if [ $(date -d "$TIMESTAMP" +%s 2>/dev/null || echo 0) -lt "$CUTOFF" ]; then
|
||||||
|
aws s3 rm "s3://$S3_BUCKET/_static/$PREFIX" --recursive --profile "$AWS_PROFILE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CloudFront invalidation
|
||||||
|
if [ -n "$CF_IDS_FILE" ] && [ -f "$CF_IDS_FILE" ]; then
|
||||||
|
for DIST_ID in $(cat "$CF_IDS_FILE"); do
|
||||||
|
aws cloudfront create-invalidation --distribution-id "$DIST_ID" --paths "/*" --profile "$AWS_PROFILE"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
27
cloudfront-invalidate/README.md
Normal file
27
cloudfront-invalidate/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# cloudfront-invalidate
|
||||||
|
|
||||||
|
Invalidate one or more CloudFront distributions.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `distribution-ids` | Yes | | Space-separated CloudFront distribution IDs, or path to a file containing them |
|
||||||
|
| `aws-role-arn` | Yes | | IAM role via OIDC |
|
||||||
|
| `paths` | No | `/*` | Invalidation paths |
|
||||||
|
| `aws-profile` | No | `default` | AWS CLI profile name |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/cloudfront-invalidate@v1
|
||||||
|
with:
|
||||||
|
distribution-ids: E1234567890ABC E0987654321XYZ
|
||||||
|
aws-role-arn: ${{ secrets.AWS_ROLE_ARN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `distribution-ids` can be literal IDs or a path to a file containing them (one per line or space-separated).
|
||||||
|
- Each distribution is invalidated separately in a loop.
|
||||||
|
- Uses `aws-configure` internally for OIDC authentication.
|
||||||
46
cloudfront-invalidate/action.yml
Normal file
46
cloudfront-invalidate/action.yml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
name: cloudfront-invalidate
|
||||||
|
description: Invalidate one or more CloudFront distributions
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
distribution-ids:
|
||||||
|
description: Space-separated CloudFront distribution IDs, or path to file containing them
|
||||||
|
required: true
|
||||||
|
paths:
|
||||||
|
description: Invalidation paths
|
||||||
|
required: false
|
||||||
|
default: "/*"
|
||||||
|
aws-role-arn:
|
||||||
|
description: IAM role via OIDC
|
||||||
|
required: true
|
||||||
|
aws-profile:
|
||||||
|
description: AWS CLI profile name
|
||||||
|
required: false
|
||||||
|
default: default
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
with:
|
||||||
|
role-arn: ${{ inputs.aws-role-arn }}
|
||||||
|
aws-profile: ${{ inputs.aws-profile }}
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
DISTRIBUTION_IDS="${{ inputs.distribution-ids }}"
|
||||||
|
PATHS="${{ inputs.paths }}"
|
||||||
|
AWS_PROFILE="${{ inputs.aws-profile }}"
|
||||||
|
|
||||||
|
# Check if distribution-ids is a file path or literal IDs
|
||||||
|
if [ -f "$DISTRIBUTION_IDS" ]; then
|
||||||
|
IDS=$(cat "$DISTRIBUTION_IDS")
|
||||||
|
else
|
||||||
|
IDS="$DISTRIBUTION_IDS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for DIST_ID in $IDS; do
|
||||||
|
aws cloudfront create-invalidation \
|
||||||
|
--distribution-id "$DIST_ID" \
|
||||||
|
--paths "$PATHS" \
|
||||||
|
--profile "$AWS_PROFILE"
|
||||||
|
done
|
||||||
|
shell: bash
|
||||||
30
docker-build-push/README.md
Normal file
30
docker-build-push/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# docker-build-push
|
||||||
|
|
||||||
|
Build Docker image and push to JFrog with semver tags.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `image-name` | Yes | | Image name (e.g., `default-docker/db-devcontainer-base`) |
|
||||||
|
| `version-tag` | Yes | | Full semver tag (e.g., `1.2.3`) |
|
||||||
|
| `jfrog-token` | Yes | | JFrog registry password/token |
|
||||||
|
| `registry` | No | `schmalz.jfrog.io` | Docker registry |
|
||||||
|
| `dockerfile` | No | `Dockerfile` | Path to Dockerfile |
|
||||||
|
| `context` | No | `.` | Docker build context |
|
||||||
|
| `jfrog-user` | No | `jfrog-cicd-user` | JFrog registry user |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/docker-build-push@v1
|
||||||
|
with:
|
||||||
|
image-name: default-docker/my-service
|
||||||
|
version-tag: 1.2.3
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Pushes three tags per build: full (`1.2.3`), minor (`1.2`), and major (`1`).
|
||||||
|
- Consumers using the major or minor tag always get the latest patch release.
|
||||||
46
docker-build-push/action.yml
Normal file
46
docker-build-push/action.yml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
name: docker-build-push
|
||||||
|
description: Build Docker image and push to JFrog with semver tags (major, minor, patch)
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
image-name:
|
||||||
|
description: 'Image name (e.g., default-docker/db-devcontainer-base)'
|
||||||
|
required: true
|
||||||
|
registry:
|
||||||
|
description: 'Docker registry'
|
||||||
|
required: false
|
||||||
|
default: 'schmalz.jfrog.io'
|
||||||
|
dockerfile:
|
||||||
|
description: 'Path to Dockerfile'
|
||||||
|
required: false
|
||||||
|
default: 'Dockerfile'
|
||||||
|
context:
|
||||||
|
description: 'Docker build context'
|
||||||
|
required: false
|
||||||
|
default: '.'
|
||||||
|
version-tag:
|
||||||
|
description: 'Full semver tag (e.g., 1.2.3)'
|
||||||
|
required: true
|
||||||
|
jfrog-token:
|
||||||
|
description: 'JFrog registry password/token'
|
||||||
|
required: true
|
||||||
|
jfrog-user:
|
||||||
|
description: 'JFrog registry user'
|
||||||
|
required: false
|
||||||
|
default: 'jfrog-cicd-user'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Build and push Docker image
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "${{ inputs.jfrog-token }}" | docker login "${{ inputs.registry }}" -u "${{ inputs.jfrog-user }}" --password-stdin
|
||||||
|
|
||||||
|
FULL="${{ inputs.registry }}/${{ inputs.image-name }}:${{ inputs.version-tag }}"
|
||||||
|
MINOR="${{ inputs.registry }}/${{ inputs.image-name }}:$(echo "${{ inputs.version-tag }}" | cut -d. -f1,2)"
|
||||||
|
MAJOR="${{ inputs.registry }}/${{ inputs.image-name }}:$(echo "${{ inputs.version-tag }}" | cut -d. -f1)"
|
||||||
|
|
||||||
|
docker build -f "${{ inputs.dockerfile }}" -t "$FULL" -t "$MINOR" -t "$MAJOR" "${{ inputs.context }}"
|
||||||
|
docker push "$FULL"
|
||||||
|
docker push "$MINOR"
|
||||||
|
docker push "$MAJOR"
|
||||||
33
helm-deploy/README.md
Normal file
33
helm-deploy/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# helm-deploy
|
||||||
|
|
||||||
|
Deploy a service to Kubernetes via Helm over SSH.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `service-name` | Yes | | Helm release name |
|
||||||
|
| `helm-host` | Yes | | SSH target (e.g., `dsp1-stage.schmalzgroup.net`) |
|
||||||
|
| `image-tag` | Yes | | Docker image tag to deploy |
|
||||||
|
| `ssh-key` | Yes | | Private SSH key content |
|
||||||
|
| `overrides-file` | No | `kubernetes/overrides-su.yaml` | Local path to Helm values override file |
|
||||||
|
| `namespace` | No | `dsp` | Kubernetes namespace |
|
||||||
|
| `helm-repo` | No | `nexus-helm-repository` | Helm chart repository name |
|
||||||
|
| `helm-chart` | No | `DSP-Blueprint` | Chart name in the repo |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/helm-deploy@v1
|
||||||
|
with:
|
||||||
|
service-name: my-service
|
||||||
|
helm-host: dsp1-stage.schmalzgroup.net
|
||||||
|
image-tag: ${{ github.sha }}
|
||||||
|
ssh-key: ${{ secrets.SSH_KEY }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The override file is `scp`-ed to the remote host, then `helm upgrade --install` is run via SSH.
|
||||||
|
- Uses `--atomic` flag, so a failed deploy is automatically rolled back.
|
||||||
|
- `StrictHostKeyChecking` is disabled.
|
||||||
55
helm-deploy/action.yml
Normal file
55
helm-deploy/action.yml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
name: helm-deploy
|
||||||
|
description: Deploy a service to Kubernetes via Helm over SSH
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
service-name:
|
||||||
|
description: Helm release name
|
||||||
|
required: true
|
||||||
|
helm-host:
|
||||||
|
description: SSH target (e.g., dsp1-stage.schmalzgroup.net)
|
||||||
|
required: true
|
||||||
|
overrides-file:
|
||||||
|
description: Local path to Helm values override file
|
||||||
|
required: false
|
||||||
|
default: kubernetes/overrides-su.yaml
|
||||||
|
image-tag:
|
||||||
|
description: Docker image tag to deploy
|
||||||
|
required: true
|
||||||
|
ssh-key:
|
||||||
|
description: Private SSH key content
|
||||||
|
required: true
|
||||||
|
namespace:
|
||||||
|
description: Kubernetes namespace
|
||||||
|
required: false
|
||||||
|
default: dsp
|
||||||
|
helm-repo:
|
||||||
|
description: Helm chart repository name
|
||||||
|
required: false
|
||||||
|
default: nexus-helm-repository
|
||||||
|
helm-chart:
|
||||||
|
description: Chart name in the repo
|
||||||
|
required: false
|
||||||
|
default: DSP-Blueprint
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
run: |
|
||||||
|
SSH_KEY_FILE=$(mktemp)
|
||||||
|
trap "rm -f '$SSH_KEY_FILE'" EXIT
|
||||||
|
|
||||||
|
echo "${{ inputs.ssh-key }}" > "$SSH_KEY_FILE"
|
||||||
|
chmod 600 "$SSH_KEY_FILE"
|
||||||
|
|
||||||
|
SSH_OPTS="-i $SSH_KEY_FILE -o StrictHostKeyChecking=no"
|
||||||
|
REMOTE="root@${{ inputs.helm-host }}"
|
||||||
|
SERVICE="${{ inputs.service-name }}"
|
||||||
|
|
||||||
|
scp $SSH_OPTS "${{ inputs.overrides-file }}" "$REMOTE:/tmp/${SERVICE}-overrides.yaml"
|
||||||
|
|
||||||
|
ssh $SSH_OPTS "$REMOTE" \
|
||||||
|
"helm repo update && \
|
||||||
|
helm upgrade --install --create-namespace -n ${{ inputs.namespace }} $SERVICE \
|
||||||
|
${{ inputs.helm-repo }}/${{ inputs.helm-chart }} -f /tmp/${SERVICE}-overrides.yaml \
|
||||||
|
--set image.tag=${{ inputs.image-tag }} --atomic --debug"
|
||||||
32
maven-build/README.md
Normal file
32
maven-build/README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# maven-build
|
||||||
|
|
||||||
|
Run a Maven build -- verify-only for PRs or package+jib push for deploys.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `maven-settings` | Yes | | Secret containing `settings.xml` content |
|
||||||
|
| `java-version` | No | `8` | Java version |
|
||||||
|
| `maven-image` | No | `maven:3.8.5-openjdk-8` | Maven Docker image |
|
||||||
|
| `phase` | No | `verify` | Build phase: `verify` or `package-and-push` |
|
||||||
|
| `verify-goals` | No | `spotless:check checkstyle:check compile` | Goals for the verify phase |
|
||||||
|
| `maven-profile` | No | `test` | Maven profile used during `package-and-push` |
|
||||||
|
| `service-dir` | No | `.` | Working directory containing the POM |
|
||||||
|
| `skip-tests` | No | `false` | Pass `-DskipTests` |
|
||||||
|
| `image-tag` | No | `${{ github.sha }}-${{ github.run_id }}` | Docker image tag for jib |
|
||||||
|
| `extra-args` | No | | Additional Maven CLI arguments |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/maven-build@v1
|
||||||
|
with:
|
||||||
|
maven-settings: ${{ secrets.MAVEN_SETTINGS }}
|
||||||
|
phase: package-and-push
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Maven repository cache is restored/saved automatically using `pom.xml` hash.
|
||||||
|
- The `settings.xml` is written to a temp file and cleaned up after the build.
|
||||||
74
maven-build/action.yml
Normal file
74
maven-build/action.yml
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
name: maven-build
|
||||||
|
description: Run Maven build — verify-only (PRs) or package+jib push (deploy)
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
phase:
|
||||||
|
required: false
|
||||||
|
default: 'verify'
|
||||||
|
verify-goals:
|
||||||
|
required: false
|
||||||
|
default: 'spotless:check checkstyle:check compile'
|
||||||
|
maven-profile:
|
||||||
|
required: false
|
||||||
|
default: 'test'
|
||||||
|
service-dir:
|
||||||
|
required: false
|
||||||
|
default: '.'
|
||||||
|
skip-tests:
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
image-tag:
|
||||||
|
required: false
|
||||||
|
default: '${{ github.sha }}-${{ github.run_id }}'
|
||||||
|
maven-settings:
|
||||||
|
required: true
|
||||||
|
description: Secret containing settings.xml content
|
||||||
|
extra-args:
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Restore Maven cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
|
||||||
|
- name: Maven build
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ inputs.service-dir }}
|
||||||
|
run: |
|
||||||
|
SETTINGS_FILE=$(mktemp)
|
||||||
|
echo "${{ inputs.maven-settings }}" > "$SETTINGS_FILE"
|
||||||
|
trap 'rm -f "$SETTINGS_FILE"' EXIT
|
||||||
|
|
||||||
|
SKIP_TESTS_FLAG=""
|
||||||
|
if [ "${{ inputs.skip-tests }}" = "true" ]; then
|
||||||
|
SKIP_TESTS_FLAG="-DskipTests"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ inputs.phase }}" = "verify" ]; then
|
||||||
|
mvn ${{ inputs.verify-goals }} \
|
||||||
|
-s "$SETTINGS_FILE" \
|
||||||
|
$SKIP_TESTS_FLAG \
|
||||||
|
${{ inputs.extra-args }}
|
||||||
|
elif [ "${{ inputs.phase }}" = "package-and-push" ]; then
|
||||||
|
mvn clean package jib:build \
|
||||||
|
-DsendCredentialsOverHttp=true \
|
||||||
|
-Djib.to.tags=${{ inputs.image-tag }} \
|
||||||
|
-P ${{ inputs.maven-profile }} \
|
||||||
|
-s "$SETTINGS_FILE" \
|
||||||
|
$SKIP_TESTS_FLAG \
|
||||||
|
${{ inputs.extra-args }}
|
||||||
|
else
|
||||||
|
echo "Unknown phase: ${{ inputs.phase }}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Save Maven cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: maven-${{ hashFiles('**/pom.xml') }}
|
||||||
38
playwright-e2e/README.md
Normal file
38
playwright-e2e/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# playwright-e2e
|
||||||
|
|
||||||
|
Run Playwright E2E tests with optional sharding, upload results to S3.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `jfrog-token` | Yes | | JFrog npm auth token |
|
||||||
|
| `s3-reports-bucket` | Yes | | S3 bucket for report upload |
|
||||||
|
| `s3-reports-prefix` | Yes | | S3 path prefix |
|
||||||
|
| `aws-role-arn` | Yes | | IAM role ARN for OIDC authentication |
|
||||||
|
| `working-directory` | No | `e2e` | Directory containing Playwright config |
|
||||||
|
| `playwright-version` | No | `v1.58.2` | Playwright version tag for browser cache key |
|
||||||
|
| `pnpm-version` | No | `10.11` | pnpm version |
|
||||||
|
| `shard-index` | No | `1` | Current shard (1-based) |
|
||||||
|
| `shard-total` | No | `1` | Total shards. 1 = no sharding |
|
||||||
|
| `aws-profile` | No | `stage` | AWS CLI profile name |
|
||||||
|
| `extra-deps` | No | `""` | Space-separated apt packages to install |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/playwright-e2e@v1
|
||||||
|
with:
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
s3-reports-bucket: my-reports-bucket
|
||||||
|
s3-reports-prefix: pr-${{ github.event.number }}
|
||||||
|
aws-role-arn: ${{ secrets.AWS_ROLE_ARN }}
|
||||||
|
shard-index: "1"
|
||||||
|
shard-total: "4"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Playwright browsers are cached between runs using the `playwright-version` input as cache key.
|
||||||
|
- Reports are uploaded to `s3://<bucket>/<prefix>/shard-<index>/`.
|
||||||
|
- Uses JFrog Artifactory as the npm registry for dependency installation.
|
||||||
109
playwright-e2e/action.yml
Normal file
109
playwright-e2e/action.yml
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
name: playwright-e2e
|
||||||
|
description: Run Playwright E2E tests with optional sharding, upload results to S3.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
working-directory:
|
||||||
|
description: Directory containing Playwright config
|
||||||
|
required: false
|
||||||
|
default: e2e
|
||||||
|
playwright-version:
|
||||||
|
description: Playwright version tag (browser cache key only — actual version comes from pnpm-lock.yaml)
|
||||||
|
required: false
|
||||||
|
default: v1.58.2
|
||||||
|
jfrog-token:
|
||||||
|
description: JFrog npm auth token
|
||||||
|
required: true
|
||||||
|
pnpm-version:
|
||||||
|
description: pnpm version
|
||||||
|
required: false
|
||||||
|
default: "10.11"
|
||||||
|
node-version:
|
||||||
|
description: Node.js version
|
||||||
|
required: false
|
||||||
|
default: "22"
|
||||||
|
shard-index:
|
||||||
|
description: Current shard (1-based)
|
||||||
|
required: false
|
||||||
|
default: "1"
|
||||||
|
shard-total:
|
||||||
|
description: Total shards. 1 = no sharding.
|
||||||
|
required: false
|
||||||
|
default: "1"
|
||||||
|
s3-reports-bucket:
|
||||||
|
description: S3 bucket for report upload
|
||||||
|
required: true
|
||||||
|
s3-reports-prefix:
|
||||||
|
description: S3 path prefix
|
||||||
|
required: true
|
||||||
|
aws-role-arn:
|
||||||
|
description: IAM role ARN for OIDC authentication
|
||||||
|
required: true
|
||||||
|
aws-profile:
|
||||||
|
description: AWS CLI profile name
|
||||||
|
required: false
|
||||||
|
default: stage
|
||||||
|
extra-deps:
|
||||||
|
description: Space-separated apt packages to install
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Configure AWS
|
||||||
|
uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
with:
|
||||||
|
role-arn: ${{ inputs.aws-role-arn }}
|
||||||
|
aws-profile: ${{ inputs.aws-profile }}
|
||||||
|
|
||||||
|
- name: Restore Playwright browser cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/ms-playwright
|
||||||
|
key: playwright-${{ inputs.playwright-version }}
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: https://code.forgejo.org/actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ inputs.node-version }}
|
||||||
|
|
||||||
|
- name: Run Playwright tests and upload results
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
EXTRA_DEPS: ${{ inputs.extra-deps }}
|
||||||
|
PNPM_VERSION: ${{ inputs.pnpm-version }}
|
||||||
|
JFROG_TOKEN: ${{ inputs.jfrog-token }}
|
||||||
|
WORKING_DIR: ${{ inputs.working-directory }}
|
||||||
|
SHARD_INDEX: ${{ inputs.shard-index }}
|
||||||
|
SHARD_TOTAL: ${{ inputs.shard-total }}
|
||||||
|
BUCKET: ${{ inputs.s3-reports-bucket }}
|
||||||
|
PREFIX: ${{ inputs.s3-reports-prefix }}
|
||||||
|
AWS_PROFILE: ${{ inputs.aws-profile }}
|
||||||
|
run: |
|
||||||
|
if [ -n "${EXTRA_DEPS}" ]; then
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y ${EXTRA_DEPS}
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm i -g "pnpm@${PNPM_VERSION}"
|
||||||
|
pnpm set registry https://schmalz.jfrog.io/artifactory/api/npm/default-npm/
|
||||||
|
pnpm set "//schmalz.jfrog.io/artifactory/api/npm/default-npm/:_authToken=${JFROG_TOKEN}"
|
||||||
|
|
||||||
|
pnpm --prefix="${WORKING_DIR}" install --frozen-lockfile
|
||||||
|
pnpm --prefix="${WORKING_DIR}" exec playwright install --with-deps
|
||||||
|
|
||||||
|
SHARD_ARG=""
|
||||||
|
if [ "${SHARD_TOTAL}" != "1" ]; then
|
||||||
|
SHARD_ARG="--shard=${SHARD_INDEX}/${SHARD_TOTAL}"
|
||||||
|
fi
|
||||||
|
pnpm --prefix="${WORKING_DIR}" exec playwright test ${SHARD_ARG}
|
||||||
|
|
||||||
|
aws s3 sync "${WORKING_DIR}/playwright-report/" \
|
||||||
|
"s3://${BUCKET}/${PREFIX}/shard-${SHARD_INDEX}/" \
|
||||||
|
--profile "${AWS_PROFILE}"
|
||||||
|
|
||||||
|
- name: Save Playwright browser cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/ms-playwright
|
||||||
|
key: playwright-${{ inputs.playwright-version }}
|
||||||
28
pnpm-build/README.md
Normal file
28
pnpm-build/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# pnpm-build
|
||||||
|
|
||||||
|
Set up pnpm, authenticate with JFrog npm registry, install dependencies, and run build scripts.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `jfrog-token` | Yes | | JFrog npm auth token |
|
||||||
|
| `working-directory` | No | `.` | Directory containing `package.json` |
|
||||||
|
| `pnpm-version` | No | `10.14` | pnpm version |
|
||||||
|
| `node-version` | No | `22` | Node.js version |
|
||||||
|
| `run-scripts` | No | `ci,typecheck,build` | Comma-separated list of `pnpm run` scripts |
|
||||||
|
| `frozen-lockfile` | No | `true` | Pass `--frozen-lockfile` to `pnpm install` |
|
||||||
|
| `check-dedupe` | No | `true` | Run `pnpm dedupe --check` |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/pnpm-build@v1
|
||||||
|
with:
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- pnpm store cache is restored/saved automatically using `pnpm-lock.yaml` hash.
|
||||||
|
- Registry is set to `schmalz.jfrog.io`.
|
||||||
80
pnpm-build/action.yml
Normal file
80
pnpm-build/action.yml
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
name: pnpm-build
|
||||||
|
description: Set up pnpm, authenticate JFrog npm registry, install deps, run scripts.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
working-directory:
|
||||||
|
description: Directory containing package.json
|
||||||
|
required: false
|
||||||
|
default: "."
|
||||||
|
pnpm-version:
|
||||||
|
description: pnpm version
|
||||||
|
required: false
|
||||||
|
default: "10.14"
|
||||||
|
jfrog-token:
|
||||||
|
description: JFrog npm auth token
|
||||||
|
required: true
|
||||||
|
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"
|
||||||
|
node-version:
|
||||||
|
description: Node.js version
|
||||||
|
required: false
|
||||||
|
default: "22"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Restore pnpm cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ~/.local/share/pnpm/store/v3
|
||||||
|
key: pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: https://code.forgejo.org/actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ inputs.node-version }}
|
||||||
|
|
||||||
|
- name: Setup and build
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PNPM_VERSION: ${{ inputs.pnpm-version }}
|
||||||
|
JFROG_TOKEN: ${{ inputs.jfrog-token }}
|
||||||
|
WORKING_DIR: ${{ inputs.working-directory }}
|
||||||
|
RUN_SCRIPTS: ${{ inputs.run-scripts }}
|
||||||
|
FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }}
|
||||||
|
CHECK_DEDUPE: ${{ inputs.check-dedupe }}
|
||||||
|
run: |
|
||||||
|
npm i -g "pnpm@${PNPM_VERSION}"
|
||||||
|
pnpm set registry https://schmalz.jfrog.io/artifactory/api/npm/default-npm/
|
||||||
|
pnpm set "//schmalz.jfrog.io/artifactory/api/npm/default-npm/:_authToken=${JFROG_TOKEN}"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- name: Save pnpm cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ~/.local/share/pnpm/store/v3
|
||||||
|
key: pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
26
publish-npm-package/README.md
Normal file
26
publish-npm-package/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# publish-npm-package
|
||||||
|
|
||||||
|
Build and publish npm package to JFrog Artifactory.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `jfrog-token` | Yes | | JFrog npm auth token |
|
||||||
|
| `working-directory` | No | `.` | Directory containing package.json |
|
||||||
|
| `pnpm-version` | No | `10.14` | pnpm version |
|
||||||
|
| `registry-url` | No | `https://schmalz.jfrog.io/artifactory/api/npm/default-npm/` | JFrog npm registry URL |
|
||||||
|
| `build-script` | No | `build` | pnpm build script name |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/publish-npm-package@v1
|
||||||
|
with:
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Runs `pnpm install --frozen-lockfile`, then the build script, then `pnpm publish --no-git-checks`.
|
||||||
|
- The version published is whatever is in `package.json` -- bump it before calling this action.
|
||||||
51
publish-npm-package/action.yml
Normal file
51
publish-npm-package/action.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
name: publish-npm-package
|
||||||
|
description: Build and publish npm package to JFrog Artifactory.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
working-directory:
|
||||||
|
description: Directory containing package.json
|
||||||
|
required: false
|
||||||
|
default: "."
|
||||||
|
pnpm-version:
|
||||||
|
description: pnpm version
|
||||||
|
required: false
|
||||||
|
default: "10.14"
|
||||||
|
node-version:
|
||||||
|
description: Node.js version
|
||||||
|
required: false
|
||||||
|
default: "22"
|
||||||
|
jfrog-token:
|
||||||
|
description: JFrog npm auth token
|
||||||
|
required: true
|
||||||
|
registry-url:
|
||||||
|
description: JFrog npm registry URL
|
||||||
|
required: false
|
||||||
|
default: "https://schmalz.jfrog.io/artifactory/api/npm/default-npm/"
|
||||||
|
build-script:
|
||||||
|
description: pnpm build script name
|
||||||
|
required: false
|
||||||
|
default: "build"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: https://code.forgejo.org/actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ inputs.node-version }}
|
||||||
|
|
||||||
|
- name: Build and publish
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PNPM_VERSION: ${{ inputs.pnpm-version }}
|
||||||
|
JFROG_TOKEN: ${{ inputs.jfrog-token }}
|
||||||
|
REGISTRY_URL: ${{ inputs.registry-url }}
|
||||||
|
WORKING_DIR: ${{ inputs.working-directory }}
|
||||||
|
BUILD_SCRIPT: ${{ inputs.build-script }}
|
||||||
|
run: |
|
||||||
|
npm i -g "pnpm@${PNPM_VERSION}"
|
||||||
|
pnpm set registry "${REGISTRY_URL}"
|
||||||
|
pnpm set "//schmalz.jfrog.io/artifactory/api/npm/default-npm/:_authToken=${JFROG_TOKEN}"
|
||||||
|
pnpm --prefix="${WORKING_DIR}" install --frozen-lockfile
|
||||||
|
pnpm --prefix="${WORKING_DIR}" run "${BUILD_SCRIPT}"
|
||||||
|
pnpm --prefix="${WORKING_DIR}" publish --no-git-checks
|
||||||
26
publish-rust-crate/README.md
Normal file
26
publish-rust-crate/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# publish-rust-crate
|
||||||
|
|
||||||
|
Build, test, and publish Rust crate to JFrog Cargo registry.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `jfrog-token` | Yes | | JFrog access token for Cargo registry auth |
|
||||||
|
| `working-directory` | No | `.` | Directory containing Cargo.toml |
|
||||||
|
| `rust-version` | No | `1.87` | Rust toolchain version |
|
||||||
|
| `cargo-registry-name` | No | `jfrog` | Cargo registry name |
|
||||||
|
| `run-tests` | No | `true` | Run `cargo test` before publishing |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/publish-rust-crate@v1
|
||||||
|
with:
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The registry token is set via `CARGO_REGISTRIES_<NAME>_TOKEN` environment variable (registry name is uppercased).
|
||||||
|
- The crate version published is whatever is in `Cargo.toml` -- bump it before calling this action.
|
||||||
42
publish-rust-crate/action.yml
Normal file
42
publish-rust-crate/action.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: publish-rust-crate
|
||||||
|
description: Build, test, and publish Rust crate to JFrog Cargo registry.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
working-directory:
|
||||||
|
description: Directory containing Cargo.toml
|
||||||
|
required: false
|
||||||
|
default: "."
|
||||||
|
rust-version:
|
||||||
|
description: Rust toolchain version
|
||||||
|
required: false
|
||||||
|
default: "1.87"
|
||||||
|
cargo-registry-name:
|
||||||
|
description: Cargo registry name
|
||||||
|
required: false
|
||||||
|
default: "jfrog"
|
||||||
|
jfrog-token:
|
||||||
|
description: JFrog access token for Cargo registry auth
|
||||||
|
required: true
|
||||||
|
run-tests:
|
||||||
|
description: Run cargo test before publishing
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Test and publish
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
RUST_VERSION: ${{ inputs.rust-version }}
|
||||||
|
REGISTRY_NAME: ${{ inputs.cargo-registry-name }}
|
||||||
|
JFROG_TOKEN: ${{ inputs.jfrog-token }}
|
||||||
|
WORKING_DIR: ${{ inputs.working-directory }}
|
||||||
|
RUN_TESTS: ${{ inputs.run-tests }}
|
||||||
|
run: |
|
||||||
|
rustup default "${RUST_VERSION}"
|
||||||
|
UPPER_NAME="$(echo "${REGISTRY_NAME}" | tr '[:lower:]' '[:upper:]')"
|
||||||
|
export "CARGO_REGISTRIES_${UPPER_NAME}_TOKEN=Bearer ${JFROG_TOKEN}"
|
||||||
|
cd "${WORKING_DIR}"
|
||||||
|
if [ "${RUN_TESTS}" = "true" ]; then cargo test; fi
|
||||||
|
cargo publish --registry "${REGISTRY_NAME}"
|
||||||
42
rust-build/README.md
Normal file
42
rust-build/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# rust-build
|
||||||
|
|
||||||
|
Run Rust CI -- fmt, clippy, tests, optional cross-compilation.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `toolchain` | No | `stable` | Rust toolchain |
|
||||||
|
| `features` | No | `""` | Cargo features to enable |
|
||||||
|
| `cross-target` | No | `""` | Cross-compilation target (installs `cross` when set) |
|
||||||
|
| `run-fmt` | No | `true` | Run `cargo fmt --check` |
|
||||||
|
| `run-clippy` | No | `true` | Run clippy with `-D warnings` |
|
||||||
|
| `run-tests` | No | `true` | Run `cargo test` |
|
||||||
|
| `run-deny` | No | `false` | Run `cargo deny check` |
|
||||||
|
| `run-semver-checks` | No | `false` | Run `cargo semver-checks` |
|
||||||
|
| `sccache-enabled` | No | `true` | Enable sccache (requires `sccache_ci_setup.sh` in working directory) |
|
||||||
|
| `working-directory` | No | `.` | Working directory |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/rust-build@v1
|
||||||
|
with:
|
||||||
|
features: serde,tokio
|
||||||
|
run-deny: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cross-compilation
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/rust-build@v1
|
||||||
|
with:
|
||||||
|
cross-target: aarch64-unknown-linux-gnu
|
||||||
|
run-fmt: "false"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Cargo registry and `target/` directory are cached based on `Cargo.lock` hash.
|
||||||
|
- When `cross-target` is set, `cross` is installed and used instead of `cargo` for clippy, test, and build steps. `cargo fmt` always uses `cargo` directly.
|
||||||
|
- sccache integration requires a `sccache_ci_setup.sh` script in the working directory.
|
||||||
101
rust-build/action.yml
Normal file
101
rust-build/action.yml
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
name: rust-build
|
||||||
|
description: Run Rust CI — fmt, clippy, tests, optional cross-compilation.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
toolchain:
|
||||||
|
required: false
|
||||||
|
default: "stable"
|
||||||
|
features:
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
cross-target:
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
run-fmt:
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
run-clippy:
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
run-tests:
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
run-deny:
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
run-semver-checks:
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
sccache-enabled:
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
working-directory:
|
||||||
|
required: false
|
||||||
|
default: "."
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Restore cargo cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
target/
|
||||||
|
key: cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Rust CI
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ inputs.working-directory }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
rustup default ${{ inputs.toolchain }}
|
||||||
|
|
||||||
|
if [[ "${{ inputs.sccache-enabled }}" == "true" && -f sccache_ci_setup.sh ]]; then
|
||||||
|
source sccache_ci_setup.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
CMD=cargo
|
||||||
|
if [[ -n "${{ inputs.cross-target }}" ]]; then
|
||||||
|
cargo install cross
|
||||||
|
CMD=cross
|
||||||
|
fi
|
||||||
|
|
||||||
|
FEATURES_ARG=""
|
||||||
|
if [[ -n "${{ inputs.features }}" ]]; then
|
||||||
|
FEATURES_ARG="--features ${{ inputs.features }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
TARGET_ARG=""
|
||||||
|
if [[ -n "${{ inputs.cross-target }}" ]]; then
|
||||||
|
TARGET_ARG="--target ${{ inputs.cross-target }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ inputs.run-fmt }}" == "true" ]]; then
|
||||||
|
cargo fmt --check
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ inputs.run-clippy }}" == "true" ]]; then
|
||||||
|
$CMD clippy $FEATURES_ARG $TARGET_ARG -- -D warnings
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ inputs.run-tests }}" == "true" ]]; then
|
||||||
|
$CMD test $FEATURES_ARG $TARGET_ARG
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ inputs.run-deny }}" == "true" ]]; then
|
||||||
|
cargo deny check
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ inputs.run-semver-checks }}" == "true" ]]; then
|
||||||
|
cargo semver-checks
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Save cargo cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
target/
|
||||||
|
key: cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
23
secrets-inject/README.md
Normal file
23
secrets-inject/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# secrets-inject
|
||||||
|
|
||||||
|
Append secret properties content to a Java `.properties` file.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `secret-content` | Yes | | Properties content to append |
|
||||||
|
| `target-file` | Yes | | Path to the `.properties` file |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/secrets-inject@v1
|
||||||
|
with:
|
||||||
|
secret-content: ${{ secrets.APP_SECRETS }}
|
||||||
|
target-file: src/main/resources/application.properties
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- A blank line is inserted before the appended content to avoid merging with the last existing line.
|
||||||
18
secrets-inject/action.yml
Normal file
18
secrets-inject/action.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
name: secrets-inject
|
||||||
|
description: Append a secrets file to a Java .properties file
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
secret-content:
|
||||||
|
description: Secret value (properties content to append)
|
||||||
|
required: true
|
||||||
|
target-file:
|
||||||
|
description: Path to the .properties file
|
||||||
|
required: true
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
echo "" >> ${{ inputs.target-file }}
|
||||||
|
echo "${{ inputs.secret-content }}" >> ${{ inputs.target-file }}
|
||||||
|
shell: bash
|
||||||
38
terraform-apply/README.md
Normal file
38
terraform-apply/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# terraform-apply
|
||||||
|
|
||||||
|
Full Terraform init, workspace select, plan/apply, and output capture.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `var-file` | Yes | | Path to `.tfvars` file |
|
||||||
|
| `workspace` | Yes | | Terraform workspace (`stage` or `prod`) |
|
||||||
|
| `aws-role-arn` | Yes | | IAM role ARN for OIDC authentication |
|
||||||
|
| `jfrog-token` | Yes | | JFrog access token (sets `TF_TOKEN_schmalz_jfrog_io`) |
|
||||||
|
| `terraform-dir` | No | `terraform` | Directory containing Terraform configuration |
|
||||||
|
| `terraform-version` | No | `1.11` | Terraform version to install |
|
||||||
|
| `aws-profile` | No | `default` | AWS CLI profile name |
|
||||||
|
| `output-names` | No | `""` | Comma-separated Terraform output names to capture as raw values |
|
||||||
|
| `output-json-names` | No | `""` | Comma-separated output names to capture as JSON |
|
||||||
|
| `plan-only` | No | `false` | Run `plan -out` instead of `apply` |
|
||||||
|
| `plan-file` | No | `""` | Pre-existing plan file to apply |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/terraform-apply@v1
|
||||||
|
with:
|
||||||
|
var-file: envs/stage.tfvars
|
||||||
|
workspace: stage
|
||||||
|
aws-role-arn: arn:aws:iam::123456789012:role/my-role
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
output-names: api_url,db_host
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Requires `enable-openid-connect: true` on the job.
|
||||||
|
- Captured outputs are written to `$FORGEJO_OUTPUT` and to files under `<terraform-dir>/.outputs/`.
|
||||||
|
- Provider cache is restored/saved automatically.
|
||||||
|
- Use `plan-only: true` for a plan-then-apply workflow across jobs.
|
||||||
118
terraform-apply/action.yml
Normal file
118
terraform-apply/action.yml
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
name: terraform-apply
|
||||||
|
description: Full Terraform init + workspace + apply + output capture
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
terraform-dir:
|
||||||
|
description: Directory containing Terraform configuration
|
||||||
|
required: false
|
||||||
|
default: terraform
|
||||||
|
terraform-version:
|
||||||
|
description: Terraform version to install
|
||||||
|
required: false
|
||||||
|
default: "1.11"
|
||||||
|
var-file:
|
||||||
|
description: Path to .tfvars file
|
||||||
|
required: true
|
||||||
|
workspace:
|
||||||
|
description: Terraform workspace (stage or prod)
|
||||||
|
required: true
|
||||||
|
aws-role-arn:
|
||||||
|
description: IAM role ARN for OIDC authentication
|
||||||
|
required: true
|
||||||
|
aws-profile:
|
||||||
|
description: AWS profile name
|
||||||
|
required: false
|
||||||
|
default: default
|
||||||
|
jfrog-token:
|
||||||
|
description: JFrog access token (sets TF_TOKEN_schmalz_jfrog_io)
|
||||||
|
required: true
|
||||||
|
output-names:
|
||||||
|
description: Comma-separated list of terraform output names to capture as raw
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
plan-only:
|
||||||
|
description: Run plan -out instead of apply
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
plan-file:
|
||||||
|
description: Pre-existing plan file to apply
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
output-json-names:
|
||||||
|
description: Comma-separated output names to capture as JSON
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Configure AWS credentials
|
||||||
|
uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
with:
|
||||||
|
role-arn: ${{ inputs.aws-role-arn }}
|
||||||
|
aws-profile: ${{ inputs.aws-profile }}
|
||||||
|
|
||||||
|
- name: Restore Terraform provider cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.terraform-dir }}/.terraform/providers
|
||||||
|
key: terraform-providers-${{ hashFiles(format('{0}/.terraform.lock.hcl', inputs.terraform-dir)) }}
|
||||||
|
|
||||||
|
- name: Install Terraform
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
TF_VERSION="${{ inputs.terraform-version }}"
|
||||||
|
curl -fsSL "https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip" -o /tmp/terraform.zip
|
||||||
|
sudo unzip -o /tmp/terraform.zip -d /usr/local/bin
|
||||||
|
rm /tmp/terraform.zip
|
||||||
|
terraform version
|
||||||
|
|
||||||
|
- name: Terraform init, plan/apply, and capture outputs
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TF_TOKEN_schmalz_jfrog_io: ${{ inputs.jfrog-token }}
|
||||||
|
TF_WORKSPACE: ${{ inputs.workspace }}
|
||||||
|
AWS_PROFILE: ${{ inputs.aws-profile }}
|
||||||
|
TF_DIR: ${{ inputs.terraform-dir }}
|
||||||
|
VAR_FILE: ${{ inputs.var-file }}
|
||||||
|
PLAN_ONLY: ${{ inputs.plan-only }}
|
||||||
|
PLAN_FILE: ${{ inputs.plan-file }}
|
||||||
|
OUTPUT_NAMES: ${{ inputs.output-names }}
|
||||||
|
OUTPUT_JSON_NAMES: ${{ inputs.output-json-names }}
|
||||||
|
run: |
|
||||||
|
terraform -chdir="$TF_DIR" init -no-color
|
||||||
|
|
||||||
|
if [ "$PLAN_ONLY" = "true" ]; then
|
||||||
|
terraform -chdir="$TF_DIR" plan -var-file="$VAR_FILE" -out=terraform.plan -no-color
|
||||||
|
elif [ -n "$PLAN_FILE" ]; then
|
||||||
|
terraform -chdir="$TF_DIR" apply -auto-approve -no-color "$PLAN_FILE"
|
||||||
|
else
|
||||||
|
terraform -chdir="$TF_DIR" apply -var-file="$VAR_FILE" -auto-approve -no-color
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$TF_DIR/.outputs"
|
||||||
|
|
||||||
|
if [ -n "$OUTPUT_NAMES" ]; then
|
||||||
|
IFS=',' read -ra NAMES <<< "$OUTPUT_NAMES"
|
||||||
|
for name in "${NAMES[@]}"; do
|
||||||
|
name=$(echo "$name" | xargs)
|
||||||
|
val=$(terraform -chdir="$TF_DIR" output --raw "$name")
|
||||||
|
echo "$val" > "$TF_DIR/.outputs/$name"
|
||||||
|
echo "$name=$val" >> "$FORGEJO_OUTPUT"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$OUTPUT_JSON_NAMES" ]; then
|
||||||
|
IFS=',' read -ra JNAMES <<< "$OUTPUT_JSON_NAMES"
|
||||||
|
for name in "${JNAMES[@]}"; do
|
||||||
|
name=$(echo "$name" | xargs)
|
||||||
|
terraform -chdir="$TF_DIR" output --json "$name" > "$TF_DIR/.outputs/$name.json"
|
||||||
|
echo "$name=$(terraform -chdir="$TF_DIR" output --json "$name")" >> "$FORGEJO_OUTPUT"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Save Terraform provider cache
|
||||||
|
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.terraform-dir }}/.terraform/providers
|
||||||
|
key: terraform-providers-${{ hashFiles(format('{0}/.terraform.lock.hcl', inputs.terraform-dir)) }}
|
||||||
29
terraform-module-publish/README.md
Normal file
29
terraform-module-publish/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# terraform-module-publish
|
||||||
|
|
||||||
|
Zip a Terraform module and publish to JFrog Artifactory.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `module-dir` | Yes | | Path to the module directory to zip |
|
||||||
|
| `module-name` | Yes | | Module name (used in zip filename and upload path) |
|
||||||
|
| `version` | Yes | | Version string (from git tag) |
|
||||||
|
| `jfrog-token` | Yes | | JFrog access token |
|
||||||
|
| `jfrog-url` | No | `https://schmalz.jfrog.io/artifactory/terraform/schmalz` | JFrog Artifactory base URL |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/terraform-module-publish@v1
|
||||||
|
with:
|
||||||
|
module-dir: modules/vpc
|
||||||
|
module-name: vpc
|
||||||
|
version: ${{ github.ref_name }}
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The module directory contents are zipped and uploaded to `<jfrog-url>/<module-name>/<version>.zip`.
|
||||||
|
- Uses `curl` with bearer token authentication.
|
||||||
32
terraform-module-publish/action.yml
Normal file
32
terraform-module-publish/action.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: terraform-module-publish
|
||||||
|
description: Zip a Terraform module and publish to JFrog Artifactory
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
module-dir:
|
||||||
|
description: Path to the module directory to zip
|
||||||
|
required: true
|
||||||
|
module-name:
|
||||||
|
description: Module name (used in zip filename and upload path)
|
||||||
|
required: true
|
||||||
|
version:
|
||||||
|
description: Version string (from git tag)
|
||||||
|
required: true
|
||||||
|
jfrog-url:
|
||||||
|
description: JFrog Artifactory base URL
|
||||||
|
required: false
|
||||||
|
default: https://schmalz.jfrog.io/artifactory/terraform/schmalz
|
||||||
|
jfrog-token:
|
||||||
|
description: JFrog access token
|
||||||
|
required: true
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Zip and publish module
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd "${{ inputs.module-dir }}"
|
||||||
|
zip -r "/tmp/${{ inputs.module-name }}-${{ inputs.version }}.zip" .
|
||||||
|
curl -sf -H "Authorization: Bearer ${{ inputs.jfrog-token }}" \
|
||||||
|
-T "/tmp/${{ inputs.module-name }}-${{ inputs.version }}.zip" \
|
||||||
|
"${{ inputs.jfrog-url }}/${{ inputs.module-name }}/${{ inputs.version }}.zip"
|
||||||
28
terraform-validate/README.md
Normal file
28
terraform-validate/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# terraform-validate
|
||||||
|
|
||||||
|
Validate Terraform code without a backend (for PR checks).
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Input | Required | Default | Description |
|
||||||
|
|-------|----------|---------|-------------|
|
||||||
|
| `jfrog-token` | Yes | | Sets `TF_TOKEN_schmalz_jfrog_io` for provider auth |
|
||||||
|
| `terraform-dir` | No | `terraform` | Directory containing `.tf` files |
|
||||||
|
| `terraform-version` | No | `1.11` | Terraform version to use |
|
||||||
|
| `aws-role-arn` | No | `""` | If provided, runs `aws-configure` before validate |
|
||||||
|
| `aws-profile` | No | `stage` | AWS profile when `aws-role-arn` is given |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/terraform-validate@v1
|
||||||
|
with:
|
||||||
|
jfrog-token: ${{ secrets.JFROG_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Runs `terraform init -backend=false`, `terraform fmt -check -recursive`, and `terraform validate`.
|
||||||
|
- Sets `TF_WORKSPACE=stage` during init.
|
||||||
|
- Provider cache is restored/saved automatically.
|
||||||
|
- Optionally configures AWS credentials if `aws-role-arn` is provided (requires `enable-openid-connect: true`).
|
||||||
59
terraform-validate/action.yml
Normal file
59
terraform-validate/action.yml
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
name: terraform-validate
|
||||||
|
description: Validate Terraform code without backend (for PR checks)
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
terraform-dir:
|
||||||
|
description: Directory containing .tf files
|
||||||
|
required: false
|
||||||
|
default: terraform
|
||||||
|
terraform-version:
|
||||||
|
description: Terraform version to use
|
||||||
|
required: false
|
||||||
|
default: "1.11"
|
||||||
|
jfrog-token:
|
||||||
|
description: Sets TF_TOKEN_schmalz_jfrog_io
|
||||||
|
required: true
|
||||||
|
aws-role-arn:
|
||||||
|
description: If provided, runs aws-configure before validate
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
aws-profile:
|
||||||
|
description: Profile if aws-role-arn given
|
||||||
|
required: false
|
||||||
|
default: stage
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- uses: schmalz/shared-actions/.github/actions/aws-configure@v1
|
||||||
|
if: ${{ inputs.aws-role-arn != '' }}
|
||||||
|
with:
|
||||||
|
role-arn: ${{ inputs.aws-role-arn }}
|
||||||
|
aws-profile: ${{ inputs.aws-profile }}
|
||||||
|
|
||||||
|
- uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }}
|
||||||
|
path: ${{ inputs.terraform-dir }}/.terraform/providers
|
||||||
|
|
||||||
|
- name: Install Terraform
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
TF_VERSION="${{ inputs.terraform-version }}"
|
||||||
|
curl -fsSL "https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip" -o /tmp/terraform.zip
|
||||||
|
sudo unzip -o /tmp/terraform.zip -d /usr/local/bin
|
||||||
|
rm /tmp/terraform.zip
|
||||||
|
terraform version
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
export TF_TOKEN_schmalz_jfrog_io="${INPUT_JFROG_TOKEN}"
|
||||||
|
export TF_WORKSPACE=stage
|
||||||
|
terraform -chdir="${INPUT_TERRAFORM_DIR}" init -backend=false -no-color
|
||||||
|
terraform -chdir="${INPUT_TERRAFORM_DIR}" fmt -check -recursive
|
||||||
|
terraform -chdir="${INPUT_TERRAFORM_DIR}" validate
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }}
|
||||||
|
path: ${{ inputs.terraform-dir }}/.terraform/providers
|
||||||
Loading…
Add table
Add a link
Reference in a new issue