- go-licenses v1.6.0 added to release-tools image; license-check step
in go-ci task fails on forbidden/restricted licenses (GPL/AGPL/LGPL/MPL)
- new commit-title-check task validates PR title AND each PR branch
commit against Conventional Commits regex using PAC dynamic var
{{ body.pull_request.title }}; blocks merge before semantic-release
post-merge analysis would silently drop malformed titles
- on-pull-request pipeline wires commit-title-check as parallel task
alongside build-and-test; PAC resolves target_branch → base_ref
125 lines
4.9 KiB
YAML
125 lines
4.9 KiB
YAML
apiVersion: tekton.dev/v1beta1
|
|
kind: Task
|
|
metadata:
|
|
name: commit-title-check
|
|
annotations:
|
|
tekton.dev/displayName: "Conventional Commits PR title + commit message check"
|
|
spec:
|
|
description: |
|
|
Gate Conventional Commits format BEFORE merge. semantic-release reads
|
|
individual commit titles post-merge (see .releaserc); a malformed title
|
|
silently dropped a release in the past. This task fails the PR early
|
|
instead of waiting for the post-merge release pipeline to no-op.
|
|
|
|
Checks:
|
|
1. PR title (passed via PR_TITLE param from PAC dynamic var
|
|
{{ body.pull_request.title }}) matches Conventional Commits.
|
|
2. Every commit on the PR branch (revision..origin/main..HEAD on the
|
|
clone) matches the same regex. Merge commits are skipped.
|
|
|
|
The regex accepts the type whitelist from .releaserc plus 'revert'.
|
|
Optional scope (`(scope)`), optional breaking marker (`!`), required
|
|
colon+space+subject. Allows gitmoji or any character in subject.
|
|
params:
|
|
- name: PR_TITLE
|
|
description: |
|
|
Pull request title from PAC dynamic variable
|
|
{{ body.pull_request.title }}.
|
|
type: string
|
|
- name: BASE_REF
|
|
description: Base branch to diff PR commits against.
|
|
type: string
|
|
default: main
|
|
- name: GIT_IMAGE
|
|
description: |
|
|
Image carrying git + bash + grep. Reuses release-tools because it is
|
|
already pulled by go-ci on every PR run.
|
|
type: string
|
|
default: codeberg.org/operate-first/release-tools:latest
|
|
- name: CC_REGEX
|
|
description: Conventional Commits regex (anchored, ERE).
|
|
type: string
|
|
default: '^(feat|fix|docs|style|refactor|perf|test|ci|chore|build|revert)(\([^)]+\))?!?: .+'
|
|
workspaces:
|
|
- name: source
|
|
mountPath: /workspace/src
|
|
stepTemplate:
|
|
workingDir: $(workspaces.source.path)
|
|
steps:
|
|
- name: check-pr-title
|
|
image: $(params.GIT_IMAGE)
|
|
env:
|
|
- name: PR_TITLE
|
|
value: $(params.PR_TITLE)
|
|
- name: CC_REGEX
|
|
value: $(params.CC_REGEX)
|
|
script: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
if [ -z "${PR_TITLE}" ]; then
|
|
echo "PR_TITLE param is empty — cannot validate Conventional Commits format." >&2
|
|
echo "Hint: PipelineRun must pass {{ body.pull_request.title }} as PR_TITLE." >&2
|
|
exit 1
|
|
fi
|
|
printf 'PR title: %s\n' "${PR_TITLE}"
|
|
if printf '%s' "${PR_TITLE}" | grep -Eq "${CC_REGEX}"; then
|
|
echo "OK: PR title matches Conventional Commits."
|
|
else
|
|
echo "::error::PR title does not match Conventional Commits format." >&2
|
|
echo "Expected regex: ${CC_REGEX}" >&2
|
|
echo "Allowed types: feat, fix, docs, style, refactor, perf, test, ci, chore, build, revert" >&2
|
|
echo "Examples:" >&2
|
|
echo " feat(api): add /healthz endpoint" >&2
|
|
echo " fix: handle nil session token" >&2
|
|
echo " chore(release): 2.25.1" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: check-commits
|
|
image: $(params.GIT_IMAGE)
|
|
env:
|
|
- name: BASE_REF
|
|
value: $(params.BASE_REF)
|
|
- name: CC_REGEX
|
|
value: $(params.CC_REGEX)
|
|
script: |
|
|
#!/usr/bin/env bash
|
|
# Validate each commit unique to the PR branch. Skips merge commits
|
|
# (--no-merges) — their subject is generated by Forgejo and is not
|
|
# what semantic-release reads.
|
|
set -euo pipefail
|
|
git config --global --add safe.directory "$(workspaces.source.path)"
|
|
git fetch --no-tags --depth=200 origin "${BASE_REF}:refs/remotes/origin/${BASE_REF}" || \
|
|
git fetch --no-tags origin "${BASE_REF}:refs/remotes/origin/${BASE_REF}"
|
|
|
|
range="origin/${BASE_REF}..HEAD"
|
|
echo "Checking commits in range: ${range}"
|
|
|
|
# Bail out clean if PR branch is empty relative to base (no commits to check).
|
|
if ! git rev-list --no-merges "${range}" --count >/dev/null 2>&1; then
|
|
echo "No commits found in range — nothing to validate."
|
|
exit 0
|
|
fi
|
|
count=$(git rev-list --no-merges "${range}" --count)
|
|
if [ "${count}" -eq 0 ]; then
|
|
echo "No non-merge commits in range — nothing to validate."
|
|
exit 0
|
|
fi
|
|
|
|
failed=0
|
|
while IFS=$'\t' read -r sha subject; do
|
|
if printf '%s' "${subject}" | grep -Eq "${CC_REGEX}"; then
|
|
printf ' OK %s %s\n' "${sha}" "${subject}"
|
|
else
|
|
printf ' FAIL %s %s\n' "${sha}" "${subject}" >&2
|
|
failed=$((failed + 1))
|
|
fi
|
|
done < <(git log --no-merges --format='%h%x09%s' "${range}")
|
|
|
|
if [ "${failed}" -gt 0 ]; then
|
|
echo "::error::${failed} commit(s) do not match Conventional Commits format." >&2
|
|
echo "Rewrite history (git rebase -i origin/${BASE_REF}) so every commit matches:" >&2
|
|
echo " ${CC_REGEX}" >&2
|
|
exit 1
|
|
fi
|
|
echo "OK: ${count} commit(s) match Conventional Commits."
|