Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ jobs:
- name: Run release-please
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
target-branch: main
token: ${{ steps.get_token.outputs.token }}
33 changes: 33 additions & 0 deletions .github/workflows/validate-release-please-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Validate release-please config

on:
pull_request:
branches:
- main
paths:
- 'release-please-config.json'
- 'scripts/check-release-please-config.sh'
- '.github/workflows/validate-release-please-config.yml'
push:
branches:
- main
paths:
- 'release-please-config.json'
- 'scripts/check-release-please-config.sh'
- '.github/workflows/validate-release-please-config.yml'

permissions:
contents: read

concurrency:
group: validate-release-please-config-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
name: Validate release-please-config.json
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Run config validation
run: bash scripts/check-release-please-config.sh release-please-config.json
17 changes: 16 additions & 1 deletion .github/workflows/verify-workflow-sha256.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@ name: verify-workflow-sha256

on:
pull_request:
branches:
- main
paths:
- '.github/workflows/*.yml'
- 'release-please-config.json'
- 'scripts/compute-workflow-sha256.sh'
- '**/workflow-sha256'
push:
branches:
- main
paths:
- '.github/workflows/*.yml'
- 'release-please-config.json'
- 'scripts/compute-workflow-sha256.sh'
- '**/workflow-sha256'

permissions:
contents: read

concurrency:
group: verify-workflow-sha256-${{ github.event.pull_request.number }}
group: verify-workflow-sha256-${{ github.ref }}
cancel-in-progress: true

jobs:
Expand Down
34 changes: 14 additions & 20 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,44 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"separate-pull-requests": true,
"release-type": "simple",
"tag-separator": "-",
"always-update": true,
"bump-patch-for-minor-pre-major": true,
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"include-component-in-tag": true,
"include-v-in-tag": true,
"release-type": "simple",
"separate-pull-requests": true,
"tag-separator": "-",
"packages": {
"conventional-commits": {
"version-file": "conventional-commits/version.txt",
"changelog-path": "conventional-commits/CHANGELOG.md",
"component": "conventional-commits",
"extra-label": "conventional-commits"
},
"docker-build": {
"version-file": "docker-build/version.txt",
"changelog-path": "docker-build/CHANGELOG.md",
"component": "docker-build",
"extra-label": "docker-build"
},
"docker-build-cloud": {
"version-file": "docker-build-cloud/version.txt",
"changelog-path": "docker-build-cloud/CHANGELOG.md",
"component": "docker-build-cloud",
"extra-label": "docker-build-cloud"
},
"propose-safe-multisig-tx": {
"version-file": "propose-safe-multisig-tx/version.txt",
"changelog-path": "propose-safe-multisig-tx/CHANGELOG.md",
"component": "propose-safe-multisig-tx",
"extra-label": "propose-safe-multisig-tx"
},
"publish-npm": {
"version-file": "publish-npm/version.txt",
"changelog-path": "publish-npm/CHANGELOG.md",
"component": "publish-npm",
"extra-label": "publish-npm"
},
"release-please": {
"version-file": "release-please/version.txt",
"changelog-path": "release-please/CHANGELOG.md",
"component": "release-please",
"extra-label": "release-please"
},
"rust-build": {
"version-file": "rust-build/version.txt",
"changelog-path": "rust-build/CHANGELOG.md",
"component": "rust-build",
"extra-label": "rust-build"
},
"stale": {
"version-file": "stale/version.txt",
"changelog-path": "stale/CHANGELOG.md",
"component": "stale",
"extra-label": "stale"
}
}
Expand Down
115 changes: 115 additions & 0 deletions scripts/check-release-please-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
# Validates release-please-config.json so manifest releases stay attribution-correct.
#
# The two things that must never regress:
# 1. Every package must declare `component` (or `package-name`); without it
# release-please resolves an empty component, which silently breaks tag
# matching, per-component branch names and per-package pull requests.
# 2. Key ordering ($schema, then lexicographic, packages last) for deterministic diffs.
#
# Usage (works locally or in CI):
# bash scripts/check-release-please-config.sh [path/to/release-please-config.json]
#
# Depends on jq; errors are accumulated and reported all at once.

set -uo pipefail

CONFIG="${1:-release-please-config.json}"

say_err() { echo "error: $*" >&2; }
say_warn() { echo "warning: $*" >&2; }

failures=0

if ! command -v jq >/dev/null 2>&1; then
say_err "jq is required to run this check"
exit 1
fi

if [[ ! -f "$CONFIG" ]]; then
say_err "config file not found: $CONFIG"
exit 1
fi

# --- JSON validity and object shape ---
if ! jq -e 'type == "object"' "$CONFIG" >/dev/null 2>&1; then
say_err "$CONFIG is not a valid JSON object"
exit 1
fi

# --- root key ordering: $schema first, lexicographic, packages last ---
mapfile -t root_keys < <(jq -r 'keys_unsorted[]' "$CONFIG")

if [[ "${root_keys[0]-}" != '$schema' ]]; then
say_err "root key order: first key must be \"\$schema\" (got \"${root_keys[0]-<none>}\")"
((failures += 1))
fi

if [[ "${root_keys[-1]-}" != "packages" ]]; then
say_err "root key order: \"packages\" must be the last key (got \"${root_keys[-1]-<none>}\")"
((failures += 1))
fi

