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
8 changes: 4 additions & 4 deletions .github/workflows/block-non-dev-to-main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Block non-dev PRs to main
name: Block non-staging PRs to main

on:
pull_request:
Expand All @@ -8,9 +8,9 @@ jobs:
check-source:
runs-on: ubuntu-latest
steps:
- name: Block PRs not from dev
- name: Block PRs not from staging
run: |
if [[ "${{ github.head_ref }}" != "dev" ]]; then
echo "You can only merge from dev to main."
if [[ "${{ github.head_ref }}" != "staging" ]]; then
echo "You can only merge from staging to main."
exit 1
Comment on lines +11 to 15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden the head ref check to avoid command injection.

Interpolating ${{ github.head_ref }} directly inside the shell script leaves the step vulnerable to command injection if the branch name is crafted maliciously (see GitHub’s security guidance). Please pass the value through env: and reference the environment variable inside the script instead.

Apply this diff:

     steps:
-      - name: Block PRs not from staging
-        run: |
-          if [[ "${{ github.head_ref }}" != "staging" ]]; then
+      - name: Block PRs not from staging
+        env:
+          PR_HEAD_REF: ${{ github.head_ref }}
+        run: |
+          if [[ "${PR_HEAD_REF}" != "staging" ]]; then
             echo "You can only merge from staging to main."
             exit 1
           fi
🧰 Tools
🪛 actionlint (1.7.7)

12-12: "github.head_ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details

(expression)

🤖 Prompt for AI Agents
.github/workflows/block-non-dev-to-main.yml lines 11-15: the workflow
interpolates ${{ github.head_ref }} directly into the shell which can allow
command injection; change the step to pass github.head_ref via env (e.g., env:
HEAD_REF: ${{ github.head_ref }}) and inside the run script reference the safe
environment variable with proper quoting and a plain string comparison (e.g., if
[[ "$HEAD_REF" != "staging" ]]; then ... exit 1; fi) to avoid executing any
injected content.

fi
15 changes: 9 additions & 6 deletions .github/workflows/mobile-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1320,18 +1320,21 @@ jobs:
run: |
VERSION="${{ needs.bump-version.outputs.version }}"
TARGET_BRANCH="${{ inputs.bump_target_branch || 'dev' }}"
# Use version-based branch name for idempotency
BRANCH_NAME="ci/bump-mobile-version-${VERSION}"
# Add timestamp to branch name to avoid collisions
TIMESTAMP=$(date +%s%N | cut -b1-13) # Milliseconds since epoch (13 digits)
BRANCH_NAME="ci/bump-mobile-version-${VERSION}-${TIMESTAMP}"
PR_TITLE="${{ steps.platforms.outputs.pr_title }}"

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Check if branch already exists (idempotent PR creation)
if git ls-remote --heads origin "${BRANCH_NAME}" | grep -q "${BRANCH_NAME}"; then
echo "⚠️ Branch ${BRANCH_NAME} already exists"
echo "ℹ️ Version bump PR may already exist for version ${VERSION}"
# Check if a PR already exists for this version (avoid duplicate PRs)
EXISTING_PR=$(gh pr list --base "${TARGET_BRANCH}" --state open --json number,title,headRefName --jq ".[] | select(.title | contains(\"${VERSION}\")) | .number" | head -1)

if [ -n "$EXISTING_PR" ]; then
echo "⚠️ PR #${EXISTING_PR} already exists for version ${VERSION}"
echo "ℹ️ Skipping PR creation to avoid duplicates"
echo "ℹ️ Existing PR: https://github.com/${{ github.repository }}/pull/${EXISTING_PR}"
exit 0
Comment on lines +1332 to 1338
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Tighten the duplicate PR detection to avoid false skips.

Filtering on title | contains("${VERSION}") will skip creating the version bump PR whenever any other open PR to ${TARGET_BRANCH} mentions the same version in its title (e.g., a manually curated release doc), which blocks the automation. Please key off the head branch name pattern instead so we only skip genuine duplicates.

Apply this diff:

