From 54ae25effa41225d73eb78c801cc96b29ddc9713 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Wed, 30 Jul 2025 17:37:32 -0400 Subject: [PATCH 1/8] chore: [M3-10396] - Release label automation --- .github/workflows/release-labeling.yml | 110 +++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 .github/workflows/release-labeling.yml diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml new file mode 100644 index 00000000000..9327ae4d689 --- /dev/null +++ b/.github/workflows/release-labeling.yml @@ -0,0 +1,110 @@ +name: Release Labeler + +permissions: + pull-requests: write + issues: write + +on: + pull_request: + types: [opened] + branches: + - develop + +jobs: + add-release-label: + name: Add Release Label + runs-on: ubuntu-latest + steps: + - name: Calculate next release date + id: calculate_date + run: | + # Function to adjust date to next Tuesday if needed + adjust_to_tuesday() { + local input_date="$1" + local day_of_week=$(date -d "$input_date" +%u) + if [ $day_of_week -ne 2 ]; then + local days_to_add=$(( (2 - $day_of_week + 7) % 7 )) + date -d "$input_date + $days_to_add days" +%Y-%m-%d + else + echo "$input_date" + fi + } + + RELEASE_START="${{ vars.RELEASE_START_DATE || '2025-08-12' }}" + + FREEZE_START_1="${{ vars.FREEZE_START_1 || '2025-12-02' }}" + FREEZE_END_1="${{ vars.FREEZE_END_1 || '2025-12-15' }}" + FREEZE_START_2="${{ vars.FREEZE_START_2 || '2025-12-30' }}" + FREEZE_END_2="${{ vars.FREEZE_END_2 || '2026-01-06' }}" + + TODAY=$(date +%Y-%m-%d) + + # Calculate which release cycle we're in (releases every 14 days) + DAYS_SINCE_START=$(( ($(date -d "$TODAY" +%s) - $(date -d "$RELEASE_START" +%s)) / 86400 )) + + if [ $DAYS_SINCE_START -lt 0 ]; then + NEXT_RELEASE_DATE="$RELEASE_START" + else + CYCLES_PASSED=$(( $DAYS_SINCE_START / 14 )) + NEXT_CYCLE=$(( $CYCLES_PASSED + 1 )) + NEXT_RELEASE_DATE=$(date -d "$RELEASE_START + $(( $NEXT_CYCLE * 14 )) days" +%Y-%m-%d) + fi + + # Skip freeze periods - if calculated date falls during freeze, + # jump to first Tuesday after freeze ends + MAX_ITERATIONS=3 # Safety guard - worst case is 2 freeze periods + ITERATION=0 + while [ $ITERATION -lt $MAX_ITERATIONS ]; do + RELEASE_TIMESTAMP=$(date -d "$NEXT_RELEASE_DATE" +%s) + FREEZE1_START_TS=$(date -d "$FREEZE_START_1" +%s) + FREEZE1_END_TS=$(date -d "$FREEZE_END_1" +%s) + FREEZE2_START_TS=$(date -d "$FREEZE_START_2" +%s) + FREEZE2_END_TS=$(date -d "$FREEZE_END_2" +%s) + + # Check if calculated release date falls during December freeze (Dec 2-15) + if [ $RELEASE_TIMESTAMP -ge $FREEZE1_START_TS ] && [ $RELEASE_TIMESTAMP -le $FREEZE1_END_TS ]; then + # Skip freeze period: move to day after freeze ends, then find next Tuesday + NEXT_RELEASE_DATE=$(date -d "$FREEZE_END_1 + 1 day" +%Y-%m-%d) + NEXT_RELEASE_DATE=$(adjust_to_tuesday "$NEXT_RELEASE_DATE") + ITERATION=$((ITERATION + 1)) + continue + fi + + # Check if calculated release date falls during year-end freeze (Dec 30-Jan 6) + if [ $RELEASE_TIMESTAMP -ge $FREEZE2_START_TS ] && [ $RELEASE_TIMESTAMP -le $FREEZE2_END_TS ]; then + # Skip freeze period: move to day after freeze ends, then find next Tuesday + NEXT_RELEASE_DATE=$(date -d "$FREEZE_END_2 + 1 day" +%Y-%m-%d) + NEXT_RELEASE_DATE=$(adjust_to_tuesday "$NEXT_RELEASE_DATE") + ITERATION=$((ITERATION + 1)) + continue + fi + + break + done + + if [ $ITERATION -eq $MAX_ITERATIONS ]; then + echo "Error: Too many freeze period adjustments" >&2 + exit 1 + fi + + if ! date -d "$NEXT_RELEASE_DATE" >/dev/null 2>&1; then + echo "Error: Invalid date calculated" >&2 + exit 1 + fi + + echo "date=$NEXT_RELEASE_DATE" >> $GITHUB_OUTPUT + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Create and add release label to PR + run: | + LABEL_NAME="release:${{ steps.calculate_date.outputs.date }}" + + # Create label if it doesn't exist (will fail silently if it already exists) + gh label create "$LABEL_NAME" --description "Release scheduled for ${{ steps.calculate_date.outputs.date }}" --color "0e8a16" || true + + # Add label to PR + gh pr edit ${{ github.event.pull_request.number }} --add-label "$LABEL_NAME" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 178bb776dbfec0b5a430acbb90fb81ad58e59d54 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Wed, 30 Jul 2025 18:03:28 -0400 Subject: [PATCH 2/8] account for release candidate --- .github/workflows/release-labeling.yml | 53 ++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index 9327ae4d689..45632bba854 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -30,6 +30,13 @@ jobs: fi } + # Function to calculate release candidate cutoff date (Wednesday before release) + get_release_candidate_date() { + local release_date="$1" + # Go back 6 days from Tuesday to get the previous Wednesday + date -d "$release_date - 6 days" +%Y-%m-%d + } + RELEASE_START="${{ vars.RELEASE_START_DATE || '2025-08-12' }}" FREEZE_START_1="${{ vars.FREEZE_START_1 || '2025-12-02' }}" @@ -87,6 +94,44 @@ jobs: exit 1 fi + # Check if today is the release candidate cutoff date (Wednesday before release) + RELEASE_CANDIDATE_DATE=$(get_release_candidate_date "$NEXT_RELEASE_DATE") + + if [ "$TODAY" = "$RELEASE_CANDIDATE_DATE" ]; then + # If today is the release candidate cutoff date, use the next release cycle + NEXT_RELEASE_DATE=$(date -d "$NEXT_RELEASE_DATE + 14 days" +%Y-%m-%d) + + # Re-check freeze periods for the next release date + ITERATION=0 + while [ $ITERATION -lt $MAX_ITERATIONS ]; do + RELEASE_TIMESTAMP=$(date -d "$NEXT_RELEASE_DATE" +%s) + FREEZE1_START_TS=$(date -d "$FREEZE_START_1" +%s) + FREEZE1_END_TS=$(date -d "$FREEZE_END_1" +%s) + FREEZE2_START_TS=$(date -d "$FREEZE_START_2" +%s) + FREEZE2_END_TS=$(date -d "$FREEZE_END_2" +%s) + + # Check if calculated release date falls during December freeze (Dec 2-15) + if [ $RELEASE_TIMESTAMP -ge $FREEZE1_START_TS ] && [ $RELEASE_TIMESTAMP -le $FREEZE1_END_TS ]; then + # Skip freeze period: move to day after freeze ends, then find next Tuesday + NEXT_RELEASE_DATE=$(date -d "$FREEZE_END_1 + 1 day" +%Y-%m-%d) + NEXT_RELEASE_DATE=$(adjust_to_tuesday "$NEXT_RELEASE_DATE") + ITERATION=$((ITERATION + 1)) + continue + fi + + # Check if calculated release date falls during year-end freeze (Dec 30-Jan 6) + if [ $RELEASE_TIMESTAMP -ge $FREEZE2_START_TS ] && [ $RELEASE_TIMESTAMP -le $FREEZE2_END_TS ]; then + # Skip freeze period: move to day after freeze ends, then find next Tuesday + NEXT_RELEASE_DATE=$(date -d "$FREEZE_END_2 + 1 day" +%Y-%m-%d) + NEXT_RELEASE_DATE=$(adjust_to_tuesday "$NEXT_RELEASE_DATE") + ITERATION=$((ITERATION + 1)) + continue + fi + + break + done + fi + if ! date -d "$NEXT_RELEASE_DATE" >/dev/null 2>&1; then echo "Error: Invalid date calculated" >&2 exit 1 @@ -101,10 +146,10 @@ jobs: run: | LABEL_NAME="release:${{ steps.calculate_date.outputs.date }}" - # Create label if it doesn't exist (will fail silently if it already exists) - gh label create "$LABEL_NAME" --description "Release scheduled for ${{ steps.calculate_date.outputs.date }}" --color "0e8a16" || true + # Try to create label if it doesn't exist (will fail silently if it already exists or no permissions) + gh label create "$LABEL_NAME" --description "Release scheduled for ${{ steps.calculate_date.outputs.date }}" --color "0e8a16" || echo "Could not create label (may already exist or insufficient permissions)" - # Add label to PR - gh pr edit ${{ github.event.pull_request.number }} --add-label "$LABEL_NAME" + # Try to add label to PR (will fail if label doesn't exist or no permissions) + gh pr edit ${{ github.event.pull_request.number }} --add-label "$LABEL_NAME" || echo "Could not add label to PR (label may not exist or insufficient permissions)" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From bc984b8c8c949f3b5965292114831661ace2d201 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 31 Jul 2025 08:58:33 -0400 Subject: [PATCH 3/8] review changes --- .github/workflows/release-labeling.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index 45632bba854..06627094e06 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -2,7 +2,6 @@ name: Release Labeler permissions: pull-requests: write - issues: write on: pull_request: @@ -139,9 +138,6 @@ jobs: echo "date=$NEXT_RELEASE_DATE" >> $GITHUB_OUTPUT - - name: Checkout repository - uses: actions/checkout@v4 - - name: Create and add release label to PR run: | LABEL_NAME="release:${{ steps.calculate_date.outputs.date }}" From 40b18edced540092f28d07ce94319f1a7f1b45ea Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 31 Jul 2025 09:16:30 -0400 Subject: [PATCH 4/8] switch to pull_request_target --- .github/workflows/release-labeling.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index 06627094e06..e4150948fff 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -1,10 +1,17 @@ name: Release Labeler +# Security note: This workflow uses pull_request_target to work with forked repositories. +# This is safe because: +# 1. We do NOT checkout the repository (no untrusted code execution) +# 2. We only use GitHub CLI commands to manage labels +# 3. We only read from GitHub context variables, not from PR content +# 4. All date calculations are done with trusted shell commands + permissions: pull-requests: write on: - pull_request: + pull_request_target: types: [opened] branches: - develop From 7c1c47d01e03e820e0afa2aaeea6763ce7b17509 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 31 Jul 2025 09:20:54 -0400 Subject: [PATCH 5/8] Adjust comments --- .github/workflows/release-labeling.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index e4150948fff..9f76093c041 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -1,9 +1,11 @@ name: Release Labeler # Security note: This workflow uses pull_request_target to work with forked repositories. +# When editing file, please read: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + # This is safe because: # 1. We do NOT checkout the repository (no untrusted code execution) -# 2. We only use GitHub CLI commands to manage labels +# 2. We only use GitHub CLI commands to manage labels, so we DO need write permissions # 3. We only read from GitHub context variables, not from PR content # 4. All date calculations are done with trusted shell commands From e50a0b80f57de9e40c83cf2a62b20a7d24a1df52 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 31 Jul 2025 09:21:49 -0400 Subject: [PATCH 6/8] Add trailing newline --- .github/workflows/release-labeling.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index 9f76093c041..544bc5c474b 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -157,4 +157,4 @@ jobs: # Try to add label to PR (will fail if label doesn't exist or no permissions) gh pr edit ${{ github.event.pull_request.number }} --add-label "$LABEL_NAME" || echo "Could not add label to PR (label may not exist or insufficient permissions)" env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 744bd18663b121b73784e005e1d439428df224de Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 31 Jul 2025 09:23:17 -0400 Subject: [PATCH 7/8] Add trailing newline --- .github/workflows/release-labeling.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index 544bc5c474b..b867689309e 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -158,3 +158,4 @@ jobs: gh pr edit ${{ github.event.pull_request.number }} --add-label "$LABEL_NAME" || echo "Could not add label to PR (label may not exist or insufficient permissions)" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + From 154acb50224357613a4c0969e308f29c64995a57 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 31 Jul 2025 10:12:04 -0400 Subject: [PATCH 8/8] Don't run the action for drafts --- .github/workflows/release-labeling.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-labeling.yml b/.github/workflows/release-labeling.yml index b867689309e..752094561c4 100644 --- a/.github/workflows/release-labeling.yml +++ b/.github/workflows/release-labeling.yml @@ -14,7 +14,7 @@ permissions: on: pull_request_target: - types: [opened] + types: [opened, ready_for_review] # ready_for_review: run when draft PRs are marked ready branches: - develop @@ -22,6 +22,7 @@ jobs: add-release-label: name: Add Release Label runs-on: ubuntu-latest + if: github.event.pull_request.draft == false steps: - name: Calculate next release date id: calculate_date