Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .github/workflows/conventional-commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2026 The Contributors to Eclipse OpenSOVD (see CONTRIBUTORS)
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0

name: Conventional Commits

on:
workflow_call:
inputs:
validate-pr-title:
description: "Validate PR title against Conventional Commits format"
type: boolean
default: true
validate-commits:
description: >
Validate individual commit subjects in the PR using prek with the conventional-pre-commit hook from the caller's .pre-commit-config.yaml
type: boolean
default: true
types:
description: >
Newline-separated list of allowed conventional commit types for PR title validation (via amannn/action-semantic-pull-request). Commit subject validation uses the types configured in the caller's .pre-commit-config.yaml.
type: string
default: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
scopes:
description: "Newline-separated list of allowed scopes for PR title validation (empty = any scope allowed)"
type: string
default: ""
require-scope:
description: "Require a scope in PR title"
type: boolean
default: false
subject-pattern:
description: "Regex pattern the PR title subject (text after type/scope) must match"
type: string
default: ""
subject-pattern-error:
description: "Error message when subject-pattern fails"
type: string
default: ""
ignore-authors:
description: "Newline-separated list of commit author emails to ignore (e.g. bots)"
type: string
default: ""
prek-version:
description: "Version of prek to install (empty = latest)"
type: string
default: ""

permissions:
contents: read
pull-requests: read

