Skip to content

chore(ads): remove ads experimental annotations #1873

chore(ads): remove ads experimental annotations

chore(ads): remove ads experimental annotations #1873

name: "@RCGitBot please test"
on:
issue_comment:
types: [created]
# Deny-all baseline: each job must declare its own `permissions:` block.
permissions: {}
jobs:
approve-full-tests:
runs-on: ubuntu-latest
if: |
github.event.issue.pull_request &&
github.event.comment.body == '@RCGitBot please test' &&
github.repository == 'RevenueCat/purchases-ios' &&
github.event.comment.author_association == 'MEMBER'
permissions:
pull-requests: write
steps:
# GitHub Actions `issue_comment` workflows always run from the default branch,
# so we resolve the PR's head branch to find its CircleCI pipeline and approve
# the hold job in the existing workflow.
- name: Get PR branch
id: get-branch
run: echo "branch=$(gh pr view "$PR_NO" --repo "$REPO" --json headRefName --jq '.headRefName')" >> "$GITHUB_OUTPUT"
env:
REPO: ${{ github.repository }}
PR_NO: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Approve CircleCI hold job
id: approve
env:
CCI_TOKEN: ${{ secrets.CIRCLECI_TOKEN }}
BRANCH: ${{ steps.get-branch.outputs.branch }}
run: |
PROJECT_SLUG="gh/RevenueCat/purchases-ios"
WORKFLOW_NAME="run-all-tests"
APPROVAL_JOB_NAME="approve-full-tests"
# This repo uses CircleCI dynamic config: on push, a setup workflow runs
# first and generates a continuation config. The 'run-all-tests' workflow
# (and its 'approve-full-tests' hold job) only appears in the pipeline once
# setup finishes. If someone comments '@RCGitBot please test' while setup is
# still running, the workflow/approval job isn't there yet. So we poll for it
# instead of failing immediately.
# Keep total polling under ~1 minute: 4 attempts with 15s between them = ~45s
# of waiting (plus request time).
MAX_ATTEMPTS=4
SLEEP_SECONDS=15
# 1. Get the most recent pipeline for this branch
PIPELINE_ID=$(curl -s --max-time 15 -G \
-H "Circle-Token: $CCI_TOKEN" \
--data-urlencode "branch=$BRANCH" \
"https://circleci.com/api/v2/project/$PROJECT_SLUG/pipeline" \
| jq -r '.items[0].id')
if [[ -z "$PIPELINE_ID" || "$PIPELINE_ID" == "null" ]]; then
echo "::error::No CircleCI pipeline found for branch '$BRANCH'"
exit 1
fi
echo "Found pipeline: $PIPELINE_ID"
# 2. Poll for the run-all-tests workflow and its approval job. Both may be
# missing while the setup workflow is still generating the continuation config.
WORKFLOW_ID=""
APPROVAL_REQUEST_ID=""
for ((attempt=1; attempt<=MAX_ATTEMPTS; attempt++)); do
WORKFLOW_ID=$(curl -s --max-time 15 \
-H "Circle-Token: $CCI_TOKEN" \
"https://circleci.com/api/v2/pipeline/$PIPELINE_ID/workflow" \
| jq -r --arg name "$WORKFLOW_NAME" '.items[] | select(.name == $name) | .id')
if [[ -n "$WORKFLOW_ID" && "$WORKFLOW_ID" != "null" ]]; then
echo "Found workflow: $WORKFLOW_ID"
JOBS_RESPONSE=$(curl -s --max-time 15 \
-H "Circle-Token: $CCI_TOKEN" \
"https://circleci.com/api/v2/workflow/$WORKFLOW_ID/job")
JOB_STATUS=$(echo "$JOBS_RESPONSE" | jq -r --arg name "$APPROVAL_JOB_NAME" \
'.items[] | select(.name == $name and .type == "approval") | .status')
if [[ "$JOB_STATUS" == "success" ]]; then
echo "Hold job '$APPROVAL_JOB_NAME' was already approved"
exit 0
fi
APPROVAL_REQUEST_ID=$(echo "$JOBS_RESPONSE" | jq -r --arg name "$APPROVAL_JOB_NAME" \
'.items[] | select(.name == $name and .type == "approval") | .approval_request_id')
if [[ -n "$APPROVAL_REQUEST_ID" && "$APPROVAL_REQUEST_ID" != "null" ]]; then
break
fi
fi
if [[ "$attempt" -lt "$MAX_ATTEMPTS" ]]; then
echo "Attempt $attempt/$MAX_ATTEMPTS: '$WORKFLOW_NAME' workflow / '$APPROVAL_JOB_NAME' job not ready yet (setup may still be running). Retrying in ${SLEEP_SECONDS}s..."
sleep "$SLEEP_SECONDS"
fi
done
if [[ -z "$WORKFLOW_ID" || "$WORKFLOW_ID" == "null" ]]; then
echo "::error::No '$WORKFLOW_NAME' workflow found in pipeline $PIPELINE_ID after $MAX_ATTEMPTS attempts"
exit 1
fi
if [[ -z "$APPROVAL_REQUEST_ID" || "$APPROVAL_REQUEST_ID" == "null" ]]; then
echo "::error::Approval job '$APPROVAL_JOB_NAME' not found in workflow $WORKFLOW_ID after $MAX_ATTEMPTS attempts"
exit 1
fi
echo "Found approval request: $APPROVAL_REQUEST_ID"
# 3. Approve it
HTTP_STATUS=$(curl -s --max-time 15 -o /dev/null -w "%{http_code}" -X POST \
-H "Circle-Token: $CCI_TOKEN" \
"https://circleci.com/api/v2/workflow/$WORKFLOW_ID/approve/$APPROVAL_REQUEST_ID")
if [[ "$HTTP_STATUS" != "202" ]]; then
echo "::error::Failed to approve job. CircleCI returned HTTP $HTTP_STATUS"
exit 1
fi
echo "Successfully approved '$APPROVAL_JOB_NAME' in workflow '$WORKFLOW_NAME'"
- name: React to comment
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JOB_STATUS: ${{ job.status }}
REPO: ${{ github.repository }}
COMMENT_ID: ${{ github.event.comment.id }}
run: |
if [[ "$JOB_STATUS" == "success" ]]; then
REACTION="+1"
else
REACTION="-1"
fi
gh api "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \
-f content="$REACTION"
# Trigger CircleCI job(s) on-demand via "@RCGitBot please test <job1> <job2> <job3>".
# Triggers a new CircleCI pipeline; the setup config generates a continuation
# config containing only a workflow with the requested job(s).
run-specific-jobs:
runs-on: ubuntu-latest
if: |
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '@RCGitBot please test ') &&
github.repository == 'RevenueCat/purchases-ios' &&
github.event.comment.author_association == 'MEMBER'
permissions:
pull-requests: write
steps:
- name: Parse job names
id: parse
env:
COMMENT: ${{ github.event.comment.body }}
run: |
# GHA's startsWith() is case-insensitive, so lowercase the comment before stripping
# the prefix. Then use the length to slice the original, preserving job name case
# (e.g. backend-integration-tests-SK1).
PREFIX="@rcgitbot please test "
COMMENT_LOWER="$(echo "$COMMENT" | tr '[:upper:]' '[:lower:]')"
if [[ "$COMMENT_LOWER" != "$PREFIX"* ]]; then
echo "::error::Comment does not start with '@RCGitBot please test '"
exit 1
fi
JOBS_RAW="${COMMENT:${#PREFIX}}"
# Trim whitespace
JOBS_RAW=$(echo "$JOBS_RAW" | xargs)
if [[ -z "$JOBS_RAW" ]]; then
echo "::error::No job name(s) provided"
exit 1
fi
# Validate: only alphanumeric, hyphens, underscores, and spaces
if [[ ! "$JOBS_RAW" =~ ^[a-zA-Z0-9_[:space:]-]+$ ]]; then
echo "::error::Invalid input: $JOBS_RAW (only alphanumeric, hyphens, underscores, and spaces allowed)"
exit 1
fi
echo "job_names=$JOBS_RAW" >> "$GITHUB_OUTPUT"
echo "Parsed job(s): $JOBS_RAW"
- name: Get PR branch
id: get-branch
run: echo "branch=$(gh pr view "$PR_NO" --repo "$REPO" --json headRefName --jq '.headRefName')" >> "$GITHUB_OUTPUT"
env:
REPO: ${{ github.repository }}
PR_NO: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger CircleCI pipeline
id: trigger
env:
CCI_TOKEN: ${{ secrets.CIRCLECI_TOKEN }}
BRANCH: ${{ steps.get-branch.outputs.branch }}
JOB_NAMES: ${{ steps.parse.outputs.job_names }}
run: |
PROJECT_SLUG="gh/RevenueCat/purchases-ios"
# Build the payload with jq rather than string interpolation: branch names come
# from the PR head ref and may contain characters that need JSON escaping.
BODY_JSON=$(jq -n --arg branch "$BRANCH" --arg jobs "$JOB_NAMES" \
'{branch: $branch, parameters: {requested_jobs: $jobs}}')
RESPONSE=$(curl -s --max-time 15 -w "\n%{http_code}" -X POST \
-H "Circle-Token: $CCI_TOKEN" \
-H "Content-Type: application/json" \
-d "$BODY_JSON" \
"https://circleci.com/api/v2/project/$PROJECT_SLUG/pipeline")
HTTP_STATUS=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
if [[ "$HTTP_STATUS" != "201" ]]; then
echo "::error::Failed to trigger pipeline. CircleCI returned HTTP $HTTP_STATUS"
echo "$BODY"
exit 1
fi
PIPELINE_ID=$(echo "$BODY" | jq -r '.id')
PIPELINE_NUMBER=$(echo "$BODY" | jq -r '.number')
echo "pipeline_number=$PIPELINE_NUMBER" >> "$GITHUB_OUTPUT"
echo "Triggered pipeline #$PIPELINE_NUMBER (id: $PIPELINE_ID) for job(s) '$JOB_NAMES' on branch '$BRANCH'"
- name: Post pipeline link
if: success()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PIPELINE_NUMBER: ${{ steps.trigger.outputs.pipeline_number }}
JOB_NAMES: ${{ steps.parse.outputs.job_names }}
REPO: ${{ github.repository }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
gh api "repos/$REPO/issues/$ISSUE_NUMBER/comments" \
-f body="🚀 Triggered **$JOB_NAMES** → [Pipeline #$PIPELINE_NUMBER](https://app.circleci.com/pipelines/gh/RevenueCat/purchases-ios/$PIPELINE_NUMBER)"
- name: React to comment
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JOB_STATUS: ${{ job.status }}
REPO: ${{ github.repository }}
COMMENT_ID: ${{ github.event.comment.id }}
run: |
if [[ "$JOB_STATUS" == "success" ]]; then
REACTION="+1"
else
REACTION="-1"
fi
gh api "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \
-f content="$REACTION"