This repository hosts a set of github actions we use to deploy our apps.
- iMio github actions
- Actions
- build-push-notify
- check-url-availibility
- code-analysis-notify
- deb-build-push-notify
- helm-release-notify
- helm-test-notify
- mattermost-notify
- plone-package-test-notify
- plone-theme-build-push-notify
- rundeck-notify
- tag-notify
- k8s-update-tag
- trivy-scan-notify
- trivy-sbom-notify
- trivy-claude-analysis
- claude-agent
- Contribute
- Actions
build/push a docker image using docker/build-push-action and optionally notify via a mattermost webhook
| name | required | type | default | description |
|---|---|---|---|---|
| IMAGE_NAME | yes | string | Name of the image to build | |
| IMAGE_TAGS | yes | string | Tags of the image to build and push (one per line) | |
| REGISTRY_URL | yes | string | URL of the registry | |
| REGISTRY_USERNAME | yes | string | Username to login to registry | |
| REGISTRY_PASSWORD | yes | string | Password to login to registry | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost | |
| PLATFORMS | yes | string | "linux/amd64" |
Platforms to build the image for |
| CONTEXT | yes | string | "./" |
Build context |
| DOCKERFILE | yes | string | "Dockerfile" |
Name of the Dockerfile |
| BUILD_ARGS | yes | string | "" |
Build arguments to pass to the Dockerfile |
| TARGET | no | string | Target stage to build | |
| PRE_BUILD_COMMANDS | no | string | Optional commands to run before the build (one per line) |
Loop until a given url returns a 200 status-code. Can be used during deployments to test if an app is available.
| name | required | type | default | description |
|---|---|---|---|---|
| URL | yes | string | URL to test | |
| TIMEOUT | yes | integer | 5 | Timeout (in minutes) |
Run checks for Plone backend code and optionally notify via a mattermost webhook
This github action uses the code-analysis-action from the Plone organization.
| name | required | type | default | description |
|---|---|---|---|---|
| BASE_DIR | no | string | Base directory | |
| CHECK | no | string | Checks to be used | |
| PATH | no | string | Path to be checked | |
| LOG_LEVEL | no | string | "INFO" | Log level |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
Build a deb package, push it on a repository and optionally notify via a mattermost webhook
| name | required | type | default | description |
|---|---|---|---|---|
| REPOSITORY_URL | yes | string | URL of the repository | |
| REPOSITORY_LOGIN | yes | string | Login for the repository | |
| REPOSITORY_PASSWORD | yes | string | Passsword for the repository | |
| PACKAGE_NAME | yes | string | Name of the package to build | |
| PACKAGE_INSTALL_PATH | yes | string | '/usr/...' |
Path to install package |
| PACKAGE_VERSION | yes | string | Package version | |
| PACKAGE_DEPENDENCY | yes | string | 'passerelle' |
Package dependency |
| SIGNER_KEY | yes | string | Key to sign deb package (base64 encoded) | |
| SIGNER_KEY_ID | yes | string | '9D4...' |
ID of the key to sign deb package |
| SIGNER_KEY_PASSPHRASE | yes | string | Passphrase to sign deb package | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
Release a helm chart and optionally notify via a mattermost webhook
| name | required | type | default | description |
|---|---|---|---|---|
| HELM_VERSION | yes | string | "3.12.3" | Helm version to use |
| HELM_DEPENDENCIES | no | string | Helm dependencies | |
| INDEX_DIR | yes | string | "." | Index directory |
| CHARTS_DIR | yes | string | "." | Charts directory |
| TARGET_DIR | yes | string | "test" | Target directory to release |
| APP_ID | yes | string | Github App ID | |
| PRIVATE_KEY | yes | string | Github App private key | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
/
Lint and test a helm chart and optionally notify via a mattermost webhook
| name | required | type | default | description |
|---|---|---|---|---|
| PYTHON_VERSION | yes | string | "3.10" | Python version to use |
| HELM_VERSION | yes | string | "v3.12.3" | Helm version to use |
| HELM_RELEASE | yes | string | "test" | Helm release name |
| HELM_NAMESPACE | yes | string | "test" | Helm namespace name |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
/
Send a rich color-coded notification to a Mattermost webhook.
Notifications use Mattermost attachments with a green strip on success and red on failure, an emoji prefix, a structured bold-field body, and an auto-appended link to the GitHub Actions run. If MATTERMOST_WEBHOOK_URL is omitted the step is silently skipped.
| name | required | type | default | description |
|---|---|---|---|---|
| MATTERMOST_WEBHOOK_URL | no | string | "" |
Webhook URL. If empty, notification is skipped. |
| TITLE | yes | string | Short notification title (e.g. "Docker Build & Push") |
|
| BODY | no | string | "" |
Notification body (Markdown, newlines supported). A link to the GitHub Actions run is always appended automatically. |
| STATUS | yes | string | Job outcome: success or failure |
- name: Notify on Mattermost
if: always()
uses: imio/gha/mattermost-notify@v7
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
STATUS: ${{ steps.build.outcome == 'success' && 'success' || 'failure' }}
TITLE: "My Job"
BODY: |
**Branch:** ${{ github.ref_name }}
**Commit:** ${{ github.sha }}Test a Plone package and optionally notify via a mattermost webhook
Warning
Python 2 support has been dropped in v5. If you still need it, use v4
| name | required | type | default | description |
|---|---|---|---|---|
| BUILDOUT_COMMAND | yes | string | "buildout" | Command to run buildout |
| BUILDOUT_CONFIG_FILE | yes | string | "buildout.cfg" | Buildout config file |
| BUILDOUT_OPTIONS | no | string | Options to pass to buildout | |
| CACHE_KEY | no | string | key to use in actions/cache | |
| INSTALL_DEPENDENCIES_COMMANDS | no | string | Install dependencies commands (one per line) | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost | |
| PYTHON_VERSION | yes | string | "3.13" | Python version to use |
| TEST_COMMAND | yes | string | "bin/test" | Test command to run |
| UV_VERSION | yes | string | "0.7.13" | uv version to use |
Build a theme, upload it to a plone site and optionally notify on Mattermost
| name | required | type | default | description |
|---|---|---|---|---|
| THEME_PATH | yes | string | Folder where theme files are located | |
| PLONE_URL | yes | string | URL of the Plone site | |
| PLONE_USERNAME | yes | string | Username to login to Plone | |
| PLONE_PASSWORD | yes | string | Password to login to Plone | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
Trigger a repository dispatch event and optionally notify on Mattermost
| name | required | type | default | description |
|---|---|---|---|---|
| REPOSITORY | yes | string | Repository to trigger the dispatch event | |
| GIT_REFERENCE | no | string | "main" | Reference to trigger the event on |
| INPUTS | no | string | "{}" | Inputs to pass to the workflow, Exemple : {"input1":"abc", "input2":"abc"} |
| REPOSITORY_OWNER | yes | string | Repository owner | |
| WORKFLOW_FILENAME | yes | string | Filename of the workflow to trigger | |
| APP_ID | yes | string | GitHub App ID | |
| APP_PRIVATE_KEY | yes | string | GitHub App private key | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
/
call a rundeck job and optionally notify via a mattermost webhook
| name | required | type | default | description |
|---|---|---|---|---|
| RUNDECK_URL | yes | string | URL of the Rundeck server | |
| RUNDECK_TOKEN | yes | string | Auth token to call Rundeck job | |
| RUNDECK_JOB_ID | yes | string | ID of the rundeck job to call | |
| RUNDECK_PARAMETERS | no | string | Parameters to pass to the Rundeck job | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
Add tags to a docker image and optionally notify via a mattermost webhook
| name | required | type | default | description |
|---|---|---|---|---|
| IMAGE_NAME | yes | string | Name of the image to tag | |
| IMAGE_TAG | yes | string | "staging" |
Actual tag of the image |
| NEW_IMAGE_TAGS | yes | string | Tags to add to the image (one per line) | |
| REGISTRY_URL | yes | string | URL of the registry | |
| REGISTRY_USERNAME | yes | string | Username to login to registry | |
| REGISTRY_PASSWORD | yes | string | Password to login to registry | |
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
Update a component tag in Kubernetes values file and commit to repository. This action is useful for automated deployments where you want to update the image tag in your Kubernetes configuration files.
| name | required | type | default | description |
|---|---|---|---|---|
| TAG | yes | string | Tag to set for the component (e.g., commit SHA) | |
| REPO_TOKEN_NAME | yes | string | Name of the repository access token | |
| REPO_ACCESS_TOKEN | yes | string | Repository access token for authentication | |
| REPO_URL | yes | string | Repository URL (without https://) | |
| TARGET_BRANCH | no | string | "main" |
Target branch to update |
| VALUES_FILE_PATH | yes | string | Path to the values file to update |
- name: Update Kubernetes tag
uses: IMIO/gha/k8s-update-tag@v5
with:
TAG: ${{ github.sha }}
REPO_TOKEN_NAME: DEPLOY_TOKEN
REPO_ACCESS_TOKEN: ${{ secrets.K8S_DEPLOY_TOKEN }}
REPO_URL: github.com/myorg/k8s-configs.git
TARGET_BRANCH: main
VALUES_FILE_PATH: staging/myapp/values-dev.yamlRun a Trivy scan (container image, filesystem or IaC config), upload the SARIF report to GitHub Code Scanning, archive it as a workflow artifact, and optionally notify via Mattermost with parsed severity counts.
External actions are pinned by SHA as required by the iMio security référentiel (§5.5 CI/CD).
Important
The calling workflow must grant permissions: security-events: write for the SARIF upload to Code Scanning to succeed. On private repositories, GitHub Advanced Security must be enabled.
| name | required | type | default | description |
|---|---|---|---|---|
| SCAN_TYPE | yes | string | One of image, fs, config |
|
| IMAGE_REF | cond. | string | Image reference to scan (required when SCAN_TYPE=image) |
|
| SCAN_REF | no | string | "." |
Filesystem path (used when SCAN_TYPE is fs or config) |
| SEVERITY | no | string | "HIGH,CRITICAL" |
Comma-separated severity levels to report |
| SCANNERS | no | string | (per-type default) | Trivy scanners. If empty: image→vuln,secret,misconfig, fs→vuln,secret, config→secret,misconfig |
| EXIT_CODE | no | string | "1" |
Exit code when findings match SEVERITY (set to "0" during bootstrap) |
| IGNORE_UNFIXED | no | string | "true" |
Ignore vulnerabilities without a known fix |
| TRIVYIGNORES | no | string | ".trivyignore" |
Path to a .trivyignore file |
| UPLOAD_SARIF | no | string | "true" |
Upload SARIF to GitHub Code Scanning |
| SARIF_CATEGORY | no | string | "trivy-<SCAN_TYPE>" |
Code Scanning category for split results |
| TRIVY_USERNAME | no | string | Username for a private image registry | |
| TRIVY_PASSWORD | no | string | Password for a private image registry | |
| GITHUB_TOKEN | no | string | Pass secrets.GITHUB_TOKEN to avoid Trivy DB rate-limits |
|
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
| name | description |
|---|---|
| critical | Number of CRITICAL findings |
| high | Number of HIGH findings |
| medium | Number of MEDIUM findings |
| json_file | Filename of the Trivy JSON report (relative to the workspace) — use as JSON_FILE input to trivy-claude-analysis in the same job |
| artifact_name | Name of the uploaded Trivy JSON workflow artifact — use with actions/download-artifact in a later job |
name: Trivy
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
security-events: write # required to upload SARIF
jobs:
trivy-fs:
runs-on: ubuntu-latest
steps:
- uses: imio/gha/trivy-scan-notify@v7
with:
SCAN_TYPE: fs
SCAN_REF: .
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
trivy-iac:
runs-on: ubuntu-latest
steps:
- uses: imio/gha/trivy-scan-notify@v7
with:
SCAN_TYPE: config
SCAN_REF: .
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
trivy-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- run: docker build -t ${{ github.repository }}:${{ github.sha }} .
- uses: imio/gha/trivy-scan-notify@v7
with:
SCAN_TYPE: image
IMAGE_REF: ${{ github.repository }}:${{ github.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}To run Claude on the scan results — either immediately in the same job, or after a manual approval step in a second job — see trivy-claude-analysis below.
Generate a CycloneDX (or SPDX) SBOM for a container image with Trivy, upload it as a workflow artifact, and optionally notify on Mattermost. This action does not block the pipeline.
| name | required | type | default | description |
|---|---|---|---|---|
| IMAGE_REF | yes | string | Full registry-qualified image reference | |
| FORMAT | no | string | "cyclonedx" |
cyclonedx or spdx-json |
| OUTPUT | no | string | "sbom.cdx.json" |
Output file path |
| ARTIFACT_NAME | no | string | "sbom-${{ github.sha }}" |
Name of the workflow artifact |
| RETENTION_DAYS | no | string | "90" |
Artifact retention in days |
| TRIVY_USERNAME | no | string | Username for a private image registry | |
| TRIVY_PASSWORD | no | string | Password for a private image registry | |
| GITHUB_TOKEN | no | string | Pass secrets.GITHUB_TOKEN to avoid rate-limits |
|
| MATTERMOST_WEBHOOK_URL | no | string | Webhook URL to send notifications on Mattermost |
jobs:
sbom:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: trivy-image
steps:
- uses: imio/gha/trivy-sbom-notify@v7
with:
IMAGE_REF: registry.example.org/${{ github.repository }}:${{ github.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}Post-processing action that reads a Trivy JSON report and asks Claude to create private draft GitHub repository security advisories — one per finding, at the severities you choose. The Trivy-specific prompt (CVE handling, ecosystem mapping, deduplication) is versioned inside this action, so caller repositories never duplicate it.
Important
The calling workflow must grant permissions: repository-advisories: write for advisory creation to succeed.
This action is typically chained after trivy-scan-notify, which produces the JSON report and uploads it as a workflow artifact.
| name | required | type | default | description |
|---|---|---|---|---|
| JSON_FILE | yes | string | Path to the Trivy JSON report (e.g. trivy-image.json). Use steps.<id>.outputs.json_file from trivy-scan-notify in the same job. |
|
| SCAN_TYPE | yes | string | image, fs, or config |
|
| TARGET | yes | string | What was scanned (image reference or scan path) | |
| SARIF_CATEGORY | no | string | "trivy-<SCAN_TYPE>" |
SARIF category from the originating scan, used to link advisories to the Code Scanning report |
| SEVERITIES | no | string | "CRITICAL,HIGH" |
Comma-separated severities. Append ,MEDIUM to include medium findings. |
| ANTHROPIC_API_KEY | yes | string | Anthropic API key. Pass secrets.ANTHROPIC_API_KEY. |
|
| GITHUB_TOKEN | yes | string | GitHub token with repository-advisories: write. Pass secrets.GITHUB_TOKEN. |
|
| MODEL | no | string | "claude-sonnet-4-6" |
Claude model ID override |
permissions:
contents: read
security-events: write
repository-advisories: write
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: imio/gha/trivy-scan-notify@v7
id: trivy
with:
SCAN_TYPE: image
IMAGE_REF: registry.example.org/myapp:${{ github.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
- uses: imio/gha/trivy-claude-analysis@v7
if: steps.trivy.outputs.critical > 0 || steps.trivy.outputs.high > 0
with:
JSON_FILE: ${{ steps.trivy.outputs.json_file }}
SCAN_TYPE: image
TARGET: registry.example.org/myapp:${{ github.sha }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}The JSON file is already on disk from trivy-scan-notify, so no re-scan is needed.
This pattern gates the Claude analysis (and the Anthropic token cost) behind a human approval step. Job 1 runs the scan and posts finding counts to Mattermost. A reviewer approves job 2 only when Claude analysis is worth running. Job 2 operates on the exact JSON produced by job 1 (downloaded from the workflow artifact), so there is no re-scan and no risk of state drift.
Note
Create a security-review environment in repo Settings → Environments and add required reviewers before using this pattern.
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
outputs:
critical: ${{ steps.trivy.outputs.critical }}
high: ${{ steps.trivy.outputs.high }}
json_file: ${{ steps.trivy.outputs.json_file }}
artifact_name: ${{ steps.trivy.outputs.artifact_name }}
steps:
- uses: imio/gha/trivy-scan-notify@v7
id: trivy
with:
SCAN_TYPE: image
IMAGE_REF: registry.example.org/myapp:${{ github.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
claude-analysis:
needs: scan
if: needs.scan.outputs.critical > 0 || needs.scan.outputs.high > 0
environment: security-review # pauses for human approval
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.ADVISORY_APP_ID }}
private-key: ${{ secrets.ADVISORY_APP_SECRET }}
- uses: actions/download-artifact@v4
with:
name: ${{ needs.scan.outputs.artifact_name }}
- uses: imio/gha/trivy-claude-analysis@v7
with:
JSON_FILE: ${{ needs.scan.outputs.json_file }}
SCAN_TYPE: image
TARGET: registry.example.org/myapp:${{ github.sha }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}The two-job pattern above depends on a few things that consumers must
set up once per repository (or at the org level) before the workflow
can succeed. The default GITHUB_TOKEN cannot be granted advisory
scope — repository-advisories is not a valid GITHUB_TOKEN
permission key — so creating security advisories requires an
installation token minted from a GitHub App.
- GitHub App —
imio-advisory-app(org-level, shared across IMIO repos):- Repository permissions: Repository security advisories → Read and write.
- Webhook: disabled.
- Installed on every repo that runs this action.
- Secrets (repo or org level):
ADVISORY_APP_ID— numeric App ID.ADVISORY_APP_SECRET— full.pemprivate key, including the-----BEGIN ...-----and-----END ...-----lines.ANTHROPIC_API_KEY— Anthropic API key used by Claude.
- Environment —
security-review(repo Settings → Environments):- Required reviewers enabled, with at least one reviewer from the security team.
- Prevent self-review enabled where the repo's review policy requires it (recommended on repos where the author must not be able to approve their own run).
- Deployment branches limited to the repository's protected branches (typically
main, anddevif the repo uses one — adjust to match the branching model of the consuming repo). - This is the manual-approval gate: the
claude-analysisjob queues until a reviewer clicks Approve on the workflow run page. - On private repositories, protected environments require GitHub Team or Enterprise.
- Private vulnerability reporting must be enabled on each consuming repo (Settings → Code security) before advisories can be created.
Run a Claude Code agent with a given prompt in a CI pipeline. Thin, opinionated wrapper around anthropics/claude-code-action with prompt composition, optional file exposure, model selection, and GitHub API access for operations such as creating security advisories.
Note
The calling workflow must grant the permissions required for the operations Claude will perform. For security advisory creation, add repository-advisories: write.
| name | required | type | default | description |
|---|---|---|---|---|
| PROMPT | yes | string | Instructions for Claude | |
| FILES | no | string | Newline-separated list of file paths to expose to Claude (appended to the prompt as a "Files to examine" section) | |
| ANTHROPIC_API_KEY | yes | string | Anthropic API key. Pass secrets.ANTHROPIC_API_KEY. |
|
| MODEL | no | string | "claude-sonnet-4-6" |
Claude model ID |
| GITHUB_TOKEN | no | string | GitHub token forwarded to Claude for GitHub API operations (e.g. creating security advisories). Pass secrets.GITHUB_TOKEN. |
|
| ALLOWED_TOOLS | no | string | Comma-separated allowlist passed to Claude Code --allowedTools to pre-approve tools in headless CI (no interactive prompts). Example: Bash(gh api:*),Read,Write. Leave empty to keep default Claude Code permissions, which will pause waiting for approval on write-mode Bash in CI. |
|
| SKIP_PERMISSIONS | no | string | "false" |
Set to "true" to pass --dangerously-skip-permissions, auto-approving every tool call. Simpler than ALLOWED_TOOLS but removes all safeguards — use only in tightly-scoped CI actions with minimally-scoped credentials. Takes precedence over ALLOWED_TOOLS when both are set. |
Claude's output is displayed in the GitHub Actions Step Summary (display_report: true).
jobs:
security-advisory:
runs-on: ubuntu-latest
permissions:
contents: read
repository-advisories: write
steps:
- uses: imio/gha/claude-agent@v7
with:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PROMPT: |
Review the dependency list and create a private draft security advisory
for any known critical vulnerability you find.
FILES: |
package.json
requirements.txtFor Trivy scan post-processing, use the dedicated trivy-claude-analysis action instead of calling claude-agent directly — it keeps the Trivy-specific prompt versioned inside the iMio actions.
A new release is issued when a tag beginning with v is pushed. The main release (for instance v3) will also be updated with the latest tag.
The release note is automatically populated using the "[tag]" section from the CHANGELOG.md .
See the CHANGELOG.md file for an example
Typically, you will first update the changelog (Example commit).
Then, you will push the tags.
git add CHANGELOG.md
git commit -m 'doc: Release v5.0.0'
git tag -a -m 'release v5.0.0' v5.0.0
git push --follow-tags