Compare commits

..

86 commits

Author SHA1 Message Date
268081b28b
Merge pull request 'fix: actually provide maven profile' (!50) from feature/esb-deploy-action into main
Reviewed-on: #50
Reviewed-by: Kraft_Ruben_-_J._Schmalz_GmbH <Ruben.Kraft@schmalz.de>
2026-06-17 07:50:35 +00:00
9783972537 fix: actually provide maven profile
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 59s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 41s
2026-06-17 09:41:57 +02:00
ee976b306e
Merge pull request 'feature/esb-deploy-action' (!49) from feature/esb-deploy-action into main
Reviewed-on: #49
2026-06-17 06:54:26 +00:00
a49611f288 fix: maven action did not set profile in verify mode
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 57s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 35s
2026-06-17 08:52:37 +02:00
9149415575 fix: action did not provide stage toggle 2026-06-17 08:01:38 +02:00
6a84d5d6f2
Merge pull request 'fix: readme mentioned wrong parameters' (!48) from feature/esb-deploy-action into main
Reviewed-on: #48
Reviewed-by: Kraft_Ruben_-_J._Schmalz_GmbH <Ruben.Kraft@schmalz.de>
2026-06-17 05:46:13 +00:00
0134da8ac7 fix: propagate maven profile in esb-deploy action
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 57s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 42s
2026-06-17 07:42:35 +02:00
115300a7e1 fix: readme mentioned wrong parameters
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m57s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 2m16s
2026-06-16 16:32:34 +02:00
c57466f628
Merge pull request 'fix: use correct name in tag-release workflow' (!47) from feature/esb-deploy-action into main
Reviewed-on: #47
Reviewed-by: Kraft_Ruben_-_J._Schmalz_GmbH <Ruben.Kraft@schmalz.de>
2026-06-16 14:27:06 +00:00
4e15383d23 fix: use correct name in tag-release workflow
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 38s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 51s
2026-06-16 16:25:43 +02:00
c2587887a0
Merge pull request 'feat: create deploy-esb action' (!46) from feature/esb-deploy-action into main
Reviewed-on: #46
Reviewed-by: Kraft_Ruben_-_J._Schmalz_GmbH <Ruben.Kraft@schmalz.de>
2026-06-16 14:23:52 +00:00
5b6f2cfd28 feat: create deploy-esb action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 53s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m14s
2026-06-16 16:06:32 +02:00
f6549e5a5b
Merge pull request 'fix: revert workspace selection as validate does not initialize backend' (!45) from fix/terraform-validate-backend-error into main
Reviewed-on: #45
Reviewed-by: Marcel Frey <Marcel.Frey@schmalz.de>
2026-06-15 07:29:40 +00:00
36343e0a79 fix: revert workspace selection as validate does not initialize backend
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 32s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 48s
2026-06-15 09:28:00 +02:00
463f657b4a
Merge pull request 'fix: create terraform workspaces automatically in shared actions' (!44) from fix/terraform-workspace-or-create into main
Reviewed-on: #44
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-06-15 06:46:11 +00:00
312d297a55 fix: create terraform workspaces automatically in shared actions
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m5s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 41s
2026-06-15 12:09:09 +05:30
add4dd1b95
Merge pull request 'feat: add publish actions with documentation' (!42) from feature/publish into main
Reviewed-on: #42
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-06-11 08:23:20 +00:00
801c7e2249 feat: add publish actions with documentation
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m5s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 47s
2026-06-11 07:34:01 +00:00
c6c23a8827
Merge pull request 'feat: add playwright actions with documentation' (#41) from feature/playwright-actions into main
Reviewed-on: #41
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-06-10 07:05:40 +00:00
3c3aa7a8ce feat: add playwright actions with documentation
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m39s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m43s
2026-06-08 15:06:22 +00:00
6a167350f0
Merge pull request 'feat: add vacuum-lint action with documentation' (#40) from feature/vacuum-lint into main
Reviewed-on: #40
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-06-08 06:52:28 +00:00
DMI
e3c0fdac23 feat: add vacuum-lint action with documentation
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 34s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 45s
2026-06-03 14:42:18 +00:00
35b0a321bc
Merge pull request 'feat: add rust-build action with documentation' (#39) from feature/rust-build into main
Reviewed-on: #39
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-06-02 05:21:33 +00:00
802aa7d6fe feat: add rust-build action with documentation
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 44s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 51s
2026-06-01 14:30:16 +00:00
909fb37930
Merge pull request 'fix: update cache control argument handling for S3 sync' (#38) from bugfix/CACHE_CONTROL_ARG into main
Reviewed-on: #38
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-06-01 12:44:17 +00:00
03b9e01225
Merge pull request 'feat: decouple hostname from action' (#37) from feature/ITDO-339 into main
Reviewed-on: #37
Reviewed-by: Kraft_Ruben_-_J._Schmalz_GmbH <Ruben.Kraft@schmalz.de>
2026-06-01 12:35:51 +00:00
08123e3f56 fix: update cache control argument handling for S3 sync
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 49s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 26s
2026-06-01 12:35:17 +00:00
e05e71fe12
Merge pull request 'feat: add download-artifact and upload-artifact actions with documentation' (#35) from feature/download-upload-artifact into main
Reviewed-on: #35
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-06-01 11:42:23 +00:00
f13ae2e528
Merge pull request 'feat: add i18n-sync action to fetch translations and create pull requests' (#34) from feature/i18n-sync into main
Reviewed-on: #34
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-06-01 11:40:45 +00:00
452039b6fd feat: decouple hostname from action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 47s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m16s
Refs: ITDO-339
2026-06-01 11:06:31 +02:00
7607756bf0
Merge pull request 'chore: use the workflow standards' (#36) from chore/pipelines into main
Reviewed-on: #36
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-06-01 07:19:45 +00:00
dcb17b88bf chore: use the workflow standards
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 34s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m2s
2026-05-22 09:57:52 +00:00
ed2dcca9bb feat: add download-artifact and upload-artifact actions with documentation
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 56s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m7s
2026-05-22 09:55:02 +00:00
afa2cfc241 feat: add i18n-sync action to fetch translations and create pull requests
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 40s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 39s
2026-05-22 09:27:55 +00:00
0473d4bc1d
Merge pull request 'fix: force public npm registry for pnpm self-installer bootstrap' (#33) from bugfix/pnpm-bootstrap-registry into main
Reviewed-on: #33
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-05-22 08:41:28 +00:00
6dc474f759 fix: force public npm registry for pnpm self-installer bootstrap
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 40s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 39s
pnpm/action-setup bootstraps itself via npm before pnpm is available.
If a repo has a custom registry in .npmrc (e.g. pointing to JFrog or
Nexus), the self-installer tries to fetch pnpm from that registry
without credentials and fails with exit code 1.

Setting NPM_CONFIG_REGISTRY overrides .npmrc for this step only,
ensuring pnpm is always fetched from the public registry. Private
registry auth is configured in subsequent steps once pnpm is ready.
2026-05-22 09:29:42 +02:00
97d17f46e8
Merge pull request 'feat: add per-file cache_rules and fix argument escaping' (#32) from feature/publish-static-contents-cache-rules into main
Reviewed-on: #32
Reviewed-by: Böhringer_Sebastian_-_J._Schmalz_GmbH <Sebastian.Boehringer@schmalz.de>
2026-05-22 05:09:47 +00:00
c690178858 feat: add per-file cache_rules and fix argument escaping
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 29s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m17s
- Add cache_rules input: JSON array of per-file cache overrides, each uploaded individually with its own Cache-Control and Content-Type headers and excluded from the bulk sync
- When versioning is true and cache_rules is empty, auto short-cache index.html (no-cache, max-age=300) if it exists in dist_dir
- Fix EXCLUDE_ARGS and CT_ARG to use bash arrays for correct handling of filenames and content-type values containing spaces
2026-05-22 05:06:42 +00:00
00d818b153
Merge pull request 'feat: add mock-files input to create empty files for Terraform validation' (#31) from feature/terraform-validate-mock-files into main
Reviewed-on: #31
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-05-21 16:07:26 +00:00
bd5e3add23 feat: add mock-files input to create empty files for Terraform validation
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m18s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 2m27s
2026-05-21 13:50:14 +00:00
038b488a6b
Merge pull request 'fix: strip leading slash from forgejo.repository in aikido full scan' (#29) from fix/aikido-full-scan-strip-leading-slash into main
Reviewed-on: #29
Reviewed-by: Marcel Frey <Marcel.Frey@schmalz.de>
2026-05-21 07:55:55 +00:00
e6012e11c0 fix: strip leading slash from forgejo.repository in aikido full scan
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 56s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m43s
2026-05-21 07:50:37 +00:00
9f70cd804d
Merge pull request 'fix: use forgejo.repository for aikido full scan schedule compatibility' (#28) from fix/aikido-full-scan-schedule-repository into main
Reviewed-on: #28
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-05-21 07:40:47 +00:00
44164494c8 fix: use forgejo.repository for aikido full scan schedule compatibility
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 38s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 2m4s
forgejo.event.repository.name is absent on schedule triggers.
forgejo.repository is always set to 'owner/repo' regardless of trigger type.
2026-05-21 07:37:05 +00:00
74aa5eb12f
Merge pull request 'fix: update versioning parameters in README and action.yml for clarity' (#27) from bugfix/publish-static-contents-versioning into main
Reviewed-on: #27
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-19 15:23:03 +00:00
4d2a9815d7 fix: update versioning parameters in README and action.yml for clarity
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 48s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m23s
Co-authored-by: Copilot <copilot@github.com>
2026-05-19 15:07:29 +00:00
4bb087153f
Merge pull request 'fix: install AWS CLI if not present before publishing assets' (#26) from bugfix/aws-sdk into main
Reviewed-on: #26
Reviewed-by: Maier David - J. Schmalz GmbH <david.maier@noreply.schmalz-git.git.onstackit.cloud>
2026-05-11 15:08:36 +00:00
d89b7842e2 fix: install AWS CLI if not present before publishing assets
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 32s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m20s
Co-authored-by: Copilot <copilot@github.com>
2026-05-11 13:52:20 +00:00
98fb3d79dd
Merge pull request 'fix: use actions with node20 support' (#25) from bugfix/node20 into main
Reviewed-on: #25
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-06 14:26:29 +00:00
c87077f8b0 feat: add support for nexus npm registry
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 41s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m35s
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 14:24:05 +00:00
4af880a1f0 fix: use actions with node20 support
node24 is not available in latest stackit runner containers

Co-authored-by: Copilot <copilot@github.com>
2026-05-06 14:23:32 +00:00
5102827397
Merge pull request 'feat: add caching for pnpm store and Terraform providers' (#23) from feature/add-cache into main
Reviewed-on: #23
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-06 08:53:04 +00:00
aa4aa9131a
Merge pull request 'feat: enable manual triggering for Aikido Security Full Scan workflow' (#24) from chore/workflow-dispatch into main
Reviewed-on: #24
2026-05-05 13:55:07 +00:00
afed7e800e
Merge pull request 'feat: add output export for Terraform apply action' (#20) from feature/terraform-apply-outputs into main
Reviewed-on: #20
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-05 13:22:42 +00:00
bad1852f79
Merge pull request 'feat: add maven-build action' (#21) from feature/maven-build into main
Reviewed-on: #21
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-05 13:22:00 +00:00
b666024c65
Merge pull request 'feat: add helm-deploy action' (#22) from feature/helm-deploy into main
Reviewed-on: #22
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-05 13:21:23 +00:00
45794cdf82 feat: enable manual triggering for Aikido Security Full Scan workflow
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 44s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 54s
2026-05-05 13:08:43 +00:00
a8be9bf8dc feat: add maven-build action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 44s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 53s
2026-05-05 13:07:32 +00:00
4030dee789 feat: add caching for pnpm store and Terraform providers
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 44s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 56s
2026-05-05 13:06:35 +00:00
0aa9f4274d feat: add helm-deploy action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m0s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m9s
2026-05-05 12:57:56 +00:00
dd41de5246
Merge pull request 'feat: add inject-content action' (#19) from feature/inject-content into main
Reviewed-on: #19
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-05 12:56:09 +00:00
a0e6adf3db feat: add output export for Terraform apply action
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m2s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 31s
2026-05-05 12:32:00 +00:00
9ab5db7b61 feat: add inject-content action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 40s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 52s
2026-05-05 12:23:18 +00:00
5df90fed35
Merge pull request 'feat: add cache action' (#18) from feature/cache into main
Reviewed-on: #18
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-05 12:12:11 +00:00
1e68ce68c7 feat: add cache action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 54s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m5s
Co-authored-by: Copilot <copilot@github.com>
2026-05-05 06:04:14 +00:00
65b5f623c1
Merge pull request 'fix: downgrade checkout' (#17) from bugfix/checkout into main
Reviewed-on: #17
2026-05-04 14:29:17 +00:00
3097bd89e2 fix: downgrade checkout
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m17s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m31s
...to avoid usage of node24 - not supported from stackit right now
2026-05-04 14:27:04 +00:00
0e2a2306c0
Merge pull request 'fix: downgrade checkout' (#16) from bugfix/checkout into main
Reviewed-on: #16
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-04 13:51:28 +00:00
f6a9668f6e fix: downgrade checkout
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m26s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m43s
...to avoid usage of node24 - not supported from stackit right now
2026-05-04 13:34:39 +00:00
fe4a3e3735
Merge pull request 'XXX' (#15) from bugfix/aikido into main
Reviewed-on: #15
2026-05-04 12:59:16 +00:00
a9ea87cafe fix: reference internal actions as full qualified shared actions
Some checks failed
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m9s
Aikido Security PR Check / Aikido Security Scan (pull_request) Failing after 1m12s
2026-05-04 12:56:45 +00:00
3eaba16b67 XXX
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m42s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 2m0s
2026-05-04 12:45:53 +00:00
70f2da4381
Merge pull request 'feat: add aws-access-key-id and aws-secret-access-key inputs to aws-configure action' (#14) from feature/aws-configure-keys into main
Reviewed-on: #14
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-04 11:16:48 +00:00
7bdca13059 feat: add aws-access-key-id and aws-secret-access-key inputs to aws-configure action
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m6s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 2m32s
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 09:04:49 +00:00
73591365cf
Merge pull request 'feat: add tag-release workflow for manual major release tagging' (#13) from feature/tag-release into main
Reviewed-on: #13
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-04 08:21:02 +00:00
55121b1397
Merge pull request 'fix: update checkout action to use shared-actions version' (#12) from chore/checkout into main
Reviewed-on: #12
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-04 08:12:56 +00:00
c4b0b59780
Merge pull request 'feat: add terraform-apply action' (#11) from feature/terraform-apply into main
Reviewed-on: #11
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-04 08:11:55 +00:00
df3e62e9c4
Merge pull request 'chore: remove unused extensions' (#10) from chore/devcontainer into main
Reviewed-on: #10
Reviewed-by: Markus.Opahle@schmalz.de <Markus.Opahle@schmalz.de>
2026-05-04 08:11:19 +00:00
14bf6ad0da feat: add tag-release workflow for manual major release tagging
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m40s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m57s
2026-05-04 08:06:16 +00:00
7a45b38c62 fix: update checkout action to use shared-actions version
All checks were successful
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m4s
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m58s
2026-05-04 08:04:13 +00:00
f3878d443b feat: add terraform-apply action
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m25s
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 07:52:38 +00:00
c8eaf38f38
Merge pull request 'feat: add Aikido security workflows for full scan and PR check' (#7) from feature/aikido-actions into main
Reviewed-on: #7
2026-05-04 07:15:18 +00:00
5f542e7a12 chore: remove unused extensions
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m17s
2026-05-04 07:13:53 +00:00
2a5f1e2fe7 feat: add Aikido security workflows for full scan and PR check
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 1m20s
Aikido Security PR Check / Aikido Security Scan (pull_request) Successful in 1m48s
2026-05-04 07:10:56 +00:00
34c06ff025
Merge pull request 'fix: move aikido internal actions into the shared actions to make it work' (#9) from bugfix/aikido-actions into main
Reviewed-on: #9
2026-05-04 07:01:49 +00:00
b009c23bb7 fix: move aikido internal actions into the shared actions to make it work
All checks were successful
validate-shared-actions / validate-shared-actions (pull_request) Successful in 56s
2026-05-04 06:59:47 +00:00
52 changed files with 2112 additions and 58 deletions

View file

@ -14,9 +14,7 @@
"customizations": {
"vscode": {
"extensions": [
"amazonwebservices.aws-toolkit-vscode",
"biomejs.biome",
"likec4.likec4-vscode"
"amazonwebservices.aws-toolkit-vscode"
]
}
}

View file

@ -3,6 +3,7 @@ name: Aikido Security Full Scan
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
aikido-full-scan:
@ -15,4 +16,4 @@ jobs:
- name: Run Aikido full scan
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aikido-full-scan@aikido-full-scan-v1
with:
apikey: ${{ secrets.AIKIDO_CLIENT_API_KEY }}
apikey: ${{ secrets.AIKIDO_CLIENT_API_KEY }}

View file

@ -2,8 +2,6 @@ name: Aikido Security PR Check
on:
pull_request:
branches:
- '*'
concurrency:
group: ${{ forgejo.workflow }}-${{ forgejo.ref }}
@ -20,4 +18,4 @@ jobs:
- name: Security scan
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/aikido-pr-scan@aikido-pr-scan-v1
with:
apikey: ${{ secrets.AIKIDO_CLIENT_API_KEY }}
apikey: ${{ secrets.AIKIDO_CLIENT_API_KEY }}

View file

@ -2,7 +2,6 @@ name: validate-shared-actions
on:
pull_request:
types: [opened, reopened, synchronize]
permissions:
contents: read
@ -12,7 +11,8 @@ jobs:
runs-on: stackit-ubuntu-22
steps:
- name: Checkout
uses: actions/checkout@v4
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/checkout@checkout-v1
- name: Validate shared action metadata
uses: docker://data.forgejo.org/forgejo/runner:12
with:

View file

@ -0,0 +1,89 @@
name: tag-release
# Manually create or move a major release tag for a shared action.
# Tag format: <action-name>-v<major> (e.g. checkout-v1, pnpm-build-v2)
#
# If the tag already exists it is force-moved to the selected ref.
on:
workflow_dispatch:
inputs:
action:
description: Action to release
required: true
type: choice
options:
- aikido-full-scan
- aikido-pr-scan
- aws-configure
- cache
- checkout
- download-artifact
- esb-deploy
- helm-deploy
- i18n-sync
- inject-content
- maven-build
- pnpm-build
- playwright-merge
- playwright-run
- publish-npm-package
- publish-rust-crate
- publish-static-contents
- rust-build
- terraform-apply
- terraform-validate
- upload-artifact
- vacuum-lint
major-version:
description: 'Major version number (e.g. 1)'
required: true
type: string
ref:
description: 'Branch, tag, or commit SHA to tag (defaults to the default branch)'
required: false
type: string
default: ''
permissions:
contents: write
jobs:
tag-release:
runs-on: stackit-ubuntu-22
steps:
- name: Checkout
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/checkout@checkout-v1
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
- name: Validate major version
env:
MAJOR: ${{ inputs.major-version }}
run: |
if ! echo "$MAJOR" | grep -qE '^[0-9]+$'; then
echo "::error::major-version must be a positive integer, got: $MAJOR"
exit 1
fi
- name: Create or move major tag
env:
ACTION: ${{ inputs.action }}
MAJOR: ${{ inputs.major-version }}
run: |
set -euo pipefail
TAG="${ACTION}-v${MAJOR}"
COMMIT=$(git rev-parse HEAD)
echo "Tag : $TAG"
echo "Commit : $COMMIT"
git config user.name "forgejo-actions[bot]"
git config user.email "forgejo-actions[bot]@noreply.forgejo.org"
git tag -f "$TAG" "$COMMIT"
git push origin -f "refs/tags/$TAG"
echo "::notice::Tag $TAG now points to $COMMIT"

View file

@ -9,11 +9,24 @@ Shared actions for Forgejo CI/CD pipelines.
| [aikido-full-scan](aikido-full-scan) | Aikido full scan |
| [aikido-pr-scan](aikido-pr-scan) | Aikido PR scan |
| [aws-configure](aws-configure) | Authenticate with AWS via OIDC |
| [cache](cache) | Cache files between workflow runs |
| [checkout](checkout) | Action for checking out a repository |
| [download-artifact](download-artifact) | Download Forgejo Actions artifacts by name or pattern |
| [helm-deploy](helm-deploy) | Deploy a service to Kubernetes via Helm over SSH |
| [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 |
| [maven-build](maven-build) | Action for building and validating Maven projects |
| [pnpm-build](pnpm-build) | Action for building and validating with PNPM |
| [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 |
| [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 |
| [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-validate](terraform-validate) | Validate Terraform configuration files using the official Terraform CLI |
| [upload-artifact](upload-artifact) | Upload files as a Forgejo Actions artifact |
| [vacuum-lint](vacuum-lint) | Validate and lint OpenAPI specifications using Vacuum |
## Security
@ -29,4 +42,4 @@ Reference actions from your project's workflow:
# see each action's README for inputs
```
Each action has its own README with inputs, usage examples, and notes.
Each action has its own README with inputs, usage examples, and notes.

View file

@ -12,9 +12,15 @@ inputs:
runs:
using: composite
steps:
- uses: ./.forgejo/actions/internal-aikido-full-scan
- name: Normalize repository name
id: repo
shell: bash
run: |
repo="${{ forgejo.repository }}"
echo "name=${repo#/}" >> $GITHUB_OUTPUT
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/internal-aikido-full-scan@internal-aikido-full-scan-v1
with:
apikey: ${{ inputs.apikey }}
organization: ${{ forgejo.repository_owner }}
repository-name: ${{ forgejo.event.repository.name }}
repository-name: ${{ steps.repo.outputs.name }}
branch-name: ${{ forgejo.ref_name }}

View file

@ -16,7 +16,7 @@ inputs:
runs:
using: composite
steps:
- uses: ./.forgejo/actions/internal-aikido-pr-scan
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/internal-aikido-pr-scan@internal-aikido-pr-scan-v1
with:
apikey: ${{ inputs.apikey }}
organization: ${{ forgejo.repository_owner }}

View file

@ -8,6 +8,8 @@ Authenticate with AWS via OIDC and export credentials to the environment.
|-------|----------|---------|-------------|
| `role-arn` | Yes | | Full IAM role ARN |
| `region` | No | `eu-central-1` | AWS region |
| `aws-access-key-id` | No | | AWS access key to use. Only required for some authentication types. |
| `aws-secret-access-key` | No | | AWS secret key to use. Only required for some authentication types. |
## Usage

View file

@ -9,6 +9,12 @@ inputs:
description: AWS region
required: false
default: eu-central-1
aws-access-key-id:
description: AWS access key to use. Only required for some authentication types.
required: false
aws-secret-access-key:
description: AWS secret key to use. Only required for some authentication types.
required: false
runs:
using: composite
@ -20,3 +26,5 @@ runs:
with:
role-to-assume: ${{ inputs.role-arn }}
aws-region: ${{ inputs.region }}
aws-access-key-id: ${{ inputs.aws-access-key-id }}
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}

51
cache/README.md vendored Normal file
View file

@ -0,0 +1,51 @@
# cache
Composite wrapper around actions/cache pinned to a specific commit SHA to prevent supply chain attacks via tag or branch hijacking.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `path` | Yes | — | List of files, directories, and wildcard patterns to cache and restore |
| `key` | Yes | — | An explicit key for saving and restoring the cache |
| `restore-keys` | No | `''` | Ordered multiline string of prefix-matched keys used for restoring stale cache |
| `upload-chunk-size` | No | `''` | Chunk size in bytes used to split large files during upload |
| `enableCrossOsArchive` | No | `false` | Allow caches saved on one OS to be restored on another |
| `fail-on-cache-miss` | No | `false` | Fail the workflow if no cache entry is found |
| `lookup-only` | No | `false` | Check if a cache entry exists without downloading it |
## Outputs
| Output | Description |
|--------|-------------|
| `cache-hit` | `true` if an exact match was found for the primary key |
## Usage
```yaml
- name: Cache pnpm store
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: ~/.local/share/pnpm/store
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
```
```yaml
- name: Cache node_modules
id: node-modules-cache
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.node-modules-cache.outputs.cache-hit != 'true'
run: npm ci
```
## Notes
- Pinned to `actions/cache` commit SHA `0057852b` (v4.3.0) to prevent supply chain attacks via tag or branch hijacking.
- Upstream action: [code.forgejo.org/actions/cache](https://code.forgejo.org/actions/cache).

54
cache/action.yml vendored Normal file
View file

@ -0,0 +1,54 @@
name: Schmalz Cache
description: >
Composite wrapper around actions/cache pinned to a specific commit SHA
to prevent supply chain attacks via tag or branch hijacking.
inputs:
path:
description: A list of files, directories, and wildcard patterns to cache and restore.
required: true
key:
description: An explicit key for saving and restoring the cache.
required: true
restore-keys:
description: An ordered multiline string listing prefix-matched keys used for restoring stale cache if no cache hit occurred for key.
required: false
default: ''
upload-chunk-size:
description: The chunk size used to split up large files during upload, in bytes.
required: false
default: ''
enableCrossOsArchive:
description: When enabled, allows Windows runners to save or restore caches that can be used on other platforms.
required: false
default: 'false'
fail-on-cache-miss:
description: Fail the workflow if cache entry is not found.
required: false
default: 'false'
lookup-only:
description: Check if a cache entry exists for the given input(s) without downloading the cache.
required: false
default: 'false'
outputs:
cache-hit:
description: A boolean value to indicate an exact match was found for the primary key.
value: ${{ steps.cache.outputs.cache-hit }}
runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# actions/cache v4.3.0 — https://code.forgejo.org/actions/cache/commits/tag/v4.3.0
- name: Cache
id: cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
with:
path: ${{ inputs.path }}
key: ${{ inputs.key }}
restore-keys: ${{ inputs.restore-keys }}
upload-chunk-size: ${{ inputs.upload-chunk-size }}
enableCrossOsArchive: ${{ inputs.enableCrossOsArchive }}
fail-on-cache-miss: ${{ inputs.fail-on-cache-miss }}
lookup-only: ${{ inputs.lookup-only }}

View file

@ -33,9 +33,9 @@ runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# actions/checkout v6.0.2 — https://code.forgejo.org/actions/checkout/commits/tag/v6.0.2
# actions/checkout v4.3.1 — https://code.forgejo.org/actions/checkout/commits/tag/v4.3.1
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
ref: ${{ inputs.ref }}
repository: ${{ inputs.repository }}

View file

@ -0,0 +1,46 @@
# download-artifact
Download Forgejo Actions artifacts by name or pattern. Thin wrapper around `forgejo/download-artifact` pinned to a specific commit SHA to prevent supply chain attacks.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `name` | No | `""` | Exact artifact name or glob pattern (e.g. `blob-report-*`). If omitted, all artifacts for the run are downloaded. |
| `path` | No | `.` | Local destination directory |
| `merge-multiple` | No | `false` | When true, merge all matched artifacts into a single directory |
## Usage
Download a single artifact by name:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/download-artifact@download-artifact-v1
with:
name: my-artifact
path: dist/
```
Download all artifacts matching a pattern and merge into one directory:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/download-artifact@download-artifact-v1
with:
name: blob-report-*
path: all-blob-reports/
merge-multiple: "true"
```
Download all artifacts for the run:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/download-artifact@download-artifact-v1
with:
path: artifacts/
```
## Notes
- Wraps `forgejo/download-artifact` v4 (node20), compatible with Ubuntu 22 runners.
- The underlying action is pinned to a commit SHA rather than a mutable tag to prevent supply chain attacks.
- When `merge-multiple` is false (default), each matched artifact is extracted into its own subdirectory under `path`.

View file

@ -0,0 +1,30 @@
name: Schmalz Download Artifact
description: >
Download Forgejo Actions artifacts by name or pattern.
Thin wrapper around forgejo/download-artifact, pinned to a specific SHA.
inputs:
name:
description: Exact artifact name or glob pattern (e.g. 'blob-report-*'). If omitted, all artifacts for the run are downloaded.
required: false
default: ""
path:
description: Local destination directory
required: false
default: "."
merge-multiple:
description: When true, merge all matched artifacts into a single directory
required: false
default: "false"
runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# forgejo/download-artifact v4 — https://code.forgejo.org/forgejo/download-artifact/commits/tag/v4
- name: Download artifact
uses: https://code.forgejo.org/forgejo/download-artifact@d8d0a99033603453ad2255e58720b460a0555e1e
with:
name: ${{ inputs.name }}
path: ${{ inputs.path }}
merge-multiple: ${{ inputs.merge-multiple }}

32
esb-deploy/README.md Normal file
View file

@ -0,0 +1,32 @@
# 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

64
esb-deploy/action.yml Normal file
View file

@ -0,0 +1,64 @@
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"

34
helm-deploy/README.md Normal file
View file

@ -0,0 +1,34 @@
# helm-deploy
Deploy a service to Kubernetes via Helm over SSH.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `service-name` | Yes | — | Helm release name |
| `helm-cluster` | Yes | — | Cluster to deploye to (one of `internal_stage` or `internal_prod`) |
| `image-tag` | Yes | — | Docker image tag to deploy |
| `ssh-key` | Yes | — | Private SSH key content |
| `overrides-file` | No | `kubernetes/overrides-pu.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 repository |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/helm-deploy@helm-deploy-v1
with:
service-name: my-service
helm-host: dsp1-stage.schmalzgroup.net
image-tag: ${{ github.sha }}
ssh-key: ${{ secrets.HELM_SSH_KEY }}
```
## Notes
- The SSH key is written to a temporary file with `600` permissions and removed after the job, even on failure.
- The overrides file is copied to the remote host via `scp` before the Helm upgrade.
- `helm upgrade --install` is run with `--atomic` so a failed release is automatically rolled back.
- `StrictHostKeyChecking=no` is used; ensure the host is trusted within your network or add host verification as needed.

105
helm-deploy/action.yml Normal file
View file

@ -0,0 +1,105 @@
name: helm-deploy
description: Deploy a service to Kubernetes via Helm over SSH
inputs:
service-name:
description: Helm release name
required: true
helm-cluster:
description: Name of the target Kubernetes cluster to deploy to
required: true
overrides-file:
description: Local path to Helm values override file
required: false
default: kubernetes/overrides-pu.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:
- name: Setup SSH key
shell: bash
env:
SSH_KEY: ${{ inputs.ssh-key }}
run: |
set -euo pipefail
SSH_KEY_FILE=$(mktemp)
printf '%s\n' "$SSH_KEY" > "$SSH_KEY_FILE"
chmod 600 "$SSH_KEY_FILE"
echo "SSH_KEY_FILE=$SSH_KEY_FILE" >> "$GITHUB_ENV"
- name: Map cluster name to target host
id: map-host
shell: bash
env:
HELM_CLUSTER: ${{ inputs.helm-cluster }}
run: |
case "$HELM_CLUSTER" in
internal_stage) echo "host=dsp1-stage.schmalzgroup.net" ;;
internal_prod) echo "host=dsp1.schmalzgroup.net" ;;
*) echo "Invalid cluster '$HELM_CLUSTER'. Must be 'internal_stage' or 'internal_prod'." && exit 1 ;;
esac >> "$GITHUB_OUTPUT"
- name: Copy overrides file
shell: bash
env:
HELM_HOST: ${{ steps.map-host.outputs.host }}
SERVICE_NAME: ${{ inputs.service-name }}
OVERRIDES_FILE: ${{ inputs.overrides-file }}
run: |
set -euo pipefail
scp -i "$SSH_KEY_FILE" \
-o StrictHostKeyChecking=no \
-o BatchMode=yes \
-o ConnectTimeout=10 \
"$OVERRIDES_FILE" \
"root@${HELM_HOST}:/tmp/${SERVICE_NAME}-overrides.yaml"
- name: Helm deploy
shell: bash
env:
HELM_HOST: ${{ steps.map-host.outputs.host }}
SERVICE_NAME: ${{ inputs.service-name }}
NAMESPACE: ${{ inputs.namespace }}
HELM_REPO: ${{ inputs.helm-repo }}
HELM_CHART: ${{ inputs.helm-chart }}
IMAGE_TAG: ${{ inputs.image-tag }}
run: |
set -euo pipefail
ssh -i "$SSH_KEY_FILE" \
-o StrictHostKeyChecking=no \
-o BatchMode=yes \
-o ConnectTimeout=10 \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=5 \
"root@${HELM_HOST}" \
"helm repo update && \
helm upgrade --install --create-namespace \
-n '${NAMESPACE}' \
'${SERVICE_NAME}' \
'${HELM_REPO}/${HELM_CHART}' \
-f '/tmp/${SERVICE_NAME}-overrides.yaml' \
--set image.tag='${IMAGE_TAG}' \
--atomic --debug"
- name: Cleanup SSH key
if: always()
shell: bash
run: rm -f "$SSH_KEY_FILE"

46
i18n-sync/README.md Normal file
View file

@ -0,0 +1,46 @@
# i18n-sync
Fetches the latest translations from i18n.schmalz.com, commits them to a `chore/i18n` branch, and opens a pull request against the destination branch.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `i18n-application` | Yes | — | Application key in i18n.schmalz.com (e.g. `calculator`) |
| `locales-folder` | No | `locales` | Path to the locales directory relative to the repository root |
| `format-folder` | No | `""` | Directory containing the `package.json` whose format script should be run after downloading translations. Leave empty to skip formatting. |
| `format-script` | No | `format` | pnpm script name to run for formatting (e.g. `format`, `format:fix`) |
| `jfrog-token` | No | `""` | JFrog npm auth token (required when `format-folder` is set) |
| `forgejo-token` | Yes | — | Forgejo token with `contents:write` and `pull-requests:write` access |
| `destination-branch` | No | `dev` | Target branch for the pull request |
## Usage
Minimal — no formatting:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/i18n-sync@i18n-sync-v1
with:
i18n-application: my-app
forgejo-token: ${{ secrets.FORGEJO_I18N_UPDATE_TOKEN }}
```
With formatting after download:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/i18n-sync@i18n-sync-v1
with:
i18n-application: my-app
locales-folder: frontend/locales
format-folder: frontend
jfrog-token: ${{ secrets.JFROG_TOKEN }}
forgejo-token: ${{ secrets.FORGEJO_I18N_UPDATE_TOKEN }}
```
## Notes
- The action fails fast if the `chore/i18n` branch already exists, indicating a previous update is still pending review.
- If no translation changes are detected after downloading, the action exits cleanly without creating a branch or PR.
- Deleted languages (files removed from i18n.schmalz.com) are also staged for removal via `git add --all`.
- `jq` is installed automatically if not present on the runner.
- The `forgejo-token` must belong to a user or bot with `contents:write` and `pull-requests:write` permissions on the repository.

131
i18n-sync/action.yml Normal file
View file

@ -0,0 +1,131 @@
name: i18n Sync
description: >
Fetches the latest translations from i18n.schmalz.com, commits them to a
chore/i18n branch, and opens a pull request against the destination branch.
inputs:
i18n-application:
description: Application key in i18n.schmalz.com (e.g. "calculator")
required: true
locales-folder:
description: Path to the locales directory relative to the repository root
required: false
default: locales
format-folder:
description: >
Directory containing the package.json whose format script should be run
after downloading translations. Leave empty to skip formatting.
required: false
default: ""
format-script:
description: pnpm script name to run for formatting (e.g. "format", "format:fix")
required: false
default: format
jfrog-token:
description: JFrog npm auth token (required when format-folder is set)
required: false
default: ""
forgejo-token:
description: Forgejo token with contents:write and pull-requests:write access
required: true
destination-branch:
description: Target branch for the pull request
required: false
default: dev
runs:
using: composite
steps:
- 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: Configure git
shell: bash
env:
FORGEJO_TOKEN: ${{ inputs.forgejo-token }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
ORIGIN_URL=$(git remote get-url origin)
CLEAN_URL=$(echo "$ORIGIN_URL" | sed 's|https://[^@]*@|https://|')
git remote set-url origin "$(echo "$CLEAN_URL" | sed "s|https://|https://github-actions[bot]:${FORGEJO_TOKEN}@|")"
- name: Check if i18n branch already exists
shell: bash
run: |
set -euo pipefail
if git ls-remote --exit-code --heads origin "refs/heads/chore/i18n" >/dev/null 2>&1; then
echo "Branch chore/i18n already exists. A previous i18n update is still pending."
exit 1
fi
- name: Download translations
shell: bash
env:
I18N_APPLICATION: ${{ inputs.i18n-application }}
LOCALES_FOLDER: ${{ inputs.locales-folder }}
run: |
set -euo pipefail
git checkout -b chore/i18n
echo "Fetching available languages for application: $I18N_APPLICATION"
languages_json=$(curl --fail-with-body -sSL "https://i18n.schmalz.com/api/languages?projectKey=$I18N_APPLICATION")
echo "$languages_json" | jq -r '.[]' | while read -r lang; do
echo "Downloading language: $lang"
curl --fail-with-body -sSL -o "${LOCALES_FOLDER}/${lang}.json" \
"https://i18n.schmalz.com/api/${I18N_APPLICATION}/${lang}.json"
done
- name: Format translations
if: ${{ inputs.format-folder != '' }}
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/pnpm-build@pnpm-build-v1
with:
working-directory: ${{ inputs.format-folder }}
jfrog-token: ${{ inputs.jfrog-token }}
run-scripts: ${{ inputs.format-script }}
check-dedupe: "false"
- name: Commit and push translations
id: commit
shell: bash
env:
LOCALES_FOLDER: ${{ inputs.locales-folder }}
run: |
set -euo pipefail
git add --all "${LOCALES_FOLDER}/"
if git diff-index --cached --quiet HEAD; then
echo "No translation changes detected. Nothing to do."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git commit -m "chore: update translations via i18n"
git push origin chore/i18n
echo "has_changes=true" >> "$GITHUB_OUTPUT"
- name: Create pull request
if: ${{ steps.commit.outputs.has_changes == 'true' }}
shell: bash
env:
TOKEN: ${{ inputs.forgejo-token }}
DESTINATION_BRANCH: ${{ inputs.destination-branch }}
run: |
set -euo pipefail
curl --fail-with-body -s -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/pulls" \
-d "$(jq -n \
--arg title "chore: update translations via i18n" \
--arg head "chore/i18n" \
--arg base "$DESTINATION_BRANCH" \
--arg body "Automated translation update from i18n.schmalz.com" \
'{title: $title, head: $head, base: $base, body: $body, delete_branch_after_merge: true}'
)"

40
inject-content/README.md Normal file
View file

@ -0,0 +1,40 @@
# inject-content
Inject content into a file by appending or overwriting. Useful for writing secrets, configuration snippets, or any multi-line content into an existing file at runtime.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `content` | Yes | | Content to write into the target file |
| `target-file` | Yes | | Path to the target file |
| `mode` | No | `append` | Write mode: `append` adds to the end of the file; `overwrite` replaces its contents |
## Usage
Append secrets to a Java `.properties` file:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/inject-content@inject-content-v1
with:
content: ${{ secrets.APP_PROPERTIES }}
target-file: src/main/resources/application.properties
```
Overwrite a `.env` file:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/inject-content@inject-content-v1
with:
content: ${{ secrets.DOTENV_CONTENT }}
target-file: .env
mode: overwrite
```
## Notes
- Content is passed via an environment variable to avoid shell injection vulnerabilities.
- In `append` mode a blank line is inserted between existing content and the new content. If the target file is empty or does not exist, no leading blank line is added.
- In `overwrite` mode the file is replaced entirely; if the file does not exist it is created.
- Both modes ensure the written content ends with a trailing newline.
- Parent directories of `target-file` are created automatically if they do not exist.

67
inject-content/action.yml Normal file
View file

@ -0,0 +1,67 @@
name: inject-content
description: Inject content into a file by appending or overwriting
inputs:
content:
description: Content to write into the target file
required: true
target-file:
description: Path to the target file
required: true
mode:
description: "Write mode: 'append' adds to the end of the file; 'overwrite' replaces its contents"
required: false
default: append
outputs:
target-file:
description: Path of the file that was written
value: ${{ steps.inject.outputs.target-file }}
runs:
using: composite
steps:
- name: Validate target-file input
shell: bash
env:
TARGET_FILE: ${{ inputs.target-file }}
run: |
if [[ -z "$TARGET_FILE" ]]; then
echo "::error::target-file must not be empty"
exit 1
fi
- name: Validate mode input
shell: bash
env:
MODE: ${{ inputs.mode }}
run: |
if [[ "$MODE" != "append" && "$MODE" != "overwrite" ]]; then
echo "::error::Invalid mode '$MODE'. Must be 'append' or 'overwrite'."
exit 1
fi
- name: Inject content into file
id: inject
shell: bash
env:
CONTENT: ${{ inputs.content }}
TARGET_FILE: ${{ inputs.target-file }}
MODE: ${{ inputs.mode }}
run: |
set -euo pipefail
mkdir -p "$(dirname "$TARGET_FILE")"
if [[ "$MODE" == "overwrite" ]]; then
printf '%s\n' "$CONTENT" > "$TARGET_FILE"
else
if [[ -s "$TARGET_FILE" ]]; then
printf '\n%s\n' "$CONTENT" >> "$TARGET_FILE"
else
printf '%s\n' "$CONTENT" >> "$TARGET_FILE"
fi
fi
echo "Content injected into $TARGET_FILE (mode: $MODE)"
echo "target-file=$TARGET_FILE" >> "$GITHUB_OUTPUT"

View file

@ -5,11 +5,8 @@ inputs:
apikey:
description: Aikido CI API key
required: true
organization:
description: Organization or owner name
required: true
repository-name:
description: Repository name
description: Full repository name (owner/repo)
required: true
branch-name:
description: Branch to scan against
@ -23,9 +20,8 @@ runs:
- --apikey
- ${{ inputs.apikey }}
- --repositoryname
- ${{ inputs.organization }}/${{ inputs.repository-name }}
- ${{ inputs.repository-name }}
- --branchname
- ${{ inputs.branch-name }}
- --force-create-repository-for-branch
- --include-dev-deps

58
maven-build/README.md Normal file
View file

@ -0,0 +1,58 @@
# maven-build
Action for building and validating Maven projects.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `java-version` | No | `25` | Java version to set up for the build |
| `maven-version` | No | `3.9.15` | Maven version to set up for the build |
| `distribution` | No | `temurin` | JDK distribution to use |
| `phase` | No | `verify` | Build phase to execute: `verify` runs code-quality checks; `deploy` builds and pushes a Docker image |
| `verify-goals` | No | `spotless:check checkstyle:check test` | Space-separated Maven goals to run during the verify phase |
| `maven-profile` | No | `test` | Maven profile to activate during deploy |
| `service-dir` | No | `.` | Working directory for the Maven build |
| `maven-settings` | **Yes** | — | Secret containing the `settings.xml` content used for repository authentication |
| `extra-args` | No | `""` | Additional Maven arguments appended to the build command |
## Outputs
| Output | Description |
|--------|-------------|
| `image-tag` | The Docker image tag used during the deploy phase |
## Usage
### Verify (code quality + tests)
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/maven-build@maven-build-v1
with:
maven-settings: ${{ secrets.MAVEN_SETTINGS }}
```
### Deploy (build and push Docker image)
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/maven-build@maven-build-v1
with:
phase: deploy
maven-profile: prod
maven-settings: ${{ secrets.MAVEN_SETTINGS }}
```
### Multi-module project
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/maven-build@maven-build-v1
with:
service-dir: my-service
maven-settings: ${{ secrets.MAVEN_SETTINGS }}
```
## Notes
- The `maven-settings` input is written to a temporary file (`/tmp/maven-settings.xml`) and removed after the build, even on failure.
- During the `deploy` phase, the image tag is generated as `<FORGEJO_SHA>-<unix-timestamp>` and exposed via the `image-tag` output.
- Third-party actions used internally are pinned to exact commit SHAs to prevent supply chain attacks.

125
maven-build/action.yml Normal file
View file

@ -0,0 +1,125 @@
name: maven-build
description: Action for building and validating Maven projects
inputs:
java-version:
required: false
default: '25'
description: 'Java version to set up for the build'
maven-version:
required: false
default: '3.9.15'
description: 'Maven version to set up for the build'
distribution:
required: false
default: 'temurin'
description: 'JDK distribution to use'
phase:
required: false
default: 'verify'
description: 'Build phase to execute: "verify" runs code-quality checks; "deploy" builds and pushes a Docker image'
verify-goals:
required: false
default: 'spotless:check checkstyle:check test'
description: 'Space-separated Maven goals to run during the verify phase'
maven-profile:
required: false
default: 'test'
description: 'Maven profile to activate during deploy'
service-dir:
required: false
default: '.'
description: 'Working directory for the Maven build'
maven-settings:
required: true
description: 'Secret containing the settings.xml content used for repository authentication'
extra-args:
required: false
default: ''
description: 'Additional Maven arguments appended to the build command'
outputs:
image-tag:
description: 'The Docker image tag used during the deploy phase'
value: ${{ steps.deploy.outputs.image-tag }}
runs:
using: composite
steps:
- name: Validate phase
shell: bash
env:
BUILD_PHASE: ${{ inputs.phase }}
run: |
case "$BUILD_PHASE" in
verify|deploy) ;;
*) echo "Invalid phase '$BUILD_PHASE'. Must be 'verify' or 'deploy'." && exit 1 ;;
esac
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# actions/setup-java v4.8.0 — https://github.com/actions/setup-java/tree/v4.8.0
- name: Setup Java
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9
with:
java-version: ${{ inputs.java-version }}
distribution: ${{ inputs.distribution }}
- name: Setup Maven
shell: bash
env:
MAVEN_VERSION: ${{ inputs.maven-version }}
run: |
curl -fsSL "https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
| tar -xzf - -C /opt
echo "/opt/apache-maven-${MAVEN_VERSION}/bin" >> "$GITHUB_PATH"
echo "Maven ${MAVEN_VERSION} installed successfully"
- name: Cache Maven local repository
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ inputs.java-version }}-${{ hashFiles(format('{0}/**/pom.xml', inputs.service-dir)) }}
restore-keys: ${{ runner.os }}-maven-${{ inputs.java-version }}-
- name: Write Maven settings
shell: bash
env:
MAVEN_SETTINGS: ${{ inputs.maven-settings }}
run: printf '%s\n' "$MAVEN_SETTINGS" > /tmp/maven-settings.xml
- name: Verify
if: ${{ inputs.phase == 'verify' }}
shell: bash
working-directory: ${{ inputs.service-dir }}
env:
VERIFY_GOALS: ${{ inputs.verify-goals }}
EXTRA_ARGS: ${{ inputs.extra-args }}
MAVEN_PROFILE: ${{ inputs.maven-profile }}
run: |
mvn --batch-mode $VERIFY_GOALS \
-s /tmp/maven-settings.xml \
-P "$MAVEN_PROFILE" \
$EXTRA_ARGS
- name: Deploy
id: deploy
if: ${{ inputs.phase == 'deploy' }}
shell: bash
working-directory: ${{ inputs.service-dir }}
env:
MAVEN_PROFILE: ${{ inputs.maven-profile }}
EXTRA_ARGS: ${{ inputs.extra-args }}
run: |
IMAGE_TAG="${FORGEJO_SHA}-$(date +%s)"
mvn --batch-mode clean package jib:build \
-DsendCredentialsOverHttp=true \
"-Djib.to.tags=$IMAGE_TAG" \
-P "$MAVEN_PROFILE" \
-s /tmp/maven-settings.xml \
$EXTRA_ARGS
echo "image-tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
- name: Remove Maven settings
if: always()
shell: bash
run: rm -f /tmp/maven-settings.xml

View file

@ -0,0 +1,39 @@
# 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`.

104
playwright-merge/action.yml Normal file
View file

@ -0,0 +1,104 @@
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"

33
playwright-run/README.md Normal file
View file

@ -0,0 +1,33 @@
# 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. |
| `artifact-retention-days` | No | `3` | Number of days to retain the blob report artifact |
## Usage
```yaml
- 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 }}
shard-index: ${{ matrix.shard-index }}
shard-total: 5
```
## 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.

87
playwright-run/action.yml Normal file
View file

@ -0,0 +1,87 @@
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"
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 }}
run: |
SHARD_ARG=""
if [ "${SHARD_TOTAL}" != "1" ]; then
SHARD_ARG="--shard=${SHARD_INDEX}/${SHARD_TOTAL}"
fi
pnpm --prefix="${WORKING_DIR}" exec playwright test ${SHARD_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

@ -19,6 +19,10 @@ inputs:
description: JFrog npm auth token
required: false
default: ""
nexus-token:
description: Nexus npm auth token
required: false
default: ""
run-scripts:
description: Comma-separated list of pnpm run scripts
required: false
@ -36,25 +40,54 @@ runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# actions/setup-node v6.4.0 — https://code.forgejo.org/actions/setup-node/commits/tag/v6.4.0
# 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@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
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 v6.0.3 — https://code.forgejo.org/pnpm/action-setup/commits/tag/v6.0.3
# 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@903f9c1a6ebcba6cf41d87230be49611ac97822e
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1
env:
# Override any registry configured in .npmrc (e.g. JFrog or Nexus).
# pnpm/action-setup bootstraps itself via npm before pnpm is available,
# so it must reach the public npm registry. Auth for private registries
# is configured in a later step, after pnpm is installed.
NPM_CONFIG_REGISTRY: https://registry.npmjs.org
with:
version: ${{ inputs.pnpm-version }}
- name: Configure pnpm registry authentication
- name: Get pnpm store directory
id: pnpm-store
shell: bash
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Cache pnpm store
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-pnpm-${{ inputs.pnpm-version }}-${{ hashFiles(format('{0}/pnpm-lock.yaml', inputs.working-directory)) }}
restore-keys: ${{ runner.os }}-pnpm-${{ inputs.pnpm-version }}-
- name: Configure JFrog registry authentication
if: ${{ inputs.jfrog-token != '' }}
shell: bash
env:
JFROG_TOKEN: ${{ inputs.jfrog-token }}
run: pnpm set //schmalz.jfrog.io/artifactory/api/npm/default-npm/:_authToken "$JFROG_TOKEN"
run: |
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"
- name: Configure Nexus registry authentication
if: ${{ inputs.nexus-token != '' }}
shell: bash
env:
NEXUS_TOKEN: ${{ inputs.nexus-token }}
run: |
pnpm set registry https://nexus.schmalzgroup.com/repository/npm-all/
pnpm set //nexus.schmalzgroup.com/repository/npm-all/:_authToken "$NEXUS_TOKEN"
- name: Build
shell: bash

View file

@ -0,0 +1,28 @@
# 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

@ -0,0 +1,64 @@
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,29 @@
# 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

@ -0,0 +1,64 @@
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

@ -9,10 +9,14 @@ Syncs frontend assets to S3 and invalidates a CloudFront distribution. Optionall
| `dist_dir` | No | `frontend/dist` | Path to the frontend dist directory |
| `s3_bucket_name` | Yes | — | Name of the S3 bucket to sync assets to |
| `cloudfront_distribution_ids` | No | `''` | Space-separated list of CloudFront distribution IDs to invalidate |
| `versioned_static_prefix` | No | `''` | S3 prefix under which versioned builds are stored (e.g. `_static`). When set, old versions older than 7 days are deleted, keeping at least the 2 newest |
| `versioning` | No | `false` | When `true`, enables versioned builds. All assets get `Cache-Control: public, max-age=31536000, immutable`. Old versions older than 7 days are deleted, keeping at least the 2 newest |
| `versioning_prefix` | No | `''` | S3 prefix under which versioned builds are stored (e.g. `_static``_static/1234567890/`). Requires `versioning: true` |
| `cache_rules` | No | `[]` | JSON array of per-file cache overrides. Each matched file is uploaded individually with the given headers and excluded from the bulk sync. When `versioning` is `true` and `cache_rules` is empty and `index.html` exists, defaults to short-caching `index.html` |
## Usage
Plain sync:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-static-contents@publish-static-contents-v1
with:
@ -25,15 +29,41 @@ With versioned static assets:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-static-contents@publish-static-contents-v1
with:
dist_dir: frontend/dist
s3_bucket_name: my-bucket
cloudfront_distribution_ids: ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }}
versioned_static_prefix: _static
versioning: true
```
With versioned static assets under a prefix:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-static-contents@publish-static-contents-v1
with:
s3_bucket_name: my-bucket
cloudfront_distribution_ids: ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }}
versioning: true
versioning_prefix: _static
```
With custom per-file cache rules (e.g. for a PWA):
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/publish-static-contents@publish-static-contents-v1
with:
s3_bucket_name: my-bucket
cloudfront_distribution_ids: ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }}
versioning: true
cache_rules: |
[
{"file": "index.html", "cache_control": "no-cache, no-store, must-revalidate", "content_type": "text/html"},
{"file": "sw.js", "cache_control": "no-cache, no-store, must-revalidate", "content_type": "application/javascript"},
{"file": "manifest.webmanifest", "cache_control": "no-cache, max-age=300", "content_type": "application/manifest+json"}
]
```
## Notes
- When `versioned_static_prefix` is set, all assets are synced with `Cache-Control: public, max-age=31536000, immutable`.
- `index.html` is always synced separately with `Cache-Control: no-cache, max-age=300` so browsers pick up new deployments quickly.
- When `versioning` is `true` and `cache_rules` is empty, `index.html` is automatically short-cached (`no-cache, max-age=300`) if it exists in the dist directory.
- `cache_rules` works independently of `versioning` and can be used for plain syncs too.
- Old versioned builds are pruned: any version folder older than 7 days is deleted, with at least the 2 newest versions always retained.
- CloudFront invalidation is skipped when `cloudfront_distribution_ids` is empty.

View file

@ -13,49 +13,110 @@ inputs:
description: Space-separated list of CloudFront distribution IDs to invalidate
required: false
default: ''
versioned_static_prefix:
description: S3 prefix under which versioned builds are stored (e.g. _static). When set, old versions older than 7 days are deleted, keeping at least the 2 newest.
versioning:
description: >
When set to true, enables versioned builds: assets are synced with
"public, max-age=31536000, immutable" and old versions older than 7 days
are deleted, keeping at least the 2 newest.
required: false
default: 'false'
versioning_prefix:
description: >
S3 prefix under which versioned builds are stored
(e.g. "_static" → _static/1234567890/). When omitted, versions are
stored at the bucket root. Requires versioning to be true.
required: false
default: ''
cache_rules:
description: >
JSON array of per-file cache overrides. Each matched file is uploaded
individually with the given headers and excluded from the bulk sync.
content_type is optional. Applied independently of versioning.
When versioning is enabled and cache_rules is empty, defaults to
short-caching index.html — the standard SPA behaviour.
Example:
cache_rules: |
[
{"file": "index.html", "cache_control": "no-cache, no-store, must-revalidate", "content_type": "text/html"},
{"file": "sw.js", "cache_control": "no-cache, no-store, must-revalidate", "content_type": "application/javascript"},
{"file": "manifest.webmanifest", "cache_control": "no-cache, max-age=300", "content_type": "application/manifest+json"}
]
required: false
default: '[]'
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: Publish frontend assets
shell: bash
env:
INPUT_DIST_DIR: ${{ inputs.dist_dir }}
INPUT_S3_BUCKET_NAME: ${{ inputs.s3_bucket_name }}
INPUT_VERSIONED_STATIC_PREFIX: ${{ inputs.versioned_static_prefix }}
INPUT_VERSIONING: ${{ inputs.versioning }}
INPUT_VERSIONING_PREFIX: ${{ inputs.versioning_prefix }}
INPUT_CACHE_RULES: ${{ inputs.cache_rules }}
run: |
CACHE_CONTROL_ARG=""
if [[ -n "${INPUT_VERSIONED_STATIC_PREFIX}" ]]; then
CACHE_CONTROL_ARG="--cache-control 'public, max-age=31536000, immutable'"
# When versioning is active and no cache_rules provided, short-cache index.html by default
EFFECTIVE_CACHE_RULES="${INPUT_CACHE_RULES}"
if [[ "${INPUT_VERSIONING}" == "true" && "${EFFECTIVE_CACHE_RULES}" == "[]" && -f "${INPUT_DIST_DIR}/index.html" ]]; then
EFFECTIVE_CACHE_RULES='[{"file": "index.html", "cache_control": "no-cache, max-age=300", "content_type": "text/html"}]'
fi
EXCLUDE_INDEX_ARG=""
if [[ -n "${INPUT_VERSIONED_STATIC_PREFIX}" && -f "${INPUT_DIST_DIR}/index.html" ]]; then
EXCLUDE_INDEX_ARG="--exclude index.html"
fi
aws s3 sync "${INPUT_DIST_DIR}" "s3://${INPUT_S3_BUCKET_NAME}" $CACHE_CONTROL_ARG $EXCLUDE_INDEX_ARG
- name: Publish index.html without immutable cache
if: ${{ inputs.versioned_static_prefix != '' }}
shell: bash
run: |
if [[ -f "${INPUT_DIST_DIR}/index.html" ]]; then
aws s3 cp "${INPUT_DIST_DIR}/index.html" "s3://${INPUT_S3_BUCKET_NAME}/index.html" \
--cache-control "no-cache, max-age=300" \
--metadata-directive REPLACE
# Upload each file from cache_rules with its own headers,
# and collect --exclude args so the bulk sync skips them
EXCLUDE_ARGS=()
if [[ "${EFFECTIVE_CACHE_RULES}" != "[]" ]]; then
while IFS= read -r rule; do
file=$(echo "$rule" | jq -r '.file')
cache_control=$(echo "$rule" | jq -r '.cache_control')
content_type=$(echo "$rule" | jq -r '.content_type // empty')
EXCLUDE_ARGS+=("--exclude" "$file")
if [[ -f "${INPUT_DIST_DIR}/${file}" ]]; then
CT_ARG=()
if [[ -n "$content_type" ]]; then
CT_ARG=("--content-type" "$content_type")
fi
aws s3 cp "${INPUT_DIST_DIR}/${file}" "s3://${INPUT_S3_BUCKET_NAME}/${file}" \
--cache-control "$cache_control" \
--metadata-directive REPLACE \
"${CT_ARG[@]}"
fi
done < <(echo "$EFFECTIVE_CACHE_RULES" | jq -c '.[]')
fi
# Bulk sync remaining files; versioned builds get immutable cache headers
CACHE_CONTROL_ARG=()
if [[ "${INPUT_VERSIONING}" == "true" ]]; then
CACHE_CONTROL_ARG=("--cache-control" "public, max-age=31536000, immutable")
fi
aws s3 sync "${INPUT_DIST_DIR}" "s3://${INPUT_S3_BUCKET_NAME}" "${CACHE_CONTROL_ARG[@]}" "${EXCLUDE_ARGS[@]}"
- name: Clean up old versioned static builds
if: ${{ inputs.versioned_static_prefix != '' }}
if: ${{ inputs.versioning == 'true' }}
shell: bash
env:
INPUT_S3_BUCKET_NAME: ${{ inputs.s3_bucket_name }}
INPUT_VERSIONED_STATIC_PREFIX: ${{ inputs.versioned_static_prefix }}
INPUT_VERSIONING_PREFIX: ${{ inputs.versioning_prefix }}
run: |
aws s3 ls "s3://$INPUT_S3_BUCKET_NAME/$INPUT_VERSIONED_STATIC_PREFIX/" \
S3_PATH="s3://$INPUT_S3_BUCKET_NAME"
if [[ -n "${INPUT_VERSIONING_PREFIX}" ]]; then
S3_PATH="s3://$INPUT_S3_BUCKET_NAME/$INPUT_VERSIONING_PREFIX"
fi
aws s3 ls "${S3_PATH}/" \
| grep -oP '(?<=PRE )[0-9]+' \
| sort --stable --reverse \
| tail -n +3 \
@ -65,7 +126,7 @@ runs:
# delete if older than 7 days
if [ $diff -gt 604800000 ]; then
echo "Deleting $version"
aws s3 rm --recursive "s3://$INPUT_S3_BUCKET_NAME/$INPUT_VERSIONED_STATIC_PREFIX/$version"
aws s3 rm --recursive "${S3_PATH}/$version"
fi
done

42
rust-build/README.md Normal file
View file

@ -0,0 +1,42 @@
# rust-build
Set up Rust toolchain, configure Cargo registry, cache dependencies, run optional checks, and build via the project's `build.sh` script.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `working-directory` | No | `.` | Directory containing `Cargo.toml` and `build.sh` |
| `rust-version` | No | `1.95.0` | Rust toolchain version |
| `cross-target` | No | `x86_64-unknown-linux-musl` | Cross-compilation target triple |
| `build-mode` | No | `release` | Build mode — `release` or `debug` |
| `run-checks` | No | `""` | Comma-separated checks to run before building — `fmt`, `clippy`, `test` |
| `jfrog-token` | No | `""` | JFrog token for the Artifactory Cargo registry |
## Usage
### PR check (checks + debug build)
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/rust-build@rust-build-v1
with:
working-directory: backend-rs
build-mode: debug
run-checks: fmt,clippy,test
jfrog-token: ${{ secrets.JFROG_TOKEN }}
```
### Release build
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/rust-build@rust-build-v1
with:
working-directory: backend-rs
jfrog-token: ${{ secrets.JFROG_TOKEN }}
```
## Notes
- Requires a `build.sh` in `working-directory` that accepts `--target <triple>` and optionally `--release`. The script is responsible for running `cargo build` and copying binaries to `target/deploy/`.
- Configures the Artifactory Cargo registry only if `jfrog-token` is provided.
- Third-party actions used internally are pinned to exact commit SHAs to prevent supply chain attacks.

112
rust-build/action.yml Normal file
View file

@ -0,0 +1,112 @@
name: rust-build
description: >
Set up Rust toolchain, configure Cargo registry, cache dependencies,
run optional checks, and build via the project's build.sh script.
inputs:
working-directory:
description: Directory containing Cargo.toml and build.sh
required: false
default: "."
rust-version:
description: Rust toolchain version (passed to dtolnay/rust-toolchain)
required: false
default: "1.95.0"
cross-target:
description: Cross-compilation target triple
required: false
default: x86_64-unknown-linux-musl
build-mode:
description: Build mode — 'release' or 'debug'
required: false
default: release
run-checks:
description: Comma-separated checks to run before building — 'fmt', 'clippy', 'test'
required: false
default: ""
jfrog-token:
description: JFrog token for the Artifactory Cargo registry
required: false
default: ""
runs:
using: composite
steps:
- name: Install musl tools
shell: bash
run: |
if ! command -v musl-gcc &>/dev/null; then
sudo apt-get update -qq && sudo apt-get install -y -qq musl-tools
fi
# 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
id: rust-toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
with:
toolchain: ${{ inputs.rust-version }}
targets: ${{ inputs.cross-target }}
components: rustfmt,clippy
- name: Configure Cargo registry (JFrog Artifactory)
if: ${{ inputs.jfrog-token != '' }}
shell: bash
env:
JFROG_TOKEN: ${{ inputs.jfrog-token }}
run: |
mkdir -p "${CARGO_HOME}"
cat >> "${CARGO_HOME}/config.toml" <<'EOF'
[registries.artifactory]
index = "sparse+https://schmalz.jfrog.io/artifactory/api/cargo/schmalz-cargo-local/index/"
[registry]
global-credential-providers = ["cargo:token"]
EOF
cat >> "${CARGO_HOME}/credentials.toml" <<EOF
[registries.artifactory]
token = "Bearer ${JFROG_TOKEN}"
EOF
- name: Cache cargo registry
uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/cache@cache-v1
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-${{ inputs.cross-target }}-${{ hashFiles(format('{0}/Cargo.lock', inputs.working-directory)) }}
restore-keys: |
${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-${{ inputs.cross-target }}-
${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-
- name: Run checks
if: ${{ inputs.run-checks != '' }}
shell: bash
env:
WORKING_DIR: ${{ inputs.working-directory }}
CROSS_TARGET: ${{ inputs.cross-target }}
RUN_CHECKS: ${{ inputs.run-checks }}
run: |
IFS=',' read -ra CHECKS <<< "${RUN_CHECKS}"
for check in "${CHECKS[@]}"; do
case "${check}" in
fmt) cargo fmt --manifest-path="${WORKING_DIR}/Cargo.toml" --check ;;
clippy) cargo clippy --manifest-path="${WORKING_DIR}/Cargo.toml" --target="${CROSS_TARGET}" -- -D warnings ;;
test) cargo test --manifest-path="${WORKING_DIR}/Cargo.toml" ;;
*) echo "Unknown check: ${check}"; exit 1 ;;
esac
done
- name: Build
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
CROSS_TARGET: ${{ inputs.cross-target }}
BUILD_MODE: ${{ inputs.build-mode }}
run: |
BUILD_ARGS="--target ${CROSS_TARGET}"
if [ "${BUILD_MODE}" = "release" ]; then
BUILD_ARGS="${BUILD_ARGS} --release"
fi
./build.sh ${BUILD_ARGS}

37
terraform-apply/README.md Normal file
View file

@ -0,0 +1,37 @@
# terraform-apply
Apply 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
Non-sensitive Terraform outputs are automatically exported after apply. They are accessible on the calling step via `steps.<id>.outputs.<terraform-output-name>`. Complex types (lists, maps) are JSON-encoded. Outputs marked as `sensitive = true` in Terraform are excluded.
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/terraform-apply@terraform-apply-v1
id: tf-apply
with:
workspace: stage
var-file: stage.tfvars
jfrog-token: ${{ secrets.JFROG_TOKEN }}
- run: echo ${{ steps.tf-apply.outputs.s3_bucket_name }}
```
## Notes
- Runs `terraform init`, selects the workspace (if provided), and applies with `-auto-approve`.
- Sets `TF_TOKEN_schmalz_jfrog_io` on both `init` and `apply` steps if `jfrog-token` is provided.
- If `var-file` is provided, it is passed as `-var-file` to the apply command.
- Non-sensitive Terraform outputs are written to `$GITHUB_OUTPUT` after apply — no separate `terraform output` step needed. Sensitive outputs are excluded to prevent secret leakage.

View file

@ -0,0 +1,90 @@
name: Terraform Apply
description: >
Init and apply 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 }}
- 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"
- 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 }}-
- 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
- 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"
- name: Terraform Apply
shell: bash
env:
TF_TOKEN_schmalz_jfrog_io: ${{ inputs.jfrog-token }}
TF_DIR: ${{ inputs.terraform-dir }}
VAR_FILE: ${{ inputs.var-file }}
run: |
ARGS="-auto-approve -no-color"
if [ -n "$VAR_FILE" ]; then
ARGS="$ARGS -var-file=$VAR_FILE"
fi
terraform -chdir="$TF_DIR" apply $ARGS
- name: Export Terraform Outputs
shell: bash
env:
TF_DIR: ${{ inputs.terraform-dir }}
run: |
terraform -chdir="$TF_DIR" output -json | jq -r '
to_entries[]
| select(.value.sensitive != true)
| .key as $k
| (.value.value | if type == "string" then . else tojson end) as $v
| "\($k)<<__TF_OUT__\n\($v)\n__TF_OUT__"
' >> "$GITHUB_OUTPUT"

View file

@ -10,6 +10,7 @@ Validate Terraform configuration files using the official Terraform CLI.
| `terraform-version` | No | `~1.15` | Terraform version to use |
| `workspace` | No | `""` | Terraform workspace to use |
| `jfrog-token` | No | `""` | JFrog Artifactory token for the Terraform provider registry (`TF_TOKEN_schmalz_jfrog_io`) |
| `mock-files` | No | `""` | Newline-separated list of file paths (relative to repo root) to create as empty files before validation. Useful when Terraform uses `file()` references that do not exist in CI. |
## Usage
@ -20,8 +21,22 @@ Validate Terraform configuration files using the official Terraform CLI.
jfrog-token: ${{ secrets.JFROG_TOKEN }}
```
With mock files for `file()` dependencies:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/terraform-validate@terraform-validate-v1
with:
workspace: stage
jfrog-token: ${{ secrets.JFROG_TOKEN }}
mock-files: |
config/app.json
secrets/tls.crt
secrets/tls.key
```
## Notes
- Runs `terraform init -backend=false`, `terraform fmt -check -recursive`, and `terraform validate`.
- Sets `TF_WORKSPACE` during validate if `workspace` is provided.
- Sets `TF_TOKEN_schmalz_jfrog_io` on both `init` and `validate` steps if `jfrog-token` is provided.
- Sets `TF_TOKEN_schmalz_jfrog_io` on both `init` and `validate` steps if `jfrog-token` is provided.
- When `mock-files` is set, empty files are created at the given paths (including any missing parent directories) before `terraform init` runs. This allows validation of configurations that reference external files via `file()`.

View file

@ -19,6 +19,13 @@ inputs:
description: JFrog Artifactory token used for Terraform provider registry (sets TF_TOKEN_schmalz_jfrog_io)
required: false
default: ""
mock-files:
description: |-
Newline-separated list of file paths to create as empty files before validation.
Useful when Terraform configurations reference external files via file() that do not exist in CI.
Paths are relative to the repository root.
required: false
default: ""
runs:
using: composite
@ -30,6 +37,31 @@ runs:
with:
terraform_version: ${{ inputs.terraform-version }}
- 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"
- 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 }}-
- name: Create mock files
if: ${{ inputs.mock-files != '' }}
shell: bash
env:
MOCK_FILES: ${{ inputs.mock-files }}
run: |
while IFS= read -r mock_file; do
[ -z "$mock_file" ] && continue
mkdir -p "$(dirname "$mock_file")"
touch "$mock_file"
done <<< "$MOCK_FILES"
- name: Terraform Init
shell: bash
env:

37
upload-artifact/README.md Normal file
View file

@ -0,0 +1,37 @@
# upload-artifact
Upload files as a Forgejo Actions artifact. Thin wrapper around `forgejo/upload-artifact` pinned to a specific commit SHA to prevent supply chain attacks.
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `name` | Yes | — | Artifact name |
| `path` | Yes | — | File or directory path to upload |
| `retention-days` | No | `30` | Number of days to retain the artifact |
| `if-no-files-found` | No | `warn` | Behaviour when no files are found — `warn`, `error`, or `ignore` |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/upload-artifact@upload-artifact-v1
with:
name: my-artifact
path: dist/
```
Upload and ignore if no files exist:
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/upload-artifact@upload-artifact-v1
with:
name: blob-report-${{ matrix.shard-index }}
path: frontend/blob-report/
retention-days: 3
if-no-files-found: ignore
```
## Notes
- Wraps `forgejo/upload-artifact` v4 (node20), compatible with Ubuntu 22 runners.
- The underlying action is pinned to a commit SHA rather than a mutable tag to prevent supply chain attacks.

View file

@ -0,0 +1,33 @@
name: Schmalz Upload Artifact
description: >
Upload files as a Forgejo Actions artifact.
Thin wrapper around forgejo/upload-artifact, pinned to a specific SHA.
inputs:
name:
description: Artifact name
required: true
path:
description: File or directory path to upload
required: true
retention-days:
description: Number of days to retain the artifact
required: false
default: "30"
if-no-files-found:
description: Behaviour when no files are found — 'warn', 'error', or 'ignore'
required: false
default: warn
runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
# forgejo/upload-artifact v4 — https://code.forgejo.org/forgejo/upload-artifact/commits/tag/v4
- name: Upload artifact
uses: https://code.forgejo.org/forgejo/upload-artifact@16871d9e8cfcf27ff31822cac382bbb5450f1e1e
with:
name: ${{ inputs.name }}
path: ${{ inputs.path }}
retention-days: ${{ inputs.retention-days }}
if-no-files-found: ${{ inputs.if-no-files-found }}

25
vacuum-lint/README.md Normal file
View file

@ -0,0 +1,25 @@
# vacuum-lint
Action for validating and linting OpenAPI specifications using [Vacuum](https://github.com/daveshanley/vacuum).
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `spec-dir` | No | `spec` | Directory containing the OpenAPI spec |
| `spec-filename` | No | `openapi.json` | Filename of the OpenAPI spec |
| `rules-filename` | No | `vacuum.rules.yaml` | Filename of the lint rules config file |
| `ignore-filename` | No | `vacuum.ignore.yaml` | Filename of the lint ignore file |
| `min-score` | No | `70` | Minimum linting score for the check to pass |
## Usage
```yaml
- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/vacuum-lint@vacuum-lint-v1
```
## Notes
- If `rules-filename` is found inside `spec-dir`, it is passed to Vacuum via `-r` to apply custom rulesets; otherwise Vacuum uses its default rules.
- If `ignore-filename` is found inside `spec-dir`, it is passed to Vacuum via `--ignore-file` to suppress known violations.
- The action fails when the computed linting score falls below `min-score`.

65
vacuum-lint/action.yml Normal file
View file

@ -0,0 +1,65 @@
name: Vacuum Lint
description: >
Validate and lint OpenAPI specifications using Vacuum.
inputs:
spec-dir:
description: Directory containing OpenAPI spec
required: false
default: "spec"
spec-filename:
description: Filename of the OpenAPI spec
required: false
default: "openapi.json"
rules-filename:
description: Filename of the lint rules config file
required: false
default: "vacuum.rules.yaml"
ignore-filename:
description: Filename of the lint ignore file
required: false
default: "vacuum.ignore.yaml"
min-score:
description: Minimum linting score for the check to pass
required: false
default: "70"
runs:
using: composite
steps:
# Pinned to commit SHA instead of a tag to prevent supply chain attacks.
- name: Install Vacuum
shell: bash
run: curl -fsSL https://raw.githubusercontent.com/daveshanley/vacuum/8222bba0c8b21a3a94faf472e06c4db06f06c6ce/bin/install.sh | sudo sh > /dev/null 2>&1
- name: Lint Spec
shell: bash
env:
SPEC_DIR: ${{ inputs.spec-dir }}
SPEC_FILE: ${{ inputs.spec-filename }}
RULES_FILE: ${{ inputs.rules-filename }}
IGNORE_FILE: ${{ inputs.ignore-filename }}
MIN_SCORE: ${{ inputs.min-score }}
run: |
echo "Linting: [$SPEC_DIR/$SPEC_FILE]"
# base command
CMD="vacuum lint $SPEC_DIR/$SPEC_FILE -x --min-score $MIN_SCORE"
# check for rules file
if [ -f "$SPEC_DIR/$RULES_FILE" ]; then
CMD="$CMD -r $SPEC_DIR/$RULES_FILE"
echo " - using ruleset [$SPEC_DIR/$RULES_FILE]"
fi
# check for ignore file
if [ -f "$SPEC_DIR/$IGNORE_FILE" ]; then
CMD="$CMD --ignore-file $SPEC_DIR/$IGNORE_FILE"
echo " - using ignore file [$SPEC_DIR/$IGNORE_FILE]"
fi
# execute command
$CMD
echo "Linted: [$SPEC_DIR/$SPEC_FILE]"
echo ""