diff options
Diffstat (limited to 'tasks/signoff')
| -rwxr-xr-x | tasks/signoff | 155 | +155 −0 |
1 files changed, 155 insertions, 0 deletions
diff --git a/tasks/signoff b/tasks/signoff new file mode 100755 --- /dev/null +++ b/tasks/signoff @@ -0,0 +1,155 @@ +#!/bin/sh -eu +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: 0BSD +#MISE description="Verify contributor identities and CLA acceptance trailers" + +root=$(git rev-parse --show-toplevel) +cd "$root" + +task_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +approved_emails_file="$task_dir/../config/signoff-approved-emails" +unsupported_commits_file="$task_dir/../config/cla-unsupported-commits" + +if [ ! -f CLA.md ]; then + echo "CLA.md is missing" >&2 + exit 1 +fi + +head_cla_version_count=$(grep -c '^Version ' CLA.md || :) +if [ "$head_cla_version_count" -ne 1 ]; then + echo "CLA.md must declare exactly one version" >&2 + exit 1 +fi + +head_cla_version=$(sed -n 's/^Version //p' CLA.md) +if [ -z "$head_cla_version" ]; then + echo "CLA.md declares an empty version" >&2 + exit 1 +fi + +is_approved_email() { + grep -v '^[[:space:]]*#' "$approved_emails_file" | + grep -Fqx -- "$1" +} + +is_unsupported_commit() { + grep -v '^[[:space:]]*#' "$unsupported_commits_file" | + grep -Fqx -- "$1" +} + +check_identity() { + identity=$1 + role=$2 + email=$(printf '%s\n' "$identity" | sed -n 's/^.*<\([^<>]*\)>$/\1/p') + + if [ -z "$email" ]; then + echo "Commit $short_sha has an invalid $role identity: $identity" + bad=1 + return + fi + + if is_approved_email "$email"; then + return + fi + + requires_cla=1 + if ! printf '%s\n' "$signoffs" | grep -Fqx -- "$identity"; then + echo "Commit $short_sha $role $identity is missing a matching Signed-off-by" + bad=1 + fi +} + +if [ ! -f "$approved_emails_file" ]; then + echo "Approved email configuration is missing: $approved_emails_file" >&2 + exit 1 +fi + +if [ ! -f "$unsupported_commits_file" ]; then + echo "Unsupported commit configuration is missing: $unsupported_commits_file" >&2 + exit 1 +fi + +bad=0 +for sha in $(git log --no-merges --format=%H); do + short_sha=$(printf '%.8s' "$sha") + signoffs=$(git show -s --format='%(trailers:key=Signed-off-by,valueonly)' "$sha") + requires_cla=0 + + author=$(git show -s --format='%an <%ae>' "$sha") + check_identity "$author" "author" + + coauthors=$(git show -s --format='%(trailers:key=Co-authored-by,valueonly)' "$sha") + if [ -n "$coauthors" ]; then + old_ifs=$IFS + IFS=' +' + for coauthor in $coauthors; do + check_identity "$coauthor" "co-author" + done + IFS=$old_ifs + fi + + if [ "$requires_cla" -eq 1 ] && ! is_unsupported_commit "$sha"; then + expected_cla_version_count=$( + git show "$sha:CLA.md" 2>/dev/null | grep -c '^Version ' || : + ) + if [ "$expected_cla_version_count" -ne 1 ]; then + echo "Commit $short_sha does not contain a CLA.md with exactly one version" + bad=1 + continue + fi + + expected_cla_version=$( + git show "$sha:CLA.md" | sed -n 's/^Version //p' + ) + if [ -z "$expected_cla_version" ]; then + echo "Commit $short_sha contains an empty CLA version" + bad=1 + continue + fi + + commit_cla_version=$( + git show -s --format='%(trailers:key=CLA-Version,valueonly)' "$sha" + ) + if [ "$commit_cla_version" != "$expected_cla_version" ]; then + if [ -z "$commit_cla_version" ]; then + echo "Commit $short_sha is missing CLA-Version: $expected_cla_version" + else + echo "Commit $short_sha has invalid CLA-Version: $commit_cla_version" + echo "Expected CLA-Version: $expected_cla_version" + fi + bad=1 + fi + fi +done + +if [ "$bad" -ne 0 ]; then + echo "Every non-approved author and co-author must accept the CLA in their commit" + echo "Required trailers:" + echo " CLA-Version: <version from CLA.md>" + echo " Signed-off-by: Name <email>" + echo "See CLA.md" + exit 1 +fi + +if [ ! -f .mailmap ]; then + echo "Contributor registry .mailmap is missing" >&2 + exit 1 +fi + +missing=0 +for email in $(git log --no-merges --format='%ae%n%ce' | sort -u); do + if is_approved_email "$email"; then + continue + fi + + if ! grep -v '^[[:space:]]*#' .mailmap | grep -qF -- "<${email}>"; then + echo "Email <${email}> is not in .mailmap" + missing=1 + fi +done + +if [ "$missing" -ne 0 ]; then + echo "All authors and committers must be listed in .mailmap" + exit 1 +fi |
