Compare commits

..

1 commit

Author SHA1 Message Date
b0f915be4c feat: add release note and webhook actions
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 46s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m3s
2026-06-09 07:08:14 +02:00
25 changed files with 218 additions and 1045 deletions

View file

@ -16,25 +16,17 @@ on:
- aikido-full-scan - aikido-full-scan
- aikido-pr-scan - aikido-pr-scan
- aws-configure - aws-configure
- aws-lambda-alias-update
- aws-lambda-wait-for-provisioned-concurrency
- cache - cache
- checkout - checkout
- download-artifact - download-artifact
- esb-deploy
- helm-deploy - helm-deploy
- i18n-sync - i18n-sync
- inject-content - inject-content
- maven-build - maven-build
- playwright-merge
- playwright-run
- pnpm-build - pnpm-build
- publish-npm-package
- publish-rust-crate
- publish-static-contents - publish-static-contents
- rust-build - rust-build
- terraform-apply - terraform-apply
- terraform-plan
- terraform-validate - terraform-validate
- upload-artifact - upload-artifact
- vacuum-lint - vacuum-lint

View file

@ -9,8 +9,6 @@ Shared actions for Forgejo CI/CD pipelines.
| [aikido-full-scan](aikido-full-scan) | Aikido full scan | | [aikido-full-scan](aikido-full-scan) | Aikido full scan |
| [aikido-pr-scan](aikido-pr-scan) | Aikido PR scan | | [aikido-pr-scan](aikido-pr-scan) | Aikido PR scan |
| [aws-configure](aws-configure) | Authenticate with AWS via OIDC | | [aws-configure](aws-configure) | Authenticate with AWS via OIDC |
| [aws-lambda-alias-update](aws-lambda-alias-update) | Update Aliases of Lambda Functions to a new Version |
| [aws-lambda-wait-for-provisioned-concurrency](aws-lambda-wait-for-provisioned-concurrency) | Wait until the Provisioned Concurrency is Ready for Lambda Functions |
| [cache](cache) | Cache files between workflow runs | | [cache](cache) | Cache files between workflow runs |
| [checkout](checkout) | Action for checking out a repository | | [checkout](checkout) | Action for checking out a repository |
| [download-artifact](download-artifact) | Download Forgejo Actions artifacts by name or pattern | | [download-artifact](download-artifact) | Download Forgejo Actions artifacts by name or pattern |
@ -18,15 +16,10 @@ Shared actions for Forgejo CI/CD pipelines.
| [i18n-sync](i18n-sync) | Fetch translations from i18n.schmalz.com and open a pull request | | [i18n-sync](i18n-sync) | Fetch translations from i18n.schmalz.com and open a pull request |
| [inject-content](inject-content) | Inject content into a file by appending or overwriting | | [inject-content](inject-content) | Inject content into a file by appending or overwriting |
| [maven-build](maven-build) | Action for building and validating Maven projects | | [maven-build](maven-build) | Action for building and validating Maven projects |
| [playwright-merge](playwright-merge) | Merge Playwright shard blob reports and publish consolidated reports |
| [playwright-run](playwright-run) | Run Playwright tests for one shard and upload its blob report |
| [pnpm-build](pnpm-build) | Action for building and validating with PNPM | | [pnpm-build](pnpm-build) | Action for building and validating with PNPM |
| [publish-npm-package](publish-npm-package) | Publish a PNPM package to JFrog Artifactory |
| [publish-rust-crate](publish-rust-crate) | Publish a Rust crate to JFrog Artifactory |
| [publish-static-contents](publish-static-contents) | Syncs frontend assets to S3 and invalidates a CloudFront distribution | | [publish-static-contents](publish-static-contents) | Syncs frontend assets to S3 and invalidates a CloudFront distribution |
| [rust-build](rust-build) | Set up Rust toolchain, run checks, and build via the project's build.sh | | [rust-build](rust-build) | Set up Rust toolchain, run checks, and build via the project's build.sh |
| [terraform-apply](terraform-apply) | Apply Terraform configuration files using the official Terraform CLI | | [terraform-apply](terraform-apply) | Apply Terraform configuration files using the official Terraform CLI |
| [terraform-plan](terraform-plan) | Preview Terraform infrastructure changes (create, update, delete, replace) without applying them |
| [terraform-validate](terraform-validate) | Validate Terraform configuration files using the official Terraform CLI | | [terraform-validate](terraform-validate) | Validate Terraform configuration files using the official Terraform CLI |
| [upload-artifact](upload-artifact) | Upload files as a Forgejo Actions artifact | | [upload-artifact](upload-artifact) | Upload files as a Forgejo Actions artifact |
| [vacuum-lint](vacuum-lint) | Validate and lint OpenAPI specifications using Vacuum | | [vacuum-lint](vacuum-lint) | Validate and lint OpenAPI specifications using Vacuum |
@ -35,26 +28,6 @@ Shared actions for Forgejo CI/CD pipelines.
Where third-party Forgejo/GitHub Actions are used internally, they are pinned to exact commit hashes rather than mutable tags to prevent supply chain attacks. Where third-party Forgejo/GitHub Actions are used internally, they are pinned to exact commit hashes rather than mutable tags to prevent supply chain attacks.
## Adding a new Action
- Create a new directory for the action
- Implement the action
- Add a `README.md` file that describes (1) purpose, (2) inputs using a table, (3) example usage, and additional details if requried to the action directory
- Update the table in the main README (this file) with a new row. The list is sorted alphabetically.
- Update the `tag-release.yml` workflow in the `.forgejo/` directory if the action is a public action: Add the name to the option list.
## Releasing a new Version
**We only use Major-Versions, e.g. `1`, `2`, `3`, etc.**
- Decide which Version to use
- Breaking Change: Increment the current version by one (e.g. `1 -> 2`)
- All non-breaking changes: Stay on the current major version (`1 -> 1`)
- Manually run the `tag-release.yml` workflow
- Branch: `main`
- Action: Name of the Action to release
- Version: The version to release
## Usage ## Usage
Reference actions from your project's workflow: Reference actions from your project's workflow:

View file

