Skip to content

trigger

trigger #31

name: Trigger Skypilot job with Approval Check
on:
issue_comment:
types: [created]
jobs:
validate-pr:
name: Validate PR comment & approval
runs-on: ubuntu-slim
# 1️⃣ Only run for PR comments containing the trigger string
if: |
github.event.issue.pull_request != null &&
contains(github.event.comment.body, '/run')
steps:
- name: Check PR approval
id: check-approval
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=${{ github.event.issue.number }}
REPO=${{ github.repository }}
APPROVALS=$(curl -s \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/$REPO/pulls/$PR_NUMBER/reviews \
| jq '[.[] | select(.state=="APPROVED")] | length')
if [ "$APPROVALS" -ge 1 ]; then
echo "approved=true" >> "$GITHUB_OUTPUT"
echo "PR is approved, continuing skypilot job..."
else
echo "approved=false" >> "$GITHUB_OUTPUT"
echo "PR is not approved, exiting."
fi
outputs:
approved: ${{ steps.check-approval.outputs.approved }}
launch-skypilot-job:
name: Run job after validation
needs: validate-pr
runs-on: ubuntu-latest
# 2️⃣ Run only if validation job confirmed approval
if: needs.validate-pr.outputs.approved == 'true'
steps:
- name: Checkout the code
uses: actions/checkout@v4
- name: Generate Job Name
id: generate_job_name
shell: bash
run: |
set -eo pipefail
# GitHub actor (person triggering the workflow)
ACTOR="${{ github.actor }}"
# Short SHA of the checked-out commit
SHORT_COMMIT_HASH=$(git rev-parse --short HEAD)
# Branch name, sanitized for safe use in job name
BRANCH_RAW=$(git rev-parse --abbrev-ref HEAD)
BRANCH_SANITIZED=$(echo "$BRANCH_RAW" | sed 's/[^a-zA-Z0-9_-]/-/g')
# GitHub run ID ensures uniqueness for every workflow run
RUN_ID="${GITHUB_RUN_ID}"
# Construct final job name
FINAL_RUN_NAME="${ACTOR}.${BRANCH_SANITIZED}.${SHORT_COMMIT_HASH}.${RUN_ID}"
echo "Generated job name: $FINAL_RUN_NAME"
echo "job_name=$FINAL_RUN_NAME" >> "$GITHUB_OUTPUT"
echo "### Generated job name: $FINAL_RUN_NAME" >> "$GITHUB_STEP_SUMMARY"
- name: Launch SkyPilot Job via Custom Action
id: skylaunch
uses: ./.github/actions/launch-skypilot-job
with:
# The file path is intentionally fixed and sourced from the main branch only.
# This is a governance decision to prevent feature branches from altering SkyPilot configuration behavior.
task_yaml_path: './skypilot.yaml'
job_name: ${{ steps.generate_job_name.outputs.job_name }}
skypilot_api_url: ${{ secrets.SKYPILOT_API_URL }}
skypilot_service_account_token: ${{ secrets.SKYPILOT_SERVICE_ACCOUNT_TOKEN }}
- name: Extract SkyPilot Request ID
id: extract_request_id
shell: bash
run: |
# Extract the request ID from the launch output
# Looking for pattern like "Submitted sky.jobs.launch request: c01f1273-4610-4949-8f1c-e24365ad0330"
REQUEST_ID=$(echo "${{ steps.skylaunch.outputs.launch_output }}" | grep -oP 'Submitted sky.jobs.launch request: \K[a-f0-9-]+' || true)
if [ -z "$REQUEST_ID" ]; then
# Try alternate extraction method from stored output
REQUEST_ID=$(cat /tmp/skypilot_launch_output.txt 2>/dev/null | grep -oP 'Submitted sky.jobs.launch request: \K[a-f0-9-]+' || true)
fi
if [ -n "$REQUEST_ID" ]; then
echo "Extracted request ID: $REQUEST_ID"
echo "request_id=$REQUEST_ID" >> $GITHUB_OUTPUT
echo "request_id_short=$(echo $REQUEST_ID | cut -c1-8)" >> $GITHUB_OUTPUT
echo '### SkyPilot Request ID $REQUEST_ID extracted' >> $GITHUB_STEP_SUMMARY
else
echo "Could not extract request ID from launch output"
echo "request_id=" >> $GITHUB_OUTPUT
echo "request_id_short=" >> $GITHUB_OUTPUT
echo '### SkyPilot Request ID not found in logs' >> $GITHUB_STEP_SUMMARY
fi
- name: Get SkyPilot Job ID
id: get_job_id
if: steps.extract_request_id.outputs.request_id != ''
shell: bash
run: |
set +e # Don't exit on error
# Wait a bit for the job to be submitted
sleep 5
# Get the logs and extract the job ID
echo "Getting logs for request: ${{ steps.extract_request_id.outputs.request_id_short }}"
LOG_OUTPUT=$(sky api logs ${{ steps.extract_request_id.outputs.request_id_short }} 2>&1 | tail -20)
# Extract job ID from pattern like "Job submitted, ID: 4700"
JOB_ID=$(echo "$LOG_OUTPUT" | grep -oP 'Job submitted, ID: \K\d+' || true)
if [ -n "$JOB_ID" ]; then
echo "Extracted SkyPilot Job ID: $JOB_ID"
echo "job_id=$JOB_ID" >> $GITHUB_OUTPUT
echo '### SkyPilot Job ID $JOB_ID launched' >> $GITHUB_STEP_SUMMARY
else
echo "Could not extract Job ID from logs"
echo "Log output was:"
echo "$LOG_OUTPUT"
echo "job_id=" >> $GITHUB_OUTPUT
echo '### SkyPilot Job ID not found in logs' >> $GITHUB_STEP_SUMMARY
fi
set -e
- name: Fetch Slack-GitHub json mapping
id: fetch_mapping
shell: bash
run: |
set -euo pipefail
OWNER="aftershootco"
REPO="infra"
FILE_PATH="user-mapping.json"
BRANCH="main"
curl -sSf \
-H "Authorization: Bearer ${{ secrets.AFTERSHOOT_INFRA_REPO_READ_TOKEN }}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$OWNER/$REPO/contents/$FILE_PATH?ref=$BRANCH" \
| jq -r '.content' \
| base64 --decode > user-mapping.json
- name: Check if slack secrets are set
id: check_slack_secrets
shell: bash
run: |
if [ -z "${{ secrets.SLACK_BOT_TOKEN }}" ]; then
echo "slack_secrets_set=False" >> $GITHUB_OUTPUT
else
echo "slack_secrets_set=True" >> $GITHUB_OUTPUT
fi
- name: Resolve Slack user for GitHub actor
id: slack_user
shell: bash
run: |
set -euo pipefail
ACTOR="${{ github.actor }}"
MAP_FILE="user-mapping.json"
SLACK_USER_ID=$(jq -r --arg actor "$ACTOR" '.[$actor] // empty' "$MAP_FILE")
if [[ -z "$SLACK_USER_ID" ]]; then
echo "No Slack user mapped for GitHub actor: $ACTOR"
exit 0
fi
echo "slack_user_id=$SLACK_USER_ID" >> "$GITHUB_OUTPUT"
- name: Generate Run Information for Slack
id: generate_slack_message
if: steps.check_slack_secrets.outputs.slack_secrets_set == 'True' && steps.slack_user.outputs.slack_user_id != ''
shell: bash
run: |
MESSAGE_TEXT="Skypilot job ${{ steps.get_job_id.outputs.job_id }} triggered by: ${{ github.event_name }}"
MESSAGE_BLOCK="$MESSAGE_TEXT\nSkyPilot Job Name: ${{ steps.generate_job_name.outputs.job_name }}"
if [ -n "${{ steps.get_job_id.outputs.job_id }}" ]; then
MESSAGE_BLOCK="$MESSAGE_BLOCK\n✅ SkyPilot Job ID: ${{ steps.get_job_id.outputs.job_id }}"
MESSAGE_BLOCK="$MESSAGE_BLOCK\n📝 View logs: sky jobs logs ${{ steps.get_job_id.outputs.job_id }}"
MESSAGE_BLOCK="$MESSAGE_BLOCK\n🔗 View job: ${{ secrets.SKYPILOT_API_URL }}/dashboard/jobs/${{ steps.get_job_id.outputs.job_id }}"
elif [ -n "${{ steps.extract_request_id.outputs.request_id_short }}" ]; then
MESSAGE_BLOCK="$MESSAGE_BLOCK\n⏳ Request ID: ${{ steps.extract_request_id.outputs.request_id_short }}"
MESSAGE_BLOCK="$MESSAGE_BLOCK\n📝 Get job ID: sky api logs ${{ steps.extract_request_id.outputs.request_id_short }}"
else
MESSAGE_BLOCK="$MESSAGE_BLOCK\nJob ID / Request ID not found"
fi
echo "message_text=$MESSAGE_TEXT" >> $GITHUB_OUTPUT
echo "message_block=$MESSAGE_BLOCK" >> $GITHUB_OUTPUT
echo $MESSAGE_BLOCK
echo "$MESSAGE_BLOCK" >> $GITHUB_STEP_SUMMARY
- name: Notify Slack channel
uses: slackapi/[email protected]
if: steps.check_slack_secrets.outputs.slack_secrets_set == 'True' && steps.slack_user.outputs.slack_user_id != ''
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
{
"channel": "${{ steps.slack_user.outputs.slack_user_id }}",
"text": "${{ steps.generate_slack_message.outputs.message_text }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${{ steps.generate_slack_message.outputs.message_block }}"
}
}
]
}