jobs:
pr-title:
name: PR Title
if: ${{ inputs.validate-pr-title && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: ${{ inputs.types }}
scopes: ${{ inputs.scopes }}
requireScope: ${{ inputs.require-scope }}
subjectPattern: ${{ inputs.subject-pattern }}
subjectPatternError: ${{ inputs.subject-pattern-error }}

commit-subjects:
name: Commit Subjects
if: ${{ inputs.validate-commits && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: astral-sh/setup-uv@v7

- name: Install prek
shell: bash
run: |
if [[ -n "${{ inputs.prek-version }}" ]]; then
uv tool install "prek==${{ inputs.prek-version }}"
else
uv tool install prek
fi

- name: Validate commit subjects
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
IGNORE_AUTHORS: ${{ inputs.ignore-authors }}
shell: bash
run: |
set -euo pipefail

declare -A ignore_map
while IFS= read -r email; do
email=$(echo "$email" | xargs)
[[ -n "$email" ]] && ignore_map["$email"]=1
done <<< "$IGNORE_AUTHORS"

fail=0
for sha in $(git rev-list "$BASE_SHA..$HEAD_SHA"); do
author_email=$(git log -1 --format='%ae' "$sha")
if [[ -n "${ignore_map[$author_email]:-}" ]]; then
continue
fi

git log -1 --format=%B "$sha" > /tmp/commit-msg
if ! prek run --hook-stage commit-msg \
--commit-msg-filename /tmp/commit-msg conventional-pre-commit; then
echo "::error::Non-conventional commit: $(git log -1 --format='%h %s' "$sha")"
fail=1
fi
done
exit "$fail"
159 changes: 159 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2026 The Contributors to Eclipse OpenSOVD (see CONTRIBUTORS)
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0

name: Release

on:
workflow_call:
inputs:
version:
description: >
Release version string. Typically the git tag (e.g. "v1.2.3") or a rolling identifier like "nightly" or "latest". Used for archive names and the GitHub Release title.
type: string
required: true
artifact-pattern:
description: >
Pattern for actions/download-artifact to match build artifacts. Each matching artifact directory is expected to contain the binary. Example: "my-app-*" matches "my-app-x86_64-unknown-linux-gnu", etc.
type: string
required: true
binary-name:
description: >
Base name of the binary inside each artifact directory (without extension). Windows artifacts are expected to have a .exe suffix automatically. Example: "opensovd-gateway"
type: string
required: true
git-cliff-version:
description: "Version of git-cliff to install"
type: string
default: "2.11.0"
git-cliff-args:
description: >
Extra arguments for git-cliff (e.g. "--config cliff.toml"). The workflow automatically uses --unreleased for nightly/latest and --current for versioned tags.
type: string
default: ""
attestation:
description: "Generate build provenance attestation for release artifacts"
type: boolean
default: true
prerelease:
description: >
Mark the release as a prerelease. When not explicitly set, nightly and latest releases are automatically marked as prereleases.
type: boolean
default: false
delete-existing:
description: >
Delete an existing release with the same tag before creating a new one. Useful for rolling releases (nightly, latest).
type: boolean
default: false
draft:
description: "Create the release as a draft"
type: boolean
default: false
archive-prefix:
description: >
Prefix for archive filenames. Defaults to the binary-name input. Archives are named: <prefix>-<version>-<target>.<ext>
type: string
default: ""

permissions:
contents: write
attestations: write
id-token: write

jobs:
release:
name: Create Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: taiki-e/install-action@v2
with:
tool: git-cliff@${{ inputs.git-cliff-version }}

- uses: actions/download-artifact@v8
with:
pattern: ${{ inputs.artifact-pattern }}
path: artifacts

- name: Generate changelog
shell: bash
run: |
VERSION="${{ inputs.version }}"
EXTRA_ARGS="${{ inputs.git-cliff-args }}"
if [[ "$VERSION" == "nightly" || "$VERSION" == "latest" ]]; then
git cliff --unreleased $EXTRA_ARGS -o CHANGELOG.md
else
git cliff --current $EXTRA_ARGS -o CHANGELOG.md
fi

- name: Package artifacts
shell: bash
run: |
VERSION="${{ inputs.version }}"
BINARY="${{ inputs.binary-name }}"
PREFIX="${{ inputs.archive-prefix }}"
PREFIX="${PREFIX:-$BINARY}"

mkdir -p release

for dir in artifacts/${BINARY}-*/; do
[ -d "$dir" ] || continue
target=$(basename "$dir" | sed "s/${BINARY}-//")

if [[ "$target" == *windows* ]]; then
zip "release/${PREFIX}-${VERSION}-${target}.zip" "${dir}${BINARY}.exe"
else
chmod +x "${dir}${BINARY}"
tar -czf "release/${PREFIX}-${VERSION}-${target}.tar.gz" -C "$dir" "$BINARY"
fi
done

cd release
for file in *.tar.gz *.zip; do
[ -f "$file" ] || continue
sha512sum "$file" > "$file.sha512"
done

- name: Attest build provenance
if: ${{ inputs.attestation }}
uses: actions/attest-build-provenance@v4
with:
subject-path: 'release/*'

- name: Delete existing release
if: ${{ inputs.delete-existing }}
env:
GH_TOKEN: ${{ github.token }}
run: gh release delete "${{ inputs.version }}" --yes --cleanup-tag || true

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
VERSION="${{ inputs.version }}"

ARGS=()
ARGS+=(--title "$VERSION")
ARGS+=(--notes-file CHANGELOG.md)

if [[ "${{ inputs.draft }}" == "true" ]]; then
ARGS+=(--draft)
fi

if [[ "${{ inputs.prerelease }}" == "true" ]]; then
ARGS+=(--prerelease)
elif [[ "$VERSION" == "nightly" || "$VERSION" == "latest" ]]; then
ARGS+=(--prerelease)
fi

gh release create "$VERSION" "${ARGS[@]}" release/*
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,98 @@ jobs:
clippy-deny-warnings: 'true'
```

### Using the Conventional Commits Workflow

The `conventional-commits.yml` workflow validates PR titles and individual commit messages against the [Conventional Commits](https://www.conventionalcommits.org/) specification.

Commit subject validation uses [prek](https://prek.j178.dev/) to run the `conventional-pre-commit` hook from the caller's `.pre-commit-config.yaml`. This ensures CI validates exactly what developers see locally.

**Prerequisites**: The calling repository must have a `.pre-commit-config.yaml` with the `conventional-pre-commit` hook configured:

```yaml
# .pre-commit-config.yaml (in your repository)
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v4.4.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
```

**Usage**:

```yaml
name: CI

on:
pull_request:

jobs:
conventional-commits:
uses: eclipse-opensovd/cicd-workflows/.github/workflows/conventional-commits.yml@main
with:
validate-pr-title: true
validate-commits: true
```

#### Available Inputs

- `validate-pr-title` (optional): Validate PR title against Conventional Commits format. Defaults to `true`.
- `validate-commits` (optional): Validate individual commit subjects using prek with the caller's `.pre-commit-config.yaml`. Defaults to `true`.
- `types` (optional): Newline-separated list of allowed conventional commit types for PR title validation. Commit subject validation uses types from the caller's `.pre-commit-config.yaml`. Defaults to `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.
- `scopes` (optional): Newline-separated list of allowed scopes for PR title validation. Empty means any scope is allowed.
- `require-scope` (optional): Require a scope in PR title. Defaults to `false`.
- `subject-pattern` (optional): Regex pattern the PR title subject must match.
- `subject-pattern-error` (optional): Error message when `subject-pattern` fails.
- `ignore-authors` (optional): Newline-separated list of commit author emails to skip (e.g. bot accounts).
- `prek-version` (optional): Version of prek to install. Defaults to latest.

### Using the Release Workflow

The `release.yml` workflow packages build artifacts into platform archives, generates a changelog with git-cliff, creates SHA-512 checksums, optionally attests build provenance, and publishes a GitHub Release.

```yaml
name: Release

on:
push:
tags: ['v*']

jobs:
build:
# Your build matrix that uploads artifacts named like:
# my-app-x86_64-unknown-linux-gnu
# my-app-aarch64-unknown-linux-gnu
# my-app-x86_64-pc-windows-msvc
# Each artifact contains the binary (e.g. "my-app" or "my-app.exe").
...

release:
needs: build
uses: eclipse-opensovd/cicd-workflows/.github/workflows/release.yml@main
with:
version: ${{ github.ref_name }}
artifact-pattern: "my-app-*"
binary-name: "my-app"
permissions:
contents: write
attestations: write
id-token: write
```

#### Available Inputs

- `version` (required): Release version string (e.g. `v1.2.3`, `nightly`, `latest`). Used for archive names and the GitHub Release title.
- `artifact-pattern` (required): Pattern for `actions/download-artifact` to match build artifacts (e.g. `my-app-*`).
- `binary-name` (required): Base name of the binary inside each artifact directory, without extension. Windows artifacts are expected to have a `.exe` suffix.
- `git-cliff-version` (optional): Version of git-cliff to install. Defaults to `2.11.0`.
- `git-cliff-args` (optional): Extra arguments for git-cliff (e.g. `--config cliff.toml`). The workflow automatically uses `--unreleased` for nightly/latest and `--current` for versioned tags.
- `attestation` (optional): Generate build provenance attestation for release artifacts. Defaults to `true`.
- `prerelease` (optional): Mark the release as a prerelease. Nightly and latest releases are automatically marked as prereleases. Defaults to `false`.
- `delete-existing` (optional): Delete an existing release with the same tag before creating a new one. Useful for rolling releases. Defaults to `false`.
- `draft` (optional): Create the release as a draft. Defaults to `false`.
- `archive-prefix` (optional): Prefix for archive filenames. Defaults to the `binary-name` value. Archives are named `<prefix>-<version>-<target>.<ext>`.

## Actions in This Repository

### Pre-commit Action (`pre-commit-action/`)
Expand Down
Loading