@ -1,84 +0,0 @@
# aws-lambda-alias-update
Composite action that updates Lambda function aliases from a Terraform output. Iterates over the `lambda_alias_updates` Terraform output and calls `aws lambda update-alias` for each entry.
**Example `lambda-alias-updates` input:**
```json
[
"{\"alias_name\": \"live\", \"function_name\": \"my-get-product\", \"version\": \"42\"}",
"{\"alias_name\": \"live\", \"function_name\": \"my-get-category\", \"version\": \"7\"}"
]
```
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `lambda-alias-updates` | Yes | — | JSON array of Lambda alias update objects (Terraform output: `lambda_alias_updates`). Each element is a JSON-encoded string with `alias_name`, `function_name`, and `version`. |
## Usage
```yaml
- name: Update Lambda Aliases
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-lambda-alias-update@aws-lambda-alias-update-v1
with:
lambda-alias-updates: ${{ steps.tf-apply.outputs.lambda_alias_updates }}
```
## Terraform Setup
- Add the following content to the project
- Add all Lambda Modules to the `provisioned_lambda_modules` list for which the Function Alias and/or Provisioned Concurrency should be updated
**`output.tf`**
```tf
locals {
// List of Lambda Modules that have provisioned concurrency configured.
// Required to update the aliases of these functions after deployment.
provisioned_lambda_modules = [
module.lambda_get_category,
module.lambda_product_get_full_slug,
module.lambda_get_product,
]
}
// Output which allows Updates of Lambda Alias and Provisioned Concurrency
output "lambda_alias_updates" {
value = concat([for module in local.provisioned_lambda_modules : "{\"alias_name\": \"${module.lambda_alias_name}\", \"function_name\": \"${module.lambda_name}\", \"version\": \"${module.lambda_version}\" }"])
}
```
## Example Usage with other Shared Actions
```yml
jobs:
deploy-stage:
name: Build and Deploy to Stage
runs-on: stackit-ubuntu-22
steps:
- name: Apply Terraform
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/terraform-apply@terraform-apply-v1
id: tf-apply
with:
terraform-version: 1.14.9
workspace: stage
var-file: stage.tfvars
jfrog-token: ${{ secrets.JFROG_TOKEN }}
- name: Update Lambda Aliases
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-lambda-alias-update@aws-lambda-alias-update-v1
with:
lambda-alias-updates: ${{ steps.tf-apply.outputs.lambda_alias_updates }}
- name: Wait for Lambda Provisioned Concurrency
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-lambda-wait-for-provisioned-concurrency@aws-lambda-wait-for-provisioned-concurrency-v1
with:
lambda-alias-updates: ${{ steps.tf-apply.outputs.lambda_alias_updates }}
```
## Notes
- Expects the `lambda-alias-updates` input to be the raw `lambda_alias_updates` output from the `terraform-apply` action.
- Requires AWS credentials to be configured in the job before this step runs.

View file

@ -1,49 +0,0 @@
name: "AWS Lambda - Update Alias"
description: >
Updates Lambda function aliases from a Terraform output.
Iterates over the lambda_alias_updates Terraform output and calls
aws lambda update-alias for each entry.
inputs:
lambda-alias-updates:
description: >
JSON array of Lambda alias update objects (Terraform output: lambda_alias_updates).
Each element is a JSON-encoded string with alias_name, function_name, and version.
required: true
runs:
using: "composite"
steps:
- name: Install AWS CLI
shell: bash
run: |
if ! command -v aws &> /dev/null; then
curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip
unzip -q /tmp/awscliv2.zip -d /tmp
sudo /tmp/aws/install
rm -rf /tmp/awscliv2.zip /tmp/aws
fi
- name: Install jq if missing
shell: bash
run: |
set -euo pipefail
command -v jq >/dev/null 2>&1 || sudo apt-get install -y --no-install-recommends jq
- name: Update Lambda Aliases
shell: bash
env:
LAMBDA_ALIAS_UPDATES: ${{ inputs.lambda-alias-updates }}
run: |
echo "$LAMBDA_ALIAS_UPDATES" | jq -c '.[] | fromjson' | while IFS= read -r entry; do
alias_name=$(echo "$entry" | jq -r '.alias_name')
function_name=$(echo "$entry" | jq -r '.function_name')
version=$(echo "$entry" | jq -r '.version')
echo "Updating alias '$alias_name' for '$function_name' to version '$version'"
aws lambda update-alias \
--no-cli-pager \
--name "$alias_name" \
--function-name "$function_name" \
--function-version "$version"
echo "Updated alias '$alias_name' for '$function_name' to version '$version'"
done

View file