mid_keys=()
for k in "${root_keys[@]}"; do
[[ "$k" == '$schema' || "$k" == 'packages' ]] && continue
mid_keys+=("$k")
done
mapfile -t mid_sorted < <(printf '%s\n' "${mid_keys[@]}" | sort)
if [[ "${mid_keys[*]:-}" != "${mid_sorted[*]:-}" ]]; then
say_err "root key order: keys after \"\$schema\" must be lexicographic (got: ${mid_keys[*]:-none})"
((failures += 1))
fi

# --- packages: identity + per-package ordering ---
if ! jq -e '.packages | type == "object"' "$CONFIG" >/dev/null 2>&1; then
say_err "key \"packages\" must be an object"
((failures += 1))
else
while IFS= read -r pkg; do
if ! jq -e --arg p "$pkg" '.packages[$p] | type == "object" and (has("component") or has("package-name"))' "$CONFIG" >/dev/null 2>&1; then
say_err "packages.$pkg must define \"component\" (or \"package-name\"): required for tag matching, per-component branches and per-package pull requests"
((failures += 1))
fi

mapfile -t pkg_keys < <(jq -r --arg p "$pkg" '.packages[$p] | keys_unsorted[]' "$CONFIG")
mapfile -t pkg_sorted < <(printf '%s\n' "${pkg_keys[@]}" | sort)
if [[ "${pkg_keys[*]:-}" != "${pkg_sorted[*]:-}" ]]; then
say_err "packages.$pkg: keys must be lexicographic (got: ${pkg_keys[*]:-none})"
((failures += 1))
fi

# version-file / changelog-path resolve relative to the package dir; a value
# repeating the dir name produces a doubled path (e.g. docker-build/docker-build/CHANGELOG.md)
for field in version-file changelog-path; do
if jq -e --arg p "$pkg" --arg f "$field" '.packages[$p] | has($f)' "$CONFIG" >/dev/null 2>&1; then
value=$(jq -r --arg p "$pkg" --arg f "$field" '.packages[$p][$f]' "$CONFIG")
case "$field" in
version-file) default_name="version.txt" ;;
changelog-path) default_name="CHANGELOG.md" ;;
esac
if [[ "$value" == *"$(basename "$pkg")"* ]]; then
say_warn "packages.$pkg.$field=\"$value\" is resolved relative to the package dir; use a bare filename (or omit it to use the default <package>/$default_name)"
fi
fi
done
done < <(jq -r '.packages | keys[]' "$CONFIG")
fi

# --- tag format: must resolve to <component>-vX.Y.Z tags ---
# Existing releases use e.g. docker-build-v2.5.2 / docker-build-cloud-v1.1.1; any
# deviation silently orphans tag matching and walks all history in the next run.
tag_include_component=$(jq -r 'if has("include-component-in-tag") then ."include-component-in-tag" else true end' "$CONFIG")
tag_include_v=$(jq -r 'if has("include-v-in-tag") then ."include-v-in-tag" else true end' "$CONFIG")
tag_separator=$(jq -r 'if has("tag-separator") then ."tag-separator" else "-" end' "$CONFIG")
if [[ "$tag_include_component" != "true" || "$tag_include_v" != "true" || "$tag_separator" != "-" ]]; then
say_err "tags must resolve to <component>-vX.Y.Z: keep include-component-in-tag=true, include-v-in-tag=true and tag-separator=\"-\" (effective: component=$tag_include_component, v-prefix=$tag_include_v, separator=\"$tag_separator\")"
((failures += 1))
fi

if ((failures > 0)); then
echo "release-please-config validation FAILED ($failures error(s))" >&2
exit 1
fi

echo "release-please-config validation OK"
39 changes: 36 additions & 3 deletions scripts/compute-workflow-sha256.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
set -uo pipefail

cd "$(dirname "$0")/.."

Expand All @@ -9,21 +9,49 @@ if [[ "${1:-}" == "--check" ]]; then
fi

outdated=false
errors=0

say_err() { echo "error: $*" >&2; }

if ! command -v jq >/dev/null 2>&1; then
say_err "jq is required to run this script"
exit 1
fi

if command -v sha256sum >/dev/null 2>&1; then
sha_cmd=(sha256sum)
elif command -v shasum >/dev/null 2>&1; then
sha_cmd=(shasum -a 256)
else
say_err "neither sha256sum nor shasum is available"
exit 1
fi

mapfile -t package_names < <(jq -r '.packages | keys[]' release-please-config.json)

for name in "${package_names[@]}"; do
workflow_file=".github/workflows/$name.yml"
sha_file="$name/workflow-sha256"
computed_sha="$(sha256sum "$workflow_file" | awk '{print $1}')"

if [[ ! -f "$workflow_file" ]]; then
say_err "workflow file missing: $workflow_file"
((errors += 1))
continue
fi

computed_sha="$("${sha_cmd[@]}" "$workflow_file" | awk '{print $1}')"

if [[ "$check" == true ]]; then
if [[ ! -f "$sha_file" ]] || [[ "$(cat "$sha_file")" != "$computed_sha" ]]; then
echo "outdated \`$sha_file\`" >&2
outdated=true
fi
else
mkdir -p "$name"
if ! mkdir -p "$name"; then
say_err "could not create package dir: $name"
((errors += 1))
continue
fi
echo "$computed_sha" > "$sha_file"
echo "$sha_file: $computed_sha" >&2
fi
Expand All @@ -33,3 +61,8 @@ if [[ "$check" == true ]] && [[ "$outdated" == true ]]; then
echo "found outdated workflow sha256: execute \`compute-workflow-sha256.sh\` to refresh workflows sha256"
exit 1
fi

if ((errors > 0)); then
say_err "$errors error(s)"
exit 1
fi
Loading