diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml index 4e7aaae..cbf0f10 100644 --- a/.github/workflows/cleanup.yml +++ b/.github/workflows/cleanup.yml @@ -33,5 +33,11 @@ jobs: exit 0 fi + # Safety: validate branch name matches expected Gitflow patterns + if [[ ! "$BRANCH" =~ ^(feature|release|hotfix|merge)/ ]]; then + echo "Skipping deletion — branch '$BRANCH' does not match a Gitflow pattern." + exit 0 + fi + echo "Deleting branch '$BRANCH'..." gh api -X DELETE "repos/$REPO/git/refs/heads/$BRANCH" || echo "Branch '$BRANCH' may have already been deleted." diff --git a/.github/workflows/feature.yml b/.github/workflows/feature.yml index 79017e2..963f1c6 100644 --- a/.github/workflows/feature.yml +++ b/.github/workflows/feature.yml @@ -38,12 +38,17 @@ jobs: name: Run Checks runs-on: ubuntu-latest needs: validate - if: needs.validate.outputs.is_feature == 'true' steps: + - name: Skip non-feature branches + if: needs.validate.outputs.is_feature != 'true' + run: echo "Not a feature branch — skipping CI checks." + - name: Checkout code + if: needs.validate.outputs.is_feature == 'true' uses: actions/checkout@v4 - name: Placeholder - Add your build/test/lint steps here + if: needs.validate.outputs.is_feature == 'true' run: | echo "===========================================" echo " Add your CI steps to this job." diff --git a/.github/workflows/hotfix.yml b/.github/workflows/hotfix.yml index bccc556..89bb6d7 100644 --- a/.github/workflows/hotfix.yml +++ b/.github/workflows/hotfix.yml @@ -33,7 +33,7 @@ jobs: BRANCH: ${{ github.head_ref }} run: | VERSION="${BRANCH#hotfix/}" - SEMVER_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' + SEMVER_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+$' echo "Hotfix version: $VERSION" @@ -90,6 +90,7 @@ jobs: env: BRANCH: ${{ github.head_ref }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} run: | VERSION="${BRANCH#hotfix/}" TAG="v${VERSION}" @@ -109,26 +110,38 @@ jobs: echo "Branch '$MERGE_BRANCH_DEV' already exists, skipping." else echo "Creating back-merge branch '$MERGE_BRANCH_DEV'..." - git checkout -b "$MERGE_BRANCH_DEV" main + git checkout -b "$MERGE_BRANCH_DEV" origin/main git push origin "$MERGE_BRANCH_DEV" fi - EXISTING_PR=$(gh pr list --base develop --head "$MERGE_BRANCH_DEV" --json number --jq '.[0].number' || true) - if [[ -n "$EXISTING_PR" ]]; then + EXISTING_PR=$(gh pr list --repo "$REPO" --base develop --head "$MERGE_BRANCH_DEV" --json number --jq '.[0].number' 2>/dev/null || true) + if [[ -n "$EXISTING_PR" && "$EXISTING_PR" != "null" ]]; then echo "Back-merge PR #$EXISTING_PR to develop already exists, skipping." else echo "Creating back-merge PR to develop..." - gh pr create \ + if PR_URL=$(gh pr create \ + --repo "$REPO" \ --base develop \ --head "$MERGE_BRANCH_DEV" \ --title "Merge hotfix $TAG into develop" \ - --body "Automated back-merge of hotfix $TAG into develop. Resolve any conflicts and merge." + --body "Automated back-merge of hotfix $TAG into develop. Resolve any conflicts and merge." 2>&1); then + echo "Created back-merge PR: $PR_URL" + else + echo "::warning::Failed to create back-merge PR to develop: $PR_URL" + echo "Create it manually: https://github.com/$REPO/compare/develop...$MERGE_BRANCH_DEV" + fi fi # Detect active release branches and back-merge to them RELEASE_BRANCHES=$(git for-each-ref --format='%(refname:short)' refs/remotes/origin/release/ | sed 's|^origin/||' || true) for RELEASE in $RELEASE_BRANCHES; do + # Skip release branches that are already merged into main + if git merge-base --is-ancestor "origin/$RELEASE" origin/main 2>/dev/null; then + echo "Branch '$RELEASE' is already merged into main, skipping." + continue + fi + RELEASE_NAME=$(echo "$RELEASE" | tr '/' '-') MERGE_BRANCH_REL="merge/hotfix-${TAG}-to-${RELEASE_NAME}" @@ -136,19 +149,25 @@ jobs: echo "Branch '$MERGE_BRANCH_REL' already exists, skipping." else echo "Creating back-merge branch '$MERGE_BRANCH_REL' for '$RELEASE'..." - git checkout -b "$MERGE_BRANCH_REL" main + git checkout -b "$MERGE_BRANCH_REL" origin/main git push origin "$MERGE_BRANCH_REL" fi - EXISTING_REL_PR=$(gh pr list --base "$RELEASE" --head "$MERGE_BRANCH_REL" --json number --jq '.[0].number' || true) - if [[ -n "$EXISTING_REL_PR" ]]; then + EXISTING_REL_PR=$(gh pr list --repo "$REPO" --base "$RELEASE" --head "$MERGE_BRANCH_REL" --json number --jq '.[0].number' 2>/dev/null || true) + if [[ -n "$EXISTING_REL_PR" && "$EXISTING_REL_PR" != "null" ]]; then echo "Back-merge PR #$EXISTING_REL_PR to '$RELEASE' already exists, skipping." else echo "Creating back-merge PR to '$RELEASE'..." - gh pr create \ + if PR_URL=$(gh pr create \ + --repo "$REPO" \ --base "$RELEASE" \ --head "$MERGE_BRANCH_REL" \ --title "Merge hotfix $TAG into $RELEASE" \ - --body "Automated back-merge of hotfix $TAG into $RELEASE. Resolve any conflicts and merge." + --body "Automated back-merge of hotfix $TAG into $RELEASE. Resolve any conflicts and merge." 2>&1); then + echo "Created back-merge PR: $PR_URL" + else + echo "::warning::Failed to create back-merge PR to '$RELEASE': $PR_URL" + echo "Create it manually: https://github.com/$REPO/compare/$RELEASE...$MERGE_BRANCH_REL" + fi fi done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc9662b..a4008ee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: BRANCH: ${{ github.head_ref }} run: | VERSION="${BRANCH#release/}" - SEMVER_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' + SEMVER_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+$' echo "Release version: $VERSION" diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index 6b5ada0..13a72c9 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -49,6 +49,12 @@ jobs: PREV_TAG: ${{ steps.meta.outputs.previous_tag }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + # Skip if release already exists (idempotent on re-run) + if gh release view "$TAG" &>/dev/null; then + echo "Release '$TAG' already exists, skipping." + exit 0 + fi + FLAGS=(--generate-notes) if [[ "$PRERELEASE" == "true" ]]; then FLAGS+=(--prerelease) diff --git a/scripts/apply-rulesets.sh b/scripts/apply-rulesets.sh index 01d702b..6f1d367 100755 --- a/scripts/apply-rulesets.sh +++ b/scripts/apply-rulesets.sh @@ -140,8 +140,15 @@ done RELEASE_FILE="$RULESETS_DIR/release-branches.json" if [[ -f "$RELEASE_FILE" ]]; then echo "" - read -rp "Apply release branch protection? (recommended) [Y/n]: " APPLY_RELEASE - APPLY_RELEASE="${APPLY_RELEASE:-Y}" + if [[ -t 0 ]]; then + # Interactive mode: prompt the user + read -rp "Apply release branch protection? (recommended) [Y/n]: " APPLY_RELEASE + APPLY_RELEASE="${APPLY_RELEASE:-Y}" + else + # Non-interactive mode (piped/CI): apply by default + APPLY_RELEASE="Y" + echo "Non-interactive mode detected — applying release branch protection by default." + fi if [[ "$APPLY_RELEASE" =~ ^[Yy]$ ]]; then apply_ruleset "$RELEASE_FILE" || ERRORS=$((ERRORS + 1)) else