@ -1,86 +0,0 @@
# aws-lambda-wait-for-provisioned-concurrency
Composite action that waits for provisioned concurrency to reach `READY` status for all Lambda functions listed in the Terraform `lambda_alias_updates` output. Iterates over the `lambda_alias_updates` Terraform output and polls `aws lambda get-provisioned-concurrency-config` for each entry until the status is `READY` or `FAILED`.
**Example `lambda-alias-updates` input:**
```json
[
"{\"alias_name\": \"live\", \"function_name\": \"my-get-product\", \"version\": \"42\"}",
"{\"alias_name\": \"live\", \"function_name\": \"my-get-category\", \"version\": \"7\"}"
]
```
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `lambda-alias-updates` | Yes | — | JSON array of Lambda alias update objects (Terraform output: `lambda_alias_updates`). Each element is a JSON-encoded string with `alias_name`, `function_name`, and `version`. |
## Usage
```yaml
- name: Wait for Lambda Provisioned Concurrency
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-lambda-wait-for-provisioned-concurrency@aws-lambda-wait-for-provisioned-concurrency-v1
with:
lambda-alias-updates: ${{ steps.tf-apply.outputs.lambda_alias_updates }}
```
## Terraform Setup
- Add the following content to the project
- Add all Lambda Modules to the `provisioned_lambda_modules` list for which the Function Alias and/or Provisioned Concurrency should be updated
**`output.tf`**
```tf
locals {
// List of Lambda Modules that have provisioned concurrency configured.
// Required to update the aliases of these functions after deployment.
provisioned_lambda_modules = [
module.lambda_get_category,
module.lambda_product_get_full_slug,
module.lambda_get_product,
]
}
// Output which allows Updates of Lambda Alias and Provisioned Concurrency
output "lambda_alias_updates" {
value = concat([for module in local.provisioned_lambda_modules : "{\"alias_name\": \"${module.lambda_alias_name}\", \"function_name\": \"${module.lambda_name}\", \"version\": \"${module.lambda_version}\" }"])
}
```
## Example Usage with other Shared Actions
```yml
jobs:
deploy-stage:
name: Build and Deploy to Stage
runs-on: stackit-ubuntu-22
steps:
- name: Apply Terraform
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/terraform-apply@terraform-apply-v1
id: tf-apply
with:
terraform-version: 1.14.9
workspace: stage
var-file: stage.tfvars
jfrog-token: ${{ secrets.JFROG_TOKEN }}
- name: Update Lambda Aliases
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-lambda-alias-update@aws-lambda-alias-update-v1
with:
lambda-alias-updates: ${{ steps.tf-apply.outputs.lambda_alias_updates }}
- name: Wait for Lambda Provisioned Concurrency
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-lambda-wait-for-provisioned-concurrency@aws-lambda-wait-for-provisioned-concurrency-v1
with:
lambda-alias-updates: ${{ steps.tf-apply.outputs.lambda_alias_updates }}
```
## Notes
- Expects the `lambda-alias-updates` input to be the raw `lambda_alias_updates` output from the `terraform-apply` action.
- Functions without provisioned concurrency configured are skipped automatically.
- If provisioned concurrency reaches `FAILED` status, the action logs a warning and continues without failing the workflow.
- Requires AWS credentials to be configured in the job before this step runs.

View file

@ -1,64 +0,0 @@
name: "AWS Lambda - Wait for Provisioned Concurrency"
description: >
Waits for provisioned concurrency to reach READY status for all Lambda
functions listed in the Terraform lambda_alias_updates output.
inputs:
lambda-alias-updates:
description: >
JSON array of Lambda alias update objects (Terraform output: lambda_alias_updates).
Each element is a JSON-encoded string with alias_name, function_name, and version.
required: true
runs:
using: "composite"
steps:
- name: Install AWS CLI
shell: bash
run: |
if ! command -v aws &> /dev/null; then
curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip
unzip -q /tmp/awscliv2.zip -d /tmp
sudo /tmp/aws/install
rm -rf /tmp/awscliv2.zip /tmp/aws
fi
- name: Install jq if missing
shell: bash
run: |
set -euo pipefail
command -v jq >/dev/null 2>&1 || sudo apt-get install -y --no-install-recommends jq
- name: Wait for Lambda Provisioned Concurrency
shell: bash
env:
LAMBDA_ALIAS_UPDATES: ${{ inputs.lambda-alias-updates }}
run: |
echo "$LAMBDA_ALIAS_UPDATES" | jq -c '.[] | fromjson' | while IFS= read -r entry; do
function_name=$(echo "$entry" | jq -r '.function_name')
alias_name=$(echo "$entry" | jq -r '.alias_name')
if aws lambda get-provisioned-concurrency-config \
--no-cli-pager \
--function-name "$function_name" \
--qualifier "$alias_name" >/dev/null 2>&1; then
echo "Provisioned concurrency found, waiting for READY status... ($function_name:$alias_name)"
while true; do
STATUS=$(aws lambda get-provisioned-concurrency-config \
--no-cli-pager \
--function-name "$function_name" \
--qualifier "$alias_name" \
--query 'Status' \
--output text 2>/dev/null || echo "FAILED")
echo "Current status: $STATUS ($function_name:$alias_name)"
if [[ "$STATUS" == "READY" ]]; then
echo "Provisioned Concurrency - Ready ($function_name:$alias_name)"
break
elif [[ "$STATUS" == "FAILED" ]]; then
echo "Provisioned concurrency failed, continuing anyway ($function_name:$alias_name)"
break
fi
done
else
echo "No provisioned concurrency configured, skipping wait ($function_name:$alias_name)"
fi
done

View file