-          EXISTING_PR=$(gh pr list --base "${TARGET_BRANCH}" --state open --json number,title,headRefName --jq ".[] | select(.title | contains(\"${VERSION}\")) | .number" | head -1)
+          EXISTING_PR=$(gh pr list --base "${TARGET_BRANCH}" --state open --json number,headRefName --jq ".[] | select(.headRefName | startswith(\"ci/bump-mobile-version-${VERSION}\")) | .number" | head -1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EXISTING_PR=$(gh pr list --base "${TARGET_BRANCH}" --state open --json number,title,headRefName --jq ".[] | select(.title | contains(\"${VERSION}\")) | .number" | head -1)
if [ -n "$EXISTING_PR" ]; then
echo "⚠️ PR #${EXISTING_PR} already exists for version ${VERSION}"
echo "ℹ️ Skipping PR creation to avoid duplicates"
echo "ℹ️ Existing PR: https://github.com/${{ github.repository }}/pull/${EXISTING_PR}"
exit 0
EXISTING_PR=$(gh pr list --base "${TARGET_BRANCH}" --state open --json number,headRefName --jq ".[] | select(.headRefName | startswith(\"ci/bump-mobile-version-${VERSION}\")) | .number" | head -1)
if [ -n "$EXISTING_PR" ]; then
echo "⚠️ PR #${EXISTING_PR} already exists for version ${VERSION}"
echo "ℹ️ Skipping PR creation to avoid duplicates"
echo "ℹ️ Existing PR: https://github.com/${{ github.repository }}/pull/${EXISTING_PR}"
exit 0
🤖 Prompt for AI Agents
In .github/workflows/mobile-deploy.yml around lines 1332-1338, the script
currently detects existing PRs by checking if the PR title contains the VERSION
which leads to false positives; update the gh pr list jq filter to inspect
headRefName instead (e.g., select(.headRefName | contains("${VERSION}")) or
match a specific branch-name pattern your workflow uses) so the check only skips
when an open PR was created from a branch that actually corresponds to this
version; replace the title-based select with a headRefName-based select and keep
the rest of the logic (echo and exit) unchanged.

fi

Expand Down
38 changes: 10 additions & 28 deletions .github/workflows/release-calendar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ name: Release Calendar
# Creates release PRs on a schedule or manually via workflow_dispatch.
#
# HOW IT WORKS:
# 1. Creates snapshot branches (release/staging-YYYY-MM-DD or release/production-YYYY-MM-DD)
# 2. Opens PRs from these branches (NOT directly from dev/staging)
# 3. New commits to dev/staging won't auto-update the PR - you control what's released
# 1. Dev → Staging: Creates snapshot branch (release/staging-YYYY-MM-DD) to prevent PR drift
# 2. Staging → Main: Opens PR directly from staging (no feature branch needed)
# 3. New commits to dev won't auto-update staging PRs - you control what's released
#
# SCHEDULE:
# • Friday 17:00 UTC (10am PT): Creates release/staging-* branch from dev → staging PR
# • Sunday 17:00 UTC (10am PT): Creates release/production-* branch from staging → main PR
# • Sunday 17:00 UTC (10am PT): Creates staging → main PR (direct from staging)
#
# MANUAL TRIGGER:
# Run via workflow_dispatch from main branch:
# - staging: Creates dev snapshot → staging PR
# - production: Creates staging snapshot → main PR
# - production: Creates staging → main PR (direct from staging)
#
# REQUIREMENTS:
# • Scheduled cron only runs when this file exists on the default branch (main)
Expand Down Expand Up @@ -272,9 +272,7 @@ jobs:
set -euo pipefail

PR_DATE=$(date +%Y-%m-%d)
BRANCH_NAME="release/production-${PR_DATE}"
echo "date=${PR_DATE}" >> "$GITHUB_OUTPUT"
echo "branch_name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT"

echo "Fetching latest branches..."
git fetch origin main staging
Expand All @@ -290,8 +288,8 @@ jobs:

echo "staging_not_ahead=false" >> "$GITHUB_OUTPUT"

echo "Checking for existing pull requests from ${BRANCH_NAME} to main..."
EXISTING_PR=$(gh pr list --base main --head "${BRANCH_NAME}" --state open --limit 1 --json number --jq '.[0].number // ""')
echo "Checking for existing pull requests from staging to main..."
EXISTING_PR=$(gh pr list --base main --head staging --state open --limit 1 --json number --jq '.[0].number // ""')
echo "existing_pr=${EXISTING_PR}" >> "$GITHUB_OUTPUT"

if [ -n "$EXISTING_PR" ]; then
Expand Down Expand Up @@ -327,25 +325,11 @@ jobs:
fi
done

- name: Create release branch from staging
if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.production_status.outputs.staging_not_ahead != 'true' && steps.production_status.outputs.existing_pr == '' }}
env:
BRANCH_NAME: ${{ steps.production_status.outputs.branch_name }}
shell: bash
run: |
set -euo pipefail

echo "Creating release branch ${BRANCH_NAME} from staging"
git fetch origin staging
git checkout -b "${BRANCH_NAME}" origin/staging
git push origin "${BRANCH_NAME}"

- name: Create staging to main release PR
if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.production_status.outputs.staging_not_ahead != 'true' && steps.production_status.outputs.existing_pr == '' }}
env:
GH_TOKEN: ${{ github.token }}
PR_DATE: ${{ steps.production_status.outputs.date }}
BRANCH_NAME: ${{ steps.production_status.outputs.branch_name }}
COMMITS_AHEAD: ${{ steps.production_status.outputs.commits }}
shell: bash
run: |
Expand All @@ -359,22 +343,20 @@ jobs:

commits_ahead = os.environ["COMMITS_AHEAD"]
pr_date = os.environ["PR_DATE"]
branch_name = os.environ["BRANCH_NAME"]
formatted_date = datetime.strptime(pr_date, "%Y-%m-%d").strftime("%B %d, %Y")

pathlib.Path("pr_body.md").write_text(textwrap.dedent(f"""\
## 🎯 Production Release

**Release Date:** {formatted_date}
**Release Branch:** `{branch_name}`
**Commits ahead**: {commits_ahead}

This automated PR promotes tested changes from `staging` to `main` for production deployment.

### What's Included
All changes that have been verified in the staging environment.

**Note:** This PR uses a dedicated release branch, so new commits to `staging` will NOT automatically appear here.
**Note:** This PR is directly from `staging`, so new commits merged to `staging` will automatically appear here.

### Pre-Deployment Checklist
- [ ] All staging tests passed
Expand All @@ -392,11 +374,11 @@ jobs:
PY

TITLE="Release to Production - ${PR_DATE}"
echo "Creating PR with title: ${TITLE} from branch ${BRANCH_NAME} with ${COMMITS_AHEAD} commits ahead."
echo "Creating PR with title: ${TITLE} from staging with ${COMMITS_AHEAD} commits ahead."

gh pr create \
--base main \
--head "${BRANCH_NAME}" \
--head staging \
--title "${TITLE}" \
--label release \
--label automated \
Expand Down
Loading