diff --git a/.github/workflows/close-preview.yml b/.github/workflows/close-preview.yml index 4d1ed526512b..a1db6432c5f7 100644 --- a/.github/workflows/close-preview.yml +++ b/.github/workflows/close-preview.yml @@ -9,15 +9,65 @@ on: pull_request_target: types: [closed] -permissions: {} +permissions: + id-token: write jobs: close: runs-on: ubuntu-latest if: github.repository == 'microsoft/TypeScript-Website' + environment: + name: azure-preview + deployment: false + env: + AZURE_STATIC_WEB_APP_NAME: ${{ vars.AZURE_STATIC_WEB_APP_NAME }} + AZURE_RESOURCE_GROUP: ${{ vars.AZURE_RESOURCE_GROUP }} steps: - - uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 # v1 + - name: Log in to Azure + uses: azure/login@a641126d1b8aa4d1fa005f4f92df94a3a4c4c906 # v3.1.0 with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_PREVIEW }} - action: close - app_location: /dev/null + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Close preview environment + env: + PR_NUMBER: ${{ inputs.pr || github.event.pull_request.number }} + run: | + set -euo pipefail + : "${AZURE_STATIC_WEB_APP_NAME:?Missing AZURE_STATIC_WEB_APP_NAME environment variable}" + : "${AZURE_RESOURCE_GROUP:?Missing AZURE_RESOURCE_GROUP environment variable}" + if [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::Invalid preview PR number" + exit 1 + fi + + get_preview_environment() { + az staticwebapp environment list \ + --name "$AZURE_STATIC_WEB_APP_NAME" \ + --resource-group "$AZURE_RESOURCE_GROUP" \ + --query "[?name=='$PR_NUMBER'].name" --output tsv + } + + environment="$(get_preview_environment)" + if [[ -z "$environment" ]]; then + echo "No preview environment exists for PR $PR_NUMBER" + exit 0 + fi + + # Avoid Azure CLI's subscription-scoped long-running-operation polling. + resource_id="/subscriptions/{subscriptionId}/resourceGroups/$AZURE_RESOURCE_GROUP" + resource_id+="/providers/Microsoft.Web/staticSites/$AZURE_STATIC_WEB_APP_NAME" + az rest --method delete --url "$resource_id/builds/$PR_NUMBER?api-version=2024-11-01" + + for _ in {1..30}; do + environment="$(get_preview_environment)" + if [[ -z "$environment" ]]; then + echo "Deleted preview environment for PR $PR_NUMBER" + exit 0 + fi + sleep 5 + done + + echo "::error::Timed out waiting for preview $PR_NUMBER to be deleted" + exit 1 diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index c4e5a5168691..c6835228d9b0 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -16,11 +16,18 @@ permissions: contents: read pull-requests: write actions: read + id-token: write jobs: deploy: runs-on: ubuntu-latest if: github.repository == 'microsoft/TypeScript-Website' + environment: + name: azure-preview + deployment: false + env: + AZURE_STATIC_WEB_APP_NAME: ${{ vars.AZURE_STATIC_WEB_APP_NAME }} + AZURE_RESOURCE_GROUP: ${{ vars.AZURE_RESOURCE_GROUP }} steps: - name: Get PR/workflow run info id: get-info @@ -139,19 +146,70 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ fromJson(steps.get-info.outputs.result).runId }} + - name: Set up Node.js + if: ${{ steps.get-info.outputs.result != 'null' }} + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: "22.x" + + - name: Log in to Azure + if: ${{ steps.get-info.outputs.result != 'null' }} + uses: azure/login@a641126d1b8aa4d1fa005f4f92df94a3a4c4c906 # v3.1.0 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + - name: Deploy id: deploy if: ${{ steps.get-info.outputs.result != 'null' }} - uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 # v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_PREVIEW }} - # Unsetting the repo token so we can control commenting ourselves. - # repo_token: ${{ secrets.GITHUB_TOKEN }} - action: "upload" - app_location: "site" - skip_app_build: true - production_branch: v2 - deployment_environment: ${{ fromJson(steps.get-info.outputs.result).pr }} + env: + PR_NUMBER: ${{ fromJson(steps.get-info.outputs.result).pr }} + run: | + set -euo pipefail + : "${AZURE_STATIC_WEB_APP_NAME:?Missing AZURE_STATIC_WEB_APP_NAME environment variable}" + : "${AZURE_RESOURCE_GROUP:?Missing AZURE_RESOURCE_GROUP environment variable}" + if [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::Invalid preview PR number" + exit 1 + fi + + deployment_token="$(az staticwebapp secrets list \ + --name "$AZURE_STATIC_WEB_APP_NAME" \ + --resource-group "$AZURE_RESOURCE_GROUP" \ + --query properties.apiKey --output tsv)" + if [[ -z "$deployment_token" ]]; then + echo "::error::Azure returned no preview deployment token" + exit 1 + fi + echo "::add-mask::$deployment_token" + # Keep CLI configuration outside the untrusted artifact. + printf '{"configurations":{"preview":{}}}\n' > "$RUNNER_TEMP/swa-cli.config.json" + runtime_config_dir="$(mktemp -d "$RUNNER_TEMP/swa-runtime-config.XXXXXX")" + if [[ -e "$GITHUB_WORKSPACE/site/staticwebapp.config.json" ]]; then + cp "$GITHUB_WORKSPACE/site/staticwebapp.config.json" "$runtime_config_dir/" + else + echo "No root Static Web Apps config; using an empty runtime configuration" + printf '{}\n' > "$runtime_config_dir/staticwebapp.config.json" + fi + SWA_CLI_DEPLOYMENT_TOKEN="$deployment_token" \ + npx --yes --package=@azure/static-web-apps-cli@2.1.0 swa \ + --config "$RUNNER_TEMP/swa-cli.config.json" deploy \ + --app-location "$GITHUB_WORKSPACE" \ + --output-location "$GITHUB_WORKSPACE/site" \ + --swa-config-location "$runtime_config_dir" \ + --env "$PR_NUMBER" + + hostname="$(az staticwebapp environment show \ + --name "$AZURE_STATIC_WEB_APP_NAME" \ + --resource-group "$AZURE_RESOURCE_GROUP" \ + --environment-name "$PR_NUMBER" \ + --query hostname --output tsv)" + if [[ ! "$hostname" =~ ^[A-Za-z0-9.-]+\.azurestaticapps\.net$ ]]; then + echo "::error::Azure returned an invalid hostname for preview $PR_NUMBER" + exit 1 + fi + printf 'static_web_app_url=https://%s\n' "$hostname" >> "$GITHUB_OUTPUT" - name: Comment on PR if: ${{ steps.get-info.outputs.result != 'null' }} @@ -162,7 +220,8 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const prefix = "Azure Static Web Apps: Your stage site is ready!"; + const prefix = "Azure Static Web Apps: Your preview site is ready!"; + const legacyPrefix = "Azure Static Web Apps: Your stage site is ready!"; const comments = await github.paginate(github.rest.issues.listComments, { owner: context.repo.owner, repo: context.repo.repo, @@ -171,7 +230,8 @@ jobs: }); for (const comment of comments) { - if (comment.user?.login === "github-actions[bot]" && comment.body?.startsWith(prefix)) { + if (comment.user?.login === "github-actions[bot]" && + (comment.body?.startsWith(prefix) || comment.body?.startsWith(legacyPrefix))) { console.log(`Deleting comment ${comment.id}`); await github.rest.issues.deleteComment({ owner: context.repo.owner,