@ -1,32 +0,0 @@
# esb-deploy
Deploy a service to an ESB docker host.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `docker-host` | Yes | - | esbdb3.schmalzgroup.net, esbdb4.schmalzgroup.net, esbdb2-stage.schmalzgroup.net|
| `java-version` | Yes | 25 | Same as default of the maven-build action |
| `maven-profile` | No | `test` | Maven profile to activate during deploy |
| `maven-settings` | **Yes** | — | Secret containing the `settings.xml` content used for repository authentication |
| `service` | Yes | — | Name of the service to deploy |
| `stage` | No | true | If true this is a stage deployment |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/esb-deploy@esb-deploy-v1
with:
service: my-service
docker-host: esbdocker2-stage.schmalzgroup.net
java-version: 8
maven-profile: test
maven-settings: ${{ secrets.MAVEN_SETTINGS }}
stage: true
```
## Notes
- The compose files are extracted from variables. They can be provided on the organization or repository level.
- The action uses the maven-build action to build the service. The pom.xml has to be in the root directory

View file

@ -1,64 +0,0 @@
name: Deploy ESB
description: Deploy a service to an ESB docker host.
inputs:
docker-host:
description: Docker host to deploy to
required: true
maven-profile:
required: false
default: 'test'
description: 'Maven profile to use for the build'
maven-settings:
description: Secret containing the settings.xml content used for repository authentication
required: true
java-version:
description: Java version to use for the build
required: true
service:
description: Name of the service to deploy
required: true
stage:
description: Whether to deploy to stage environment (true) or production environment (false)
required: false
default: 'true'
runs:
using: composite
steps:
- name: Create compose files
shell: bash
env:
BASE_COMPOSE: ${{ vars.DOCKER_COMPOSE }}
SU_COMPOSE: ${{ vars.DOCKER_COMPOSE_SU }}
run: |
printf '%s\n' "$BASE_COMPOSE" > compose.yml
printf '%s\n' "$SU_COMPOSE" > compose-su.yml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/maven-build@maven-build-v1
with:
phase: verify
maven-settings: ${{ inputs.maven-settings }}
verify-goals: clean package
java-version: ${{ inputs.java-version }}
maven-profile: ${{ inputs.maven-profile}}
- name: Compose stage
if: ${{ inputs.stage == 'true' }}
shell: bash
env:
SERVICE: ${{ inputs.service }}
run: |
echo "Deploying $SERVICE to stage environment"
export DOCKER_HOST="tcp://${{ inputs.docker-host }}:2375"
docker compose -f compose.yml -f compose-su.yml up -d --build --no-deps "$SERVICE"
- name: Compose prod
if: ${{ inputs.stage != 'true' }}
shell: bash
env:
SERVICE: ${{ inputs.service }}
run: |
echo "Deploying $SERVICE to production environment"
export DOCKER_HOST="tcp://${{ inputs.docker-host }}:2375"
docker compose -f compose.yml up -d --build --no-deps "$SERVICE"

View file

@ -0,0 +1,39 @@
# generate-release-notes
Collects the git commit log between two refs and generates human-readable release notes using [Opencode](https://opencode.ai) with AWS Bedrock.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `bedrock-bearer-token` | Yes | — | AWS Bedrock bearer token for Opencode |
| `base-ref` | Yes | — | Base branch for git log comparison (e.g. `main` or `dev`) |
| `head-ref` | Yes | — | Head branch for git log comparison (e.g. `HEAD` or `dev`) |
| `section-title` | Yes | — | Title for the commits section in the generated markdown |
| `empty-message` | Yes | — | Message to include when no commits are found |
| `include-diff-stat` | No | `true` | Include a "Changed files" section with `git diff --stat` output |
## Outputs
| Output | Description |
|--------|-------------|
| `release-note` | The generated release notes as a markdown string |
## Usage
```yaml
- id: notes
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/generate-release-notes@generate-release-notes-v1
with:
bedrock-bearer-token: ${{ secrets.BEDROCK_BEARER_TOKEN }}
base-ref: main
head-ref: HEAD
section-title: My Service
empty-message: No changes since last release.
```
## Notes
- Requires the repository to be checked out with enough history for the comparison (use `fetch-depth: 0` in the `checkout` action).
- Release notes are generated in German and categorised into 💡 Features, 🪲 Bugfixes, and 🧹 Wartung based on conventional commit prefixes.
- The output can be passed directly to the `publish-release-notes-via-webhook` action via `${{ steps.notes.outputs.release-note }}`.

View file

@ -0,0 +1,92 @@
name: Generate Release Notes
description: Collects git diff and generates release notes using GitHub Copilot CLI
inputs:
bedrock-bearer-token:
description: AWS Bedrock Bearer token for Opencode
required: true
base-ref:
description: Base branch name for git log comparison (e.g. "dev" or "main")
required: true
head-ref:
description: Head branch name for git log comparison (e.g. "HEAD" or "dev")
required: true
section-title:
description: Title for the commits section in the generated markdown
required: true
empty-message:
description: Message to include when no commits are found
required: true
include-diff-stat:
description: Include a "Changed files" section with git diff --stat output
default: 'true'
outputs:
release-note:
description: The generated release notes as a markdown string
value: ${{ steps.generate.outputs.release-note }}
runs:
using: composite
steps:
- name: Collect diff
shell: bash
run: |
git fetch origin ${{ inputs.base-ref }} ${{ inputs.head-ref }}
BASE_REF="origin/${{ inputs.base-ref }}"
HEAD_REF="${{ inputs.head-ref }}"
[[ "$HEAD_REF" != "HEAD" ]] && HEAD_REF="origin/$HEAD_REF"
BRANCH_COMMITS=$(git log $BASE_REF..$HEAD_REF \
--pretty=format:"- %h %s (%an, %ad)" \
--date=short)
{
echo "## ${{ inputs.section-title }}"
echo ""
echo "### Commits"
echo ""
if [ -z "$BRANCH_COMMITS" ]; then
echo "${{ inputs.empty-message }}"
else
echo "$BRANCH_COMMITS"
fi
} > commits.md
if [ "${{ inputs.include-diff-stat }}" = "true" ]; then
DIFF_STAT=$(git diff --stat $BASE_REF...$HEAD_REF)
{
echo ""
echo "### Changed files"
echo ""
if [ -z "$DIFF_STAT" ]; then
echo "No file changes found."
else
echo "$DIFF_STAT"
fi
} >> commits.md
fi
echo "--- commits.md ---"
cat commits.md
- name: Write Copilot output to Markdown
id: generate
shell: bash
env:
AWS_BEARER_TOKEN_BEDROCK: ${{ inputs.bedrock-bearer-token }}
PROMPT: "Erstelle Release Notes auf Basis der folgenden Commit-Liste. Release Notes bestehen aus einer Aufzählung mit jeweils einem Aufzählungspunkt pro Änderung. Die Aufzählung wird eingeleitet mit \"Folgende Änderungen wurden in Betrieb genommen:\". Die Änderungen sind nach Art der Änderung sortiert. Es gibt 3 Kategorien: \"💡 Features\", \"🪲 Bugfixes\" und \"🧹 Wartung\". Zu welcher Kategorie eine Änderung gehört, hängt vom Präfix der zugehörigen Commitnachricht ab. Wenn es zu einer Kategorie keine Änderungen gibt, wird sie weggelassen. Am Anfang jedes Punkts steht eine kurze, fettgedruckte Zusammenfassung der Änderung. Danach steht eine sachliche Beschreibung der Änderung in ganzen Sätzen. Sie richtet sich an ein nicht technisches Publikum, das aber mit der Materie vertraut ist (Product Owner, UX Designer, Team Lead). Beschränke dich daher aufs Wesentliche und verschweige technische Einzelheiten. Vermeide Abkürzungen, wenn du deren Bedeutung kennst. Bevorzuge aber die englischen Fachbegriffe. Am Ende jeder Änderung erwähne in Klammern den Autor sowie die referenzierten Ticketnummern, sofern vorhanden. Jira Tickets sollen mit Link eingefügt werden: https://jira.schmalz.com/browse/<TICKET_ID>"
run: |
curl -fsSL https://opencode.ai/install | bash
COMMITS=$(cat commits.md)
AWS_BEARER_TOKEN_BEDROCK=$AWS_BEARER_TOKEN_BEDROCK opencode run "$PROMPT $COMMITS" > release-notes.md
echo "--- release-notes.md ---"
cat release-notes.md
{
echo "release-note<<EOF"
cat release-notes.md
echo "EOF"
} >> "$GITHUB_OUTPUT"

View file

@ -94,11 +94,9 @@ runs:
env: env:
VERIFY_GOALS: ${{ inputs.verify-goals }} VERIFY_GOALS: ${{ inputs.verify-goals }}
EXTRA_ARGS: ${{ inputs.extra-args }} EXTRA_ARGS: ${{ inputs.extra-args }}
MAVEN_PROFILE: ${{ inputs.maven-profile }}
run: | run: |
mvn --batch-mode $VERIFY_GOALS \ mvn --batch-mode $VERIFY_GOALS \
-s /tmp/maven-settings.xml \ -s /tmp/maven-settings.xml \
-P "$MAVEN_PROFILE" \
$EXTRA_ARGS $EXTRA_ARGS
- name: Deploy - name: Deploy

View file

@ -1,39 +0,0 @@
# playwright-merge
Download all Playwright blob reports from shard jobs, merge them into a single HTML + JUnit report, and optionally upload the HTML report to S3.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `working-directory` | No | `.` | Directory containing `package.json` and `playwright.config.ts` |
| `node-version` | No | `24` | Node.js version |
| `pnpm-version` | No | `10.33` | pnpm version |
| `jfrog-token` | No | `""` | JFrog npm auth token |
| `role-arn` | Yes | — | IAM role ARN for AWS authentication |
| `aws-access-key-id` | Yes | — | AWS access key ID |
| `aws-secret-access-key` | Yes | — | AWS secret access key |
| `project-name` | Yes | — | Project name used as the folder in the report bucket (e.g. `hs-pricelist`) |
| `publish-test-reports` | No | `true` | Whether to upload the report to S3 and print the URL at `https://test-reports.schmalz.com` |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/playwright-merge@playwright-merge-v1
with:
working-directory: frontend
node-version: 22
jfrog-token: ${{ secrets.JFROG_TOKEN }}
role-arn: arn:aws:iam::953815822701:role/deployment-role
aws-access-key-id: ${{ secrets.DB_STAGE_DEPLOYMENT_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.DB_STAGE_DEPLOYMENT_SECRET_ACCESS_KEY }}
project-name: hs-pricelist
```
## Notes
- Intended as a follow-up job after all `playwright-run` shard jobs complete.
- Downloads shard artifacts into `all-blob-reports/` and merges them with `playwright merge-reports`.
- Generates HTML report in `playwright-report/` and JUnit XML in `test-results/junit.xml`.
- Uploads report files to `s3://com.schmalz.db.stage.test-reports/reports/<project-name>/<timestamp>/` when `publish-test-reports` is `true`.
- Prints the final report URL on `https://test-reports.schmalz.com`.

