From d9752af28fc8e426a897ff91dc03d40133e4d9bf Mon Sep 17 00:00:00 2001 From: Luca Cavallaro <4357400+lucacavallaro@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:16:15 +0200 Subject: [PATCH] add web_app_deploy workflow --- .github/workflows/web_app_deploy.yaml | 174 ++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 .github/workflows/web_app_deploy.yaml diff --git a/.github/workflows/web_app_deploy.yaml b/.github/workflows/web_app_deploy.yaml new file mode 100644 index 000000000..b8948673c --- /dev/null +++ b/.github/workflows/web_app_deploy.yaml @@ -0,0 +1,174 @@ +on: + workflow_call: + inputs: + workspace_name: + description: The name of the workspace to create the artifact for. + type: string + required: true + target_name: + description: "(Optional) The name of the target to create artifact for (default: main)" + type: string + required: false + default: main + environment: + description: Environment where the artifact will be deployed. + type: string + required: true + resource_group_name: + description: Web App resource group name. + type: string + required: true + web_app_name: + description: Web App name. + type: string + required: true + health_check_path: + description: The health probe path exposed by the Web App. + type: string + required: true + use_staging_slot: + description: True if artifact should be deployed to staging slot + type: boolean + required: false + default: true + use_private_agent: + description: Use a private agent to deploy the built artifact. + type: boolean + required: false + default: true + +concurrency: + group: ${{ github.workflow }}-cd + cancel-in-progress: true + +env: + BUNDLE_NAME: bundle + +jobs: + build: + name: Build Artifact + runs-on: ubuntu-20.04 + env: + WORKSPACE: ${{ inputs.workspace_name }} + + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + name: Checkout + + - name: Prune + run: npx turbo@1.13.3 prune --scope ${{ env.WORKSPACE }} + + - name: Setup Node.js + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version-file: ".node-version" + cache: "yarn" + cache-dependency-path: "./out/yarn.lock" + + - name: Install dependencies + run: yarn install --immutable + working-directory: ./out + + - name: Build + run: yarn build + working-directory: ./out + + - name: Build the artifact + id: make-artifact + env: + YARN_NODE_LINKER: node-modules + YARN_NM_HOISTING_LIMITS: workspaces + run: | + cd apps/${{ inputs.workspace_name }} + ENTRY_POINT=$(jq -r ${{ inputs.target_name == 'main' && '.main' || format('.exports.{0}', inputs.target_name) }} package.json) + if [ -z $ENTRY_POINT ]; then + echo "::error::Entry point not found" + exit 1 + fi + mkdir artifact + yarn workspaces focus --production + mv node_modules artifact/ + SHORT_HASH=$(git rev-parse --short ${{ github.sha }}) + curl -fsSL https://esbuild.github.io/dl/v0.21.5 | sh + FORMAT=$(jq -r 'if .type == "module" then "esm" else "cjs" end' package.json) + ./esbuild index=$ENTRY_POINT --bundle --format=$FORMAT --platform=node --target=node20 --packages=external --outdir=artifact + jq --arg sha "+$SHORT_HASH" '{"name": .name, "version": (.version + $sha), "main": "index.js", "type": (if has("type") then .type else "commonjs" end), "dependencies": .dependencies}' package.json > artifact/package.json + cd artifact + zip -q -r ${{ env.BUNDLE_NAME }}.zip * + echo "artifact-path=$(realpath ${{ env.BUNDLE_NAME }}.zip)" >> "$GITHUB_OUTPUT" + working-directory: ./out + + - name: Upload Artifact + uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + with: + name: ${{ env.BUNDLE_NAME }} + path: ${{ steps.make-artifact.outputs.artifact-path }} + if-no-files-found: error + retention-days: 7 + + deploy: + name: Deploy + if: ${{ !github.event.act }} + needs: [build] + runs-on: ${{ inputs.use_private_agent == true && 'self-hosted' || 'ubuntu-20.04' }} + environment: ${{ inputs.environment }}-cd + permissions: + id-token: write + contents: read + env: + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} + ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} + ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} + ARM_USE_OIDC: true + RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }} + WEB_APP_NAME: ${{ inputs.web_app_name}} + HEALTH_CHECK_PATH: ${{ inputs.health_check_path }} + USE_STAGING_SLOT: ${{ inputs.use_staging_slot }} + + steps: + - name: Download Artifact + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + with: + name: ${{ env.BUNDLE_NAME }} + + - name: Azure Login + uses: azure/login@v2 # v2.0.0 + with: + client-id: ${{ env.ARM_CLIENT_ID }} + tenant-id: ${{ env.ARM_TENANT_ID }} + subscription-id: ${{ env.ARM_SUBSCRIPTION_ID }} + + - name: Deploy + uses: azure/webapps-deploy@v2 + if: ${{ env.USE_STAGING_SLOT == 'false' }} + with: + resource-group-name: ${{ env.RESOURCE_GROUP_NAME }} + app-name: ${{ env.WEB_APP_NAME }} + package: ${{ env.BUNDLE_NAME }}.zip + + - name: Deploy to Staging Slot + uses: azure/webapps-deploy@v2 + if: ${{ env.USE_STAGING_SLOT == 'true' }} + with: + resource-group-name: ${{ env.RESOURCE_GROUP_NAME }} + app-name: ${{ env.WEB_APP_NAME }} + slot-name: staging + package: ${{ env.BUNDLE_NAME }}.zip + + - name: Ping Staging Health + if: ${{ env.USE_STAGING_SLOT == 'true' }} + run: | + curl \ + --retry 5 \ + --retry-max-time 120 \ + --retry-all-errors \ + -f 'https://${{ env.WEB_APP_NAME }}-staging.azurewebsites.net${{ env.HEALTH_CHECK_PATH }}' + + - name: Swap Staging and Production Slots + if: ${{ env.USE_STAGING_SLOT == 'true' }} + run: | + az webapp deployment slot swap \ + -g ${{ env.RESOURCE_GROUP_NAME }} \ + -n ${{ env.WEB_APP_NAME }} \ + --slot staging \ + --target-slot production