-
Notifications
You must be signed in to change notification settings - Fork 333
Add pre-release prerequisite checks to perform-release.sh #11024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 3 commits
bc2742a
ab3fc64
81a2733
8f51223
12b7e0e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,9 +3,11 @@ set -euo pipefail | |
|
|
||
| # Ask for confirmation before continuing the release | ||
| function confirmOrAbort() { | ||
| read -p "Do you want to continue? (y/N): " -n 1 -r | ||
| local prompt="${1:-Do you want to continue? (y/N): }" | ||
| local reply | ||
| read -p "$prompt" -n 1 -r reply | ||
| echo | ||
| if [[ ! $REPLY =~ ^[Yy]$ ]]; then | ||
| if [[ ! $reply =~ ^[Yy]$ ]]; then | ||
| echo "Aborting." | ||
| exit 1 | ||
| fi | ||
|
|
@@ -58,6 +60,30 @@ if ! git diff-index --quiet "$REMOTE/$CURRENT_BRANCH"; then | |
| fi | ||
| echo "✅ Working copy is clean and up-to-date." | ||
|
|
||
| # Check if gh CLI is available | ||
| if ! command -v gh &>/dev/null; then | ||
| echo "❌ GitHub CLI (gh) is not installed or not in PATH. Please install it to proceed." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Check gh authentication | ||
| if ! gh auth status &>/dev/null; then | ||
| echo "❌ GitHub CLI is not authenticated. Please run 'gh auth login' first." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Check push permission on this repository | ||
| if ! HAS_PUSH=$(gh api "repos/{owner}/{repo}" --jq ".permissions.push"); then | ||
| echo "❌ Failed to query repository permissions. Check your network connection and token scopes." | ||
| exit 1 | ||
| fi | ||
| if [ "$HAS_PUSH" != "true" ]; then | ||
| echo "❌ Your GitHub account does not have push (write) permission on this repository." | ||
| echo " Only release owners are allowed to push release tags." | ||
| exit 1 | ||
| fi | ||
| echo "✅ GitHub CLI authenticated with push permission." | ||
|
|
||
| # Check the git log history | ||
| LAST_RELEASE_TAG=$(git describe --tags --abbrev=0 --match='v[0-9]*.[0-9]*.[0-9]*') | ||
| echo "ℹ️ Last release version: $LAST_RELEASE_TAG" | ||
|
|
@@ -84,6 +110,108 @@ else | |
| fi | ||
| echo "ℹ️ Next release version: $NEXT_RELEASE_VERSION" | ||
|
|
||
| # Check the release tag does not already exist locally or remotely | ||
| if git rev-parse "refs/tags/$NEXT_RELEASE_VERSION" &>/dev/null; then | ||
| echo "❌ Tag '$NEXT_RELEASE_VERSION' already exists locally. Has this release already been tagged?" | ||
| exit 1 | ||
| fi | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❔ question: How is that even possible according how the next version tag is computed? |
||
| if git ls-remote --tags "$REMOTE" "refs/tags/$NEXT_RELEASE_VERSION" | grep -q .; then | ||
| echo "❌ Tag '$NEXT_RELEASE_VERSION' already exists on remote '$REMOTE'. Has this release already been tagged?" | ||
| exit 1 | ||
| fi | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❔ question: Same question. Tags are fetch just above |
||
| echo "✅ Release tag '$NEXT_RELEASE_VERSION' does not yet exist." | ||
|
|
||
| # Check that an open GitHub milestone exists for the next release version | ||
| MILESTONE_TITLE="${NEXT_RELEASE_VERSION#v}" | ||
| if ! MILESTONE_NUMBERS=$(gh api --paginate "repos/{owner}/{repo}/milestones?state=open&per_page=100" \ | ||
| --jq ".[] | select(.title == \"$MILESTONE_TITLE\") | .number"); then | ||
| echo "❌ Failed to query GitHub milestones. Check your network connection." | ||
| exit 1 | ||
| fi | ||
| if [ -z "$MILESTONE_NUMBERS" ]; then | ||
| echo "❌ No open GitHub milestone found for version '$MILESTONE_TITLE'." | ||
| echo " Please create the milestone and assign PRs to it before performing a release." | ||
| exit 1 | ||
| fi | ||
| MILESTONE_COUNT=$(printf '%s\n' "$MILESTONE_NUMBERS" | wc -l | tr -d ' ') | ||
| if [ "$MILESTONE_COUNT" -gt 1 ]; then | ||
| echo "❌ Multiple open milestones found for version '$MILESTONE_TITLE' (numbers: $(printf '%s ' "$MILESTONE_NUMBERS"))." | ||
| echo " Please resolve the duplicate milestones before performing a release." | ||
| exit 1 | ||
| fi | ||
| MILESTONE_NUMBER="$MILESTONE_NUMBERS" | ||
| echo "✅ GitHub milestone '$MILESTONE_TITLE' found (milestone #$MILESTONE_NUMBER)." | ||
|
|
||
| # Check that the milestone has no open issues or PRs | ||
| if ! OPEN_ISSUES=$(gh api "repos/{owner}/{repo}/milestones/$MILESTONE_NUMBER" \ | ||
| --jq ".open_issues"); then | ||
| echo "❌ Failed to query milestone '$MILESTONE_TITLE'. Check your network connection." | ||
| exit 1 | ||
| fi | ||
| if [ -z "$OPEN_ISSUES" ] || ! [[ "$OPEN_ISSUES" =~ ^[0-9]+$ ]]; then | ||
| echo "❌ Unexpected response when querying open issue count for milestone '$MILESTONE_TITLE': '$OPEN_ISSUES'." | ||
| exit 1 | ||
| fi | ||
| if [ "$OPEN_ISSUES" -gt 0 ]; then | ||
| echo "❌ Milestone '$MILESTONE_TITLE' still has $OPEN_ISSUES open issue(s) or PR(s):" | ||
| gh api "repos/{owner}/{repo}/issues?milestone=$MILESTONE_NUMBER&state=open&per_page=100" \ | ||
| --jq '.[] | " #\(.number) \(.title)"' || true | ||
| echo " All PRs must be merged before tagging a release." | ||
| exit 1 | ||
| fi | ||
| echo "✅ All issues and PRs in milestone '$MILESTONE_TITLE' are closed." | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔨 issue: This is wrong. This is expected to have open issue on milestone. |
||
|
|
||
| # Check that all closed PRs in the milestone carry the required labels. | ||
| # Required: (comp:* or inst:*) AND (type:*), OR 'tag: no release notes'. | ||
|
Comment on lines
+173
to
+176
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❔ question: What's the point? It's a duplicate of the PR checks workflow. |
||
| if ! NONCOMPLIANT=$(gh api --paginate \ | ||
| "repos/{owner}/{repo}/issues?milestone=$MILESTONE_NUMBER&state=closed&per_page=100" \ | ||
| --jq '[.[] | select(.pull_request != null) | | ||
| select( | ||
| ((.labels | map(.name) | map(. == "tag: no release notes") | any) | not) and | ||
| ( | ||
| ((.labels | map(.name) | map(startswith("comp:") or startswith("inst:")) | any) | not) or | ||
| ((.labels | map(.name) | map(startswith("type:")) | any) | not) | ||
| ) | ||
| ) | " #\(.number) \(.title)"] | .[]'); then | ||
| echo "❌ Failed to query milestone PRs. Check your network connection." | ||
| exit 1 | ||
| fi | ||
| if [ -n "$NONCOMPLIANT" ]; then | ||
| echo "⚠️ The following PRs in milestone '$MILESTONE_TITLE' are missing required labels:" | ||
| echo "$NONCOMPLIANT" | ||
| echo " Each PR needs (a 'comp:' or 'inst:' label) AND (a 'type:' label), or 'tag: no release notes'." | ||
| confirmOrAbort "Continue despite missing labels? (y/N): " | ||
| else | ||
| echo "✅ All PRs in milestone '$MILESTONE_TITLE' have required labels." | ||
| fi | ||
|
|
||
| # Check GPG signing key is configured and available (release tags are signed with -s) | ||
| SIGNING_KEY=$(git config --get user.signingkey 2>/dev/null || true) | ||
| if [ -n "$SIGNING_KEY" ]; then | ||
| if ! gpg --list-secret-keys "$SIGNING_KEY" &>/dev/null; then | ||
| echo "❌ GPG signing key '$SIGNING_KEY' is not available in the keyring (expired, revoked, or deleted)." | ||
| echo " Please reconfigure your signing key with: git config user.signingkey <KEY_ID>" | ||
| exit 1 | ||
| fi | ||
| else | ||
| GIT_EMAIL=$(git config --get user.email 2>/dev/null || true) | ||
| if [ -z "$GIT_EMAIL" ] || ! gpg --list-secret-keys "$GIT_EMAIL" &>/dev/null; then | ||
| echo "❌ No GPG signing key configured. Release tags must be signed." | ||
| echo " Configure one with: git config user.signingkey <KEY_ID>" | ||
| exit 1 | ||
| fi | ||
| fi | ||
| echo "✅ GPG signing key is configured." | ||
|
|
||
| # For minor releases: require explicit acknowledgment of manual pre-cut verification steps | ||
| if [ "$MINOR_RELEASE" = true ]; then | ||
| echo "" | ||
| echo "ℹ️ Minor release — manual pre-cut verification required (Steps 1–2 of the release process)." | ||
| confirmOrAbort "Step 1: Have you reviewed the APM Performance SDK SLO dashboard and found no regressions? (y/N): " | ||
| confirmOrAbort "Step 2: Have you reviewed the Test Optimization Performance Dashboard and found no increased overhead? (y/N): " | ||
| echo "" | ||
| fi | ||
|
|
||
| # Create and push the release tag | ||
| echo "ℹ️ The release tag will be created and pushed. No abort is possible after this point." | ||
| confirmOrAbort | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 suggestion: This should check the fact that you're part of the release owner teams.
Otherwise, the release must be done by a release owner.