View file

@ -1,104 +0,0 @@
name: Playwright Merge
description: >
Download all blob reports from shard jobs, merge them into a single HTML + JUnit
report, upload the HTML report to S3, and upload the JUnit report as an artifact.
Call this in a follow-up job after all playwright-run shard jobs complete.
inputs:
working-directory:
description: Directory containing package.json and playwright.config.ts
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: ""
role-arn:
description: IAM role ARN for AWS authentication
required: true
aws-access-key-id:
description: AWS access key ID
required: true
aws-secret-access-key:
description: AWS secret access key
required: true
project-name:
description: Project name used as the folder in the report bucket (e.g. hs-pricelist)
required: true
publish-test-reports:
description: Whether to upload the report to S3 and print the URL at https://test-reports.schmalz.com. Set to false to skip upload entirely.
required: false
default: "true"
runs:
using: composite
steps:
- name: Install AWS CLI
shell: bash
run: |
if ! command -v aws &>/dev/null; then
curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip
unzip -q /tmp/awscliv2.zip -d /tmp
sudo /tmp/aws/install
rm -rf /tmp/awscliv2.zip /tmp/aws
fi
- name: Configure AWS
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aws-configure@aws-configure-v1
with:
role-arn: ${{ inputs.role-arn }}
aws-access-key-id: ${{ inputs.aws-access-key-id }}
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
- name: Install dependencies
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/pnpm-build@pnpm-build-v1
with:
working-directory: ${{ inputs.working-directory }}
node-version: ${{ inputs.node-version }}
pnpm-version: ${{ inputs.pnpm-version }}
jfrog-token: ${{ inputs.jfrog-token }}
run-scripts: ""
frozen-lockfile: "true"
check-dedupe: "false"
- name: Download blob reports
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/download-artifact@download-artifact-v1
with:
path: ${{ inputs.working-directory }}/all-blob-reports/
merge-multiple: "true"
- name: Merge blob reports
shell: bash
env:
CI: "true"
WORKING_DIR: ${{ inputs.working-directory }}
PLAYWRIGHT_JUNIT_OUTPUT_NAME: test-results/junit.xml
run: |
cd "${WORKING_DIR}"
pnpm exec playwright merge-reports \
--reporter=html,junit \
all-blob-reports/
- name: Upload report to S3
if: ${{ inputs.publish-test-reports == 'true' }}
shell: bash
env:
WORKING_DIR: ${{ inputs.working-directory }}
PROJECT_NAME: ${{ inputs.project-name }}
run: |
TIMESTAMP=$(date +%s)
S3_BUCKET=com.schmalz.db.stage.test-reports
S3_PATH="s3://${S3_BUCKET}/reports/${PROJECT_NAME}/${TIMESTAMP}"
aws s3 sync "${WORKING_DIR}/playwright-report/" "${S3_PATH}/"
if [ -f "${WORKING_DIR}/test-results/junit.xml" ]; then
aws s3 cp "${WORKING_DIR}/test-results/junit.xml" "${S3_PATH}/junit.xml"
fi
echo "Report URL: https://test-reports.schmalz.com/reports/${PROJECT_NAME}/${TIMESTAMP}/index.html"

