diff --git a/.github/workflows/create-preview-branch.yml b/.github/workflows/create-preview-branch.yml index 473567bb1..7b3d1388c 100644 --- a/.github/workflows/create-preview-branch.yml +++ b/.github/workflows/create-preview-branch.yml @@ -5,44 +5,49 @@ run-name: Preview by @${{ github.actor }} on: pull_request: types: [opened, synchronize, reopened] - workflow_dispatch: # Keep manual trigger as fallback + workflow_dispatch: concurrency: group: create-preview-${{ github.ref_name }} cancel-in-progress: true +# Least-privileged defaults. We'll elevate at the job level only where needed. permissions: - contents: write # Required to checkout, create branches, and push - actions: read # Required to run the workflow - pull-requests: write # Required to comment on PRs + contents: read + actions: read jobs: create-preview: + # This job needs to push a branch, so contents: write here only. + permissions: + contents: write runs-on: ubuntu-latest + + # Expose the generated preview branch name for the next job + outputs: + preview_branch: ${{ steps.branch-name.outputs.branch_name }} + env: SOURCE_BRANCH: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }} GITHUB_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + steps: - name: Validate and sanitize branch context id: validate run: | set -euo pipefail - echo "[INFO] Validating current branch context" echo "[INFO] Current ref: ${{ github.ref }}" echo "[INFO] Branch name: $SOURCE_BRANCH" - # Validate source branch contains only safe characters and isn't too long if [[ ! "$SOURCE_BRANCH" =~ ^[a-zA-Z0-9/_-]+$ ]]; then - echo "[ERROR] Current branch name contains invalid characters. Only alphanumeric, slash, underscore, and hyphen are allowed." + echo "[ERROR] Branch name contains invalid characters. Only alphanumeric, slash, underscore, and hyphen are allowed." exit 1 fi - if [[ ${#SOURCE_BRANCH} -gt 100 ]]; then - echo "[ERROR] Current branch name is too long (max 100 characters)" + echo "[ERROR] Branch name too long (max 100)." exit 1 fi - # Sanitize for logging (first 20 chars max) SAFE_LOG_BRANCH=$(echo "$SOURCE_BRANCH" | cut -c1-20) echo "safe_branch_log=$SAFE_LOG_BRANCH" >> $GITHUB_OUTPUT @@ -72,19 +77,11 @@ jobs: id: branch-name run: | set -euo pipefail - echo "[INFO] Generating collision-resistant preview branch name" - - # Function to generate preview branch name - generate_preview_branch() { - local safe_prefix timestamp short_sha - safe_prefix=$(echo "$SOURCE_BRANCH" | tr -cd '[:alnum:]' | cut -c1-6) - timestamp=$(date +%s) - short_sha="${GITHUB_SHA:0:7}" - echo "preview-${safe_prefix}-${timestamp}-${short_sha}" - } - - PREVIEW_BRANCH=$(generate_preview_branch) + safe_prefix=$(echo "$SOURCE_BRANCH" | tr -cd '[:alnum:]' | cut -c1-6) + timestamp=$(date +%s) + short_sha="${GITHUB_SHA:0:7}" + PREVIEW_BRANCH="preview-${safe_prefix}-${timestamp}-${short_sha}" echo "branch_name=$PREVIEW_BRANCH" >> $GITHUB_OUTPUT echo "[INFO] Preview branch will be: $PREVIEW_BRANCH" @@ -96,7 +93,6 @@ jobs: - name: Check if preview branch already exists run: | set -euo pipefail - PREVIEW_BRANCH="${{ steps.branch-name.outputs.branch_name }}" echo "[INFO] Checking if preview branch already exists: $PREVIEW_BRANCH" @@ -106,25 +102,25 @@ jobs: echo "[INFO] Please retry the workflow to generate a new branch name." exit 1 fi - echo "[SUCCESS] Preview branch name is unique" - name: Create and push preview branch run: | set -euo pipefail - PREVIEW_BRANCH="${{ steps.branch-name.outputs.branch_name }}" SAFE_BRANCH_LOG="${{ steps.validate.outputs.safe_branch_log }}" - + echo "[INFO] Creating preview branch: $PREVIEW_BRANCH" # Create new branch from current state + git checkout -b "$PREVIEW_BRANCH" - + # Add build artifacts (force add since build/ is likely in .gitignore) echo "[INFO] Adding build artifacts to preview branch (forced)" + git add -f build/ - + # Check if there are changes to commit if git diff --cached --quiet; then echo "[WARN] No build artifacts to commit" @@ -135,24 +131,20 @@ jobs: Source branch: $SAFE_BRANCH_LOG Generated from commit: $GITHUB_SHA Timestamp: $(date -u +"%Y-%m-%d %H:%M:%S UTC") - This branch contains the built documentation for preview deployment. Do not merge this branch to main." fi - + # Push the preview branch echo "[INFO] Pushing preview branch to origin" git push origin "$PREVIEW_BRANCH" echo "[SUCCESS] Successfully pushed preview branch" - - name: Save preview branch info + - name: Save preview branch info (log) run: | - set -euo pipefail - PREVIEW_BRANCH="${{ steps.branch-name.outputs.branch_name }}" SAFE_BRANCH_LOG="${{ steps.validate.outputs.safe_branch_log }}" - - echo "[SUCCESS] Preview branch created successfully!" + echo "[SUCCESS] Preview branch created: $PREVIEW_BRANCH" echo "[INFO] Branch name: $PREVIEW_BRANCH" echo "[INFO] Source branch: $SAFE_BRANCH_LOG" echo "[INFO] Source commit: $GITHUB_SHA" @@ -162,16 +154,23 @@ jobs: echo "" echo "[INFO] 🔗 Branch URL: https://github.com/${{ github.repository }}/tree/$PREVIEW_BRANCH" + comment-on-pr: + # Only this job gets permission to write PR comments. + permissions: + pull-requests: write + runs-on: ubuntu-latest + needs: create-preview + if: github.event_name == 'pull_request' && needs.create-preview.result == 'success' + + steps: - name: Comment on PR - if: github.event_name == 'pull_request' # Only run on PR events uses: actions/github-script@v7 with: script: | + const preview = `${{ needs.create-preview.outputs.preview_branch }}`; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: `Preview ID generated: ${process.env.PREVIEW_BRANCH}` + body: `Preview ID generated: ${preview}` }); - env: - PREVIEW_BRANCH: ${{ steps.branch-name.outputs.branch_name }} \ No newline at end of file