View file

@ -1,62 +0,0 @@
# playwright-run
Run Playwright E2E tests for one shard and upload the blob report as an artifact.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `working-directory` | No | `.` | Directory containing `package.json` and `playwright.config.ts` |
| `node-version` | No | `24` | Node.js version |
| `pnpm-version` | No | `10.33` | pnpm version |
| `jfrog-token` | No | `""` | JFrog npm auth token |
| `shard-index` | No | `1` | Current shard index (1-based). Set to `1` when not sharding. |
| `shard-total` | No | `1` | Total number of shards. Set to `1` to disable sharding. |
| `no-deps` | No | `false` | Skip dependencies between Playwright projects (e.g. setup/teardown). Passes `--no-deps` to Playwright. |
| `projects` | No | `""` | Comma-separated list of Playwright projects to run (e.g. `chromium,firefox,Mobile Chrome`). Leave empty to use the Playwright default. |
| `artifact-retention-days` | No | `3` | Number of days to retain the blob report artifact |
## Usage
### Basic
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/playwright-run@playwright-run-v1
with:
working-directory: e2e
node-version: 22
jfrog-token: ${{ secrets.JFROG_TOKEN }}
```
### Sharded
```yaml
jobs:
test:
name: "Test Shard ${{ matrix.shard-index }}/${{ matrix.total }}"
# Define the matrix strategy on the parent job:
strategy:
fail-fast: false
matrix:
total: [5] # The same for all instances
shard-index: [1, 2, 3, 4, 5]
steps:
# ...other steps like checkout repo etc.
- name: Run tests
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/playwright-run@playwright-run-v1
with:
working-directory: frontend
node-version: 22
jfrog-token: ${{ secrets.JFROG_TOKEN }}
# Matrix data is passed here:
shard-index: ${{ matrix.shard-index }}
shard-total: ${{ matrix.total }}
no-deps: "true"
projects: "chromium,firefox,webkit,Mobile Chrome,Mobile Safari"
```
## Notes
- Intended for matrix shard jobs.
- Uploads one artifact per shard named `blob-report-<shard-index>`.
- Use `playwright-merge` in a follow-up job to merge shard reports.

View file

@ -1,108 +0,0 @@
name: Playwright Run
description: >
Run Playwright E2E tests for one shard and upload the blob report as an artifact.
Call this from a matrix job. Use the playwright-merge action in a follow-up job
to produce the final HTML + JUnit report.
inputs:
working-directory:
description: Directory containing package.json and playwright.config.ts
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: ""
shard-index:
description: Current shard index (1-based). Set to 1 when not sharding.
required: false
default: "1"
shard-total:
description: Total number of shards. Set to 1 to disable sharding.
required: false
default: "1"
no-deps:
description: Whether to ignore dependencies between Playwright projects (e.g. setup, teardown)
required: false
default: false
projects:
description: Comma-separated list of Playwright projects to include, leave empty to use the Playwright default
required: false
default: ""
artifact-retention-days:
description: Number of days to retain the blob report artifact
required: false
default: "3"
runs:
using: composite
steps:
- name: Install dependencies
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/pnpm-build@pnpm-build-v1
with:
working-directory: ${{ inputs.working-directory }}
node-version: ${{ inputs.node-version }}
pnpm-version: ${{ inputs.pnpm-version }}
jfrog-token: ${{ inputs.jfrog-token }}
run-scripts: ""
frozen-lockfile: "true"
check-dedupe: "false"
- name: Cache Playwright browsers
id: playwright-cache
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-playwright-
- name: Install Playwright browsers
if: ${{ steps.playwright-cache.outputs.cache-hit != 'true' }}
shell: bash
env:
WORKING_DIR: ${{ inputs.working-directory }}
run: pnpm --prefix="${WORKING_DIR}" exec playwright install --with-deps
- name: Run Playwright tests
shell: bash
env:
CI: "true"
WORKING_DIR: ${{ inputs.working-directory }}
SHARD_INDEX: ${{ inputs.shard-index }}
SHARD_TOTAL: ${{ inputs.shard-total }}
NO_DEPS: ${{ inputs.no-deps }}
PROJECTS: ${{ inputs.projects }}
run: |
SHARD_ARG=""
if [ "${SHARD_TOTAL}" != "1" ]; then
SHARD_ARG="--shard=${SHARD_INDEX}/${SHARD_TOTAL}"
fi
NO_DEPS_ARG=""
if [ "${NO_DEPS}" == "true" ]; then
NO_DEPS_ARG="--no-deps"
fi
PROJECTS_ARG=()
if [ -n "${PROJECTS}" ]; then
IFS=',' read -ra PROJECT_LIST <<< "${PROJECTS}"
for project in "${PROJECT_LIST[@]}"; do
PROJECTS_ARG+=("--project=${project}")
done
fi
pnpm --prefix="${WORKING_DIR}" exec playwright test ${SHARD_ARG} ${NO_DEPS_ARG} "${PROJECTS_ARG[@]}" --reporter=blob,dot
- name: Upload blob report
if: ${{ !cancelled() }}
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/upload-artifact@upload-artifact-v1
with:
name: blob-report-${{ inputs.shard-index }}
path: ${{ inputs.working-directory }}/blob-report/
retention-days: ${{ inputs.artifact-retention-days }}
if-no-files-found: ignore

View file

@ -1,28 +0,0 @@
# publish-npm-package
Publish a PNPM package to JFrog Artifactory.
## 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` | Yes | — | JFrog npm auth token |
| `registry-url` | No | `https://schmalz.jfrog.io/artifactory/api/npm/default-npm/` | npm registry URL |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-npm-package@publish-npm-package-v1
with:
working-directory: .
jfrog-token: ${{ secrets.JFROG_TOKEN }}
```
## Notes
- Publishes with `pnpm publish`.
- Configures the registry auth token from `registry-url` and `jfrog-token`.
- Third-party actions used internally are pinned to exact commit SHAs to prevent supply chain attacks.

View file

@ -1,64 +0,0 @@
name: publish-npm-package
description: Publish a PNPM package to JFrog Artifactory.
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: true
registry-url:
description: npm registry URL
required: false
default: "https://schmalz.jfrog.io/artifactory/api/npm/default-npm/"
runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# actions/setup-node v4.4.0 — https://code.forgejo.org/actions/setup-node/commits/tag/v4.4.0
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: ${{ inputs.node-version }}
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# pnpm/action-setup v4.3.0 — https://code.forgejo.org/pnpm/action-setup/commits/tag/v4.3.0
- name: Install pnpm
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1
env:
# pnpm/action-setup bootstraps itself via npm before pnpm is available,
# so it must reach the public npm registry.
NPM_CONFIG_REGISTRY: https://registry.npmjs.org
with:
version: ${{ inputs.pnpm-version }}
- name: Configure JFrog registry authentication
shell: bash
env:
JFROG_TOKEN: ${{ inputs.jfrog-token }}
REGISTRY_URL: ${{ inputs.registry-url }}
run: |
set -euo pipefail
pnpm set registry "${REGISTRY_URL}"
AUTHORITY="${REGISTRY_URL#https://}"
AUTHORITY="${AUTHORITY#http://}"
AUTHORITY="${AUTHORITY%/}"
pnpm set "//${AUTHORITY}/:_authToken" "${JFROG_TOKEN}"
- name: Publish
shell: bash
working-directory: ${{ inputs.working-directory }}
run: pnpm publish

View file

@ -0,0 +1,44 @@
# publish-release-notes-via-webhook
Sends release notes to a Microsoft Teams channel via a Power Automate webhook.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `service-name` | Yes | — | Name of the service included in the webhook payload |
| `release-note` | Yes | — | Release notes markdown content to publish |
| `request-url` | Yes | — | Power Automate webhook URL |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-release-notes-via-webhook@publish-release-notes-via-webhook-v1
with:
service-name: my-service
release-note: ${{ steps.notes.outputs.release-note }}
request-url: ${{ secrets.TEAMS_WEBHOOK_URL }}
```
## Webhook Payload
The action sends a `POST` request with the following JSON body:
```json
{
"servicename": "my-service",
"releasenote": "Folgende Änderungen wurden in Betrieb genommen:\n..."
}
```
| Field | Type | Description |
|-------|------|-------------|
| `servicename` | string | Value of the `service-name` input |
| `releasenote` | string | Value of the `release-note` input (markdown) |
The Power Automate flow must accept a `POST` request with `Content-Type: application/json` and handle both fields.
## Notes
- Designed to be used together with the `generate-release-notes` action, which exposes the `release-note` output.
- The webhook URL should be stored as a secret and never hardcoded in the workflow.

View file

@ -0,0 +1,41 @@
name: Publish Release Notes with Webhook
description: Sends generated release notes to a Teams webhook via Power Automate
inputs:
service-name:
description: Name of the service for the webhook payload
required: true
release-note:
description: The release notes markdown content to publish
required: true
request-url:
description: Power Automate webhook URL
required: true
runs:
using: composite
steps:
- name: Trigger Teams Webhook with release notes
shell: bash
env:
SERVICE_NAME: ${{ inputs.service-name }}
RELEASE_NOTE: ${{ inputs.release-note }}
REQUEST_URL: ${{ inputs.request-url }}
run: |
RELEASE_NOTE_CONTENT="$RELEASE_NOTE"
WEBHOOK_PAYLOAD=$(jq -n \
--arg servicename "$SERVICE_NAME" \
--arg releasenote "$RELEASE_NOTE_CONTENT" \
'{
"servicename": ($servicename),
"releasenote": ($releasenote)
}')
WEBHOOK_RESPONSE=$(curl -s -w "\n__HTTP_STATUS__:%{http_code}" -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
"$REQUEST_URL" \
-d "$WEBHOOK_PAYLOAD")
echo "Webhook response: $WEBHOOK_RESPONSE"

View file

@ -1,29 +0,0 @@
# publish-rust-crate
Publish a Rust crate to JFrog Artifactory.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `working-directory` | No | `.` | Directory containing `Cargo.toml` |
| `rust-version` | No | `1.95.0` | Rust toolchain version |
| `jfrog-token` | Yes | — | JFrog token for the Artifactory Cargo registry |
| `registry-name` | No | `artifactory` | Cargo registry name |
| `registry-index` | No | `sparse+https://schmalz.jfrog.io/artifactory/api/cargo/schmalz-cargo-local/index/` | Cargo registry index URL |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-rust-crate@publish-rust-crate-v1
with:
working-directory: .
jfrog-token: ${{ secrets.JFROG_TOKEN }}
```
## Notes
- Configures Cargo registry settings in `${CARGO_HOME}/config.toml` and `${CARGO_HOME}/credentials.toml`.
- Falls back to `$HOME/.cargo` when `CARGO_HOME` is not set.
- Publishes with `cargo publish --registry <registry-name>`.
- Third-party actions used internally are pinned to exact commit SHAs to prevent supply chain attacks.

View file

@ -1,64 +0,0 @@
name: publish-rust-crate
description: Publish a Rust crate to JFrog Artifactory.
inputs:
working-directory:
description: Directory containing Cargo.toml
required: false
default: "."
rust-version:
description: Rust toolchain version
required: false
default: "1.95.0"
jfrog-token:
description: JFrog token for the Artifactory Cargo registry
required: true
registry-name:
description: Cargo registry name
required: false
default: artifactory
registry-index:
description: Cargo registry index URL
required: false
default: "sparse+https://schmalz.jfrog.io/artifactory/api/cargo/schmalz-cargo-local/index/"
runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# dtolnay/rust-toolchain v1 (2026-03-27) — https://github.com/dtolnay/rust-toolchain/commit/3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
with:
toolchain: ${{ inputs.rust-version }}
- name: Configure Cargo registry (JFrog Artifactory)
shell: bash
env:
JFROG_TOKEN: ${{ inputs.jfrog-token }}
REGISTRY_NAME: ${{ inputs.registry-name }}
REGISTRY_INDEX: ${{ inputs.registry-index }}
run: |
set -euo pipefail
CARGO_HOME_DIR="${CARGO_HOME:-$HOME/.cargo}"
mkdir -p "${CARGO_HOME_DIR}"
cat >> "${CARGO_HOME_DIR}/config.toml" <<EOF
[registries.${REGISTRY_NAME}]
index = "${REGISTRY_INDEX}"
[registry]
global-credential-providers = ["cargo:token"]
EOF
cat >> "${CARGO_HOME_DIR}/credentials.toml" <<EOF
[registries.${REGISTRY_NAME}]
token = "Bearer ${JFROG_TOKEN}"
EOF
- name: Publish
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
REGISTRY_NAME: ${{ inputs.registry-name }}
run: cargo publish --registry "${REGISTRY_NAME}"

View file

@ -92,7 +92,7 @@ runs:
for check in "${CHECKS[@]}"; do for check in "${CHECKS[@]}"; do
case "${check}" in case "${check}" in
fmt) cargo fmt --manifest-path="${WORKING_DIR}/Cargo.toml" --check ;; fmt) cargo fmt --manifest-path="${WORKING_DIR}/Cargo.toml" --check ;;
clippy) cargo clippy --manifest-path="${WORKING_DIR}/Cargo.toml" --target="${CROSS_TARGET}" ;; clippy) cargo clippy --manifest-path="${WORKING_DIR}/Cargo.toml" --target="${CROSS_TARGET}" -- -D warnings ;;
test) cargo test --manifest-path="${WORKING_DIR}/Cargo.toml" ;; test) cargo test --manifest-path="${WORKING_DIR}/Cargo.toml" ;;
*) echo "Unknown check: ${check}"; exit 1 ;; *) echo "Unknown check: ${check}"; exit 1 ;;
esac esac

View file

@ -61,7 +61,7 @@ runs:
TF_DIR: ${{ inputs.terraform-dir }} TF_DIR: ${{ inputs.terraform-dir }}
TF_WORKSPACE_NAME: ${{ inputs.workspace }} TF_WORKSPACE_NAME: ${{ inputs.workspace }}
run: | run: |
terraform -chdir="$TF_DIR" workspace select -or-create "$TF_WORKSPACE_NAME" terraform -chdir="$TF_DIR" workspace select "$TF_WORKSPACE_NAME"
- name: Terraform Apply - name: Terraform Apply
shell: bash shell: bash

View file

@ -1,47 +0,0 @@
# terraform-plan
Plan 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 |
| `var-file` | No | `""` | Path to `.tfvars` file, relative to `terraform-dir` |
| `workspace` | No | `""` | Terraform workspace to select |
| `jfrog-token` | No | `""` | JFrog Artifactory token for the Terraform provider registry (`TF_TOKEN_schmalz_jfrog_io`) |
## Outputs
No outputs are exported.
Terraform `plan` only previews changes and does not produce finalized output values in state.
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/terraform-plan@terraform-plan-v1
id: tf-plan
with:
workspace: stage
var-file: stage.tfvars
jfrog-token: ${{ secrets.JFROG_TOKEN }}
``
## Notes
- Runs `terraform init`, selects the workspace according to PR, and executes `terraform plan`.
- Does **not** apply any changes — it only previews what Terraform would do.
- Helps identify infrastructure changes before execution, such as:
- Resources that will be created
- Resources that will be updated
- Resources that will be *deleted*
- Resources that will be replaced
- Useful for reviewing changes in environments.
- Helps detect unexpected changes caused by provider version updates, module updates, variable changes, or Terraform configuration changes.
- Improves deployment safety by showing the impact of changes before `terraform apply`.
- Sets `TF_TOKEN_schmalz_jfrog_io` on both `init` and `plan` steps if `jfrog-token` is provided.
- If `var-file` is provided, it is passed as `-var-file` to the plan command.
- Commonly used in CI for pre-apply visibility, especially in pull requests or staging validation workflows.

View file

@ -1,82 +0,0 @@
name: Terraform Plan
description: >
Init and plan 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"
var-file:
description: Path to .tfvars file, relative to terraform-dir
required: false
default: ""
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 }}
# Plugin cache setup
- name: Set Terraform plugin cache directory
shell: bash
run: |
mkdir -p ~/.terraform.d/plugin-cache
echo "TF_PLUGIN_CACHE_DIR=$HOME/.terraform.d/plugin-cache" >> "$GITHUB_ENV"
# Cache providers
- name: Cache Terraform providers
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: ~/.terraform.d/plugin-cache
key: ${{ runner.os }}-terraform-providers-${{ inputs.terraform-version }}-${{ hashFiles(format('{0}/.terraform.lock.hcl', inputs.terraform-dir)) }}
restore-keys: ${{ runner.os }}-terraform-providers-${{ inputs.terraform-version }}-
# Init (backend enabled)
- name: Terraform Init
shell: bash
env:
TF_TOKEN_schmalz_jfrog_io: ${{ inputs.jfrog-token }}
TF_DIR: ${{ inputs.terraform-dir }}
run: terraform -chdir="$TF_DIR" init -no-color
# Workspace selection
- name: Terraform Select Workspace
if: ${{ inputs.workspace != '' }}
shell: bash
env:
TF_DIR: ${{ inputs.terraform-dir }}
TF_WORKSPACE_NAME: ${{ inputs.workspace }}
run: |
terraform -chdir="$TF_DIR" workspace select -or-create "$TF_WORKSPACE_NAME"
# Plan step
- name: Terraform Plan
shell: bash
env:
TF_TOKEN_schmalz_jfrog_io: ${{ inputs.jfrog-token }}
TF_DIR: ${{ inputs.terraform-dir }}
VAR_FILE: ${{ inputs.var-file }}
run: |
ARGS="-no-color"
if [ -n "$VAR_FILE" ]; then
ARGS="$ARGS -var-file=$VAR_FILE"
fi
terraform -chdir="$TF_DIR" plan $ARGS