From 6606cfe0f8733dff28cdff72c833ebba4a28cf9c Mon Sep 17 00:00:00 2001 From: Martin Lupa Date: Sun, 8 Dec 2024 20:56:07 +0100 Subject: [PATCH] feat: refactor workflows and add actions --- .github/actions/pr-number/action.yml | 23 ++++ .../actions/slack-notifications/action.yml | 86 ++++++++++++ .github/actions/vercel/action.yml | 102 +++++++-------- .github/workflows/contentful-webhook.yml | 20 +++ .github/workflows/ignore.yml | 66 ---------- .../manual-deployment-contentful.yml | 65 ++++------ .../workflows/manual-deployment-vercel.yml | 88 ------------- .github/workflows/manual-vercel-deploy.yml | 69 ++++++++++ .github/workflows/pr.yml | 122 ++++++++++++------ .../workflows/reusable-contentful-deploy.yml | 48 +++++++ .github/workflows/reusable-vercel-deploy.yml | 117 +++++++++++++++++ .github/workflows/staging.yml | 107 +++++++++------ 12 files changed, 584 insertions(+), 329 deletions(-) create mode 100644 .github/actions/pr-number/action.yml create mode 100644 .github/actions/slack-notifications/action.yml create mode 100644 .github/workflows/contentful-webhook.yml delete mode 100644 .github/workflows/ignore.yml delete mode 100644 .github/workflows/manual-deployment-vercel.yml create mode 100644 .github/workflows/manual-vercel-deploy.yml create mode 100644 .github/workflows/reusable-contentful-deploy.yml create mode 100644 .github/workflows/reusable-vercel-deploy.yml diff --git a/.github/actions/pr-number/action.yml b/.github/actions/pr-number/action.yml new file mode 100644 index 0000000..7d46234 --- /dev/null +++ b/.github/actions/pr-number/action.yml @@ -0,0 +1,23 @@ +name: "Extract Pull Request Number" + +description: "Extract the pull request number that triggered the workflow." + +outputs: + pr-number: + description: "The pull request number that triggered the workflow." + value: ${{ steps.extract-pr-number.outputs.pr-number }} + +runs: + using: "composite" + steps: + - name: Get pull request number + id: extract-pr-number + shell: bash + run: | + # Retrieve the pull request number + PR_NUMBER="${{ github.event.pull_request.number }}" + + echo "Extracted pull request number: $PR_NUMBER" + + # Set the pull request number as output + echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/.github/actions/slack-notifications/action.yml b/.github/actions/slack-notifications/action.yml new file mode 100644 index 0000000..c3fefeb --- /dev/null +++ b/.github/actions/slack-notifications/action.yml @@ -0,0 +1,86 @@ +name: Slack Notification +description: Send Slack notification for success or failure with deployment details + +inputs: + app-name: + description: 'Name of the app being deployed' + required: true + deployment-url: + description: 'URL of the deployed app (output from a previous job)' + required: true + deployment-status: + description: 'Status of the deployment (success or failure)' + required: true + slack-channel-id: + description: 'Slack channel to send notifications' + required: true + slack-notifications-webhook-url: + description: 'Slack webhook URL for sending notifications' + required: true + +runs: + using: composite + steps: + - name: Map app name to Vercel project + shell: bash + id: vercel-project + run: | + if [ "${{ inputs.app-name }}" == "Unified-web App" ]; then + echo "project-name=dfds-unified-web" >> $GITHUB_OUTPUT + elif [ "${{ inputs.app-name }}" == "Login App" ]; then + echo "project-name=unified-web-login" >> $GITHUB_OUTPUT + fi + + - name: Set Slack notification message + shell: bash + id: set-message + run: | + if [ "${{ inputs.deployment-status }}" == "failure" ]; then + echo "message=Deployment failed* :fire_engine:\nThe deployment triggered by *${{ github.actor }}* failed." >> $GITHUB_OUTPUT + echo "text=GitHub Action failed" >> $GITHUB_OUTPUT + elif [ "${{ inputs.deployment-status }}" == "success" ]; then + echo "message=Deployment succeeded* :rocket:\n*${{ github.actor }}* triggered a successful deployment.\nYou can access the deployed app here: <${{ inputs.deployment-url }}>" >> $GITHUB_OUTPUT + echo "text=GitHub Action success" >> $GITHUB_OUTPUT + fi + + - name: Notify Slack + uses: slackapi/slack-github-action@v1.27.0 + with: + channel-id: ${{ inputs.slack-channel-id }} + payload: | + { + "text": "${{ steps.set-message.outputs.text }}", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*${{ inputs.app-name }} - ${{ steps.set-message.outputs.message }}" + } + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": ":github: View action" + }, + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View deployments" + }, + "url": "https://vercel.com//${{ steps.vercel-project.outputs.project-name }}/deployments" + } + ] + } + ] + } + env: + SLACK_WEBHOOK_URL: ${{ inputs.slack-notifications-webhook-url }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK \ No newline at end of file diff --git a/.github/actions/vercel/action.yml b/.github/actions/vercel/action.yml index 180ce75..75ee2da 100644 --- a/.github/actions/vercel/action.yml +++ b/.github/actions/vercel/action.yml @@ -2,80 +2,70 @@ name: Deploy to Vercel description: Deploy the app using Vercel CLI inputs: - app-directory: - description: 'The directory of the app to be deployed' - required: true - vercel-org-id: - description: 'The Vercel organization ID for the app' - required: true - vercel-project-id: - description: 'The Vercel project ID for the app' - required: true - vercel-token: - description: 'The Vercel authentication token' + app-name: + description: 'The name of the app to deploy.' required: true vercel-environment: - description: 'The Vercel environment to deploy to' + description: 'The Vercel environment to deploy to.' required: true default: 'preview' + domains: + description: 'The list of domains to alias the deployment, one per line.' + required: true + +outputs: + deployment-url: + description: "Vercel's deployment URL." + value: ${{ steps.vercel-deploy.outputs.deployment-url }} runs: using: composite steps: - - name: Validate Vercel environment input - shell: bash - run: | - if [[ "${{ inputs.vercel-environment }}" != "preview" && "${{ inputs.vercel-environment }}" != "production" ]]; then - echo "Invalid vercel-environment value. Must be 'preview' or 'production'." - exit 1 - fi - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install global dependencies - shell: bash - run: | - npm install -g pnpm - npm install -g vercel@canary + - name: Setup PNPM + uses: './.github/actions/setup-pnpm' - - name: Change to app directory + - name: Install Vercel CLI shell: bash - run: cd ${{ inputs.app-directory }} - - - name: Install project dependencies - shell: bash - run: pnpm install --frozen-lockfile + run: pnpm install -g vercel@latest - name: Pull Vercel environment information shell: bash - env: - VERCEL_ORG_ID: ${{ inputs.vercel-org-id }} - VERCEL_PROJECT_ID: ${{ inputs.vercel-project-id }} - run: vercel pull --yes --environment=${{ inputs.environment }} --token=${{ inputs.vercel-token }} + run: vercel pull --yes --environment=${{ inputs.vercel-environment }} --token=${{ env.VERCEL_TOKEN }} - - name: Build project artifacts + - name: Build and Deploy Project Artifacts to Vercel shell: bash - env: - VERCEL_ORG_ID: ${{ inputs.vercel-org-id }} - VERCEL_PROJECT_ID: ${{ inputs.vercel-project-id }} run: | - if [ "${{ inputs.vercel-environment }}" == "production" ]; then - vercel build --prod --token=${{ inputs.vercel-token }} + # Conditionally add --prod flag if vercel-environment is production + if [[ "${{ inputs.vercel-environment }}" == "production" ]]; then + vercel build --prod --token=${{ env.VERCEL_TOKEN }} + vercel deploy --prebuilt --prod --token=${{ env.VERCEL_TOKEN }} > domain.txt else - vercel build --token=${{ inputs.vercel-token }} + vercel build --token=${{ env.VERCEL_TOKEN }} + vercel deploy --prebuilt --token=${{ env.VERCEL_TOKEN }} > domain.txt fi - - name: Deploy project artifacts to Vercel + # Alias the deployment to all provided domains + while IFS= read -r domain; do + if [[ -n "$domain" ]]; then # Only process non-empty lines + vercel alias set `cat domain.txt` "$domain" --scope=${{ env.VERCEL_ORG_ID }} --token=${{ env.VERCEL_TOKEN }} + fi + done <<< "${{ inputs.domains }}" + + - name: Set deployment URL output + id: vercel-deploy shell: bash - env: - VERCEL_ORG_ID: ${{ inputs.vercel-org-id }} - VERCEL_PROJECT_ID: ${{ inputs.vercel-project-id }} run: | - if [ "${{ inputs.vercel-environment }}" == "production" ]; then - vercel deploy --prebuilt --prod --token=${{ inputs.vercel-token }} - else - vercel deploy --prebuilt --token=${{ inputs.vercel-token }} - fi + # Extract the first domain from the list of domains + first_domain=$(echo "${{ inputs.domains }}" | head -n 1) + + # Set the deployment URL output using the first domain + echo "deployment-url=https://$first_domain" >> $GITHUB_OUTPUT + + - name: Comment pull request with deployment URL + if: ${{ github.event_name == 'pull_request' }} + uses: thollander/actions-comment-pull-request@v3 + with: + message: | + **${{ inputs.app-name }}** - Deployment succeeded :rocket: + You can access the deployed app here: ${{ steps.vercel-deploy.outputs.deployment-url }} + comment-tag: $${{ steps.vercel-deploy.outputs.deployment-url }} \ No newline at end of file diff --git a/.github/workflows/contentful-webhook.yml b/.github/workflows/contentful-webhook.yml new file mode 100644 index 0000000..85501c3 --- /dev/null +++ b/.github/workflows/contentful-webhook.yml @@ -0,0 +1,20 @@ +name: Contentful Webhook - Redeploy apps + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + unified-web-app-preview-deployment: + name: Deploy Unified-web App (Vercel Preview) + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Unified-web App' + github-environment: 'dev' + vercel-environment: 'preview' + domains: | + ${{ needs.get-pr-number.outputs.pr-number }}-deployment-domain.vercel.app + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/ignore.yml b/.github/workflows/ignore.yml deleted file mode 100644 index 6d1fe47..0000000 --- a/.github/workflows/ignore.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Production Deployment - -on: - workflow_dispatch: - # push: - # branches: - # - master - -jobs: - check-paths: - runs-on: ubuntu-latest - outputs: - unified: ${{ steps.paths.outputs.unified }} - login: ${{ steps.paths.outputs.login }} - dynamic-forms-preview: ${{ steps.paths.outputs.dynamic-forms-preview }} - dynamic-forms-form-settings: ${{ steps.paths.outputs.dynamic-forms-form-settings }} - dynamic-forms-subscriptions: ${{ steps.paths.outputs.dynamic-forms-subscriptions }} - steps: - - name: Check out code - uses: actions/checkout@v4 - - name: Check paths - id: paths - uses: ./.github/actions/check-paths - - unified-production-deploy: - runs-on: ubuntu-latest - timeout-minutes: 30 - environment: production - needs: [check-paths] - if: ${{ needs.check-paths.outputs.unified == 'true' }} - steps: - - name: Check out code. - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - uses: ./.github/actions/vercel - with: - app-directory: 'apps/unified' - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_UNIFIED_PROJECT_ID }} - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-environment: 'production' - - login-production-deploy: - runs-on: ubuntu-latest - timeout-minutes: 30 - environment: production - needs: [check-paths] - if: ${{ needs.check-paths.outputs.login == 'true' }} - steps: - - name: Check out code. - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - uses: ./.github/actions/vercel - with: - app-directory: 'apps/login' - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_LOGIN_PROJECT_ID }} - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-environment: 'production' - - # TODO: Add deployment steps for the following apps: - # dynamic-forms-preview-prod-deploy - # dynamic-forms-form-settings-prod-deploy - # dynamic-forms-subscriptions-prod-deploy diff --git a/.github/workflows/manual-deployment-contentful.yml b/.github/workflows/manual-deployment-contentful.yml index e7fc4a3..7215a9f 100644 --- a/.github/workflows/manual-deployment-contentful.yml +++ b/.github/workflows/manual-deployment-contentful.yml @@ -1,10 +1,10 @@ -name: Contentful apps deployment +name: Contentful apps deployment - Manual workflow on: workflow_dispatch: inputs: - app: - description: 'App to deploy' + app-name: + description: 'The name of the app to deploy' type: choice required: true options: @@ -12,9 +12,7 @@ on: - 'dynamic-forms-form-settings' - 'dynamic-forms-subscriptions' default: 'dynamic-forms-preview' - version: - description: 'The branch, tag or SHA to checkout' - required: true + environment: description: 'Deployment environment' type: choice @@ -29,38 +27,31 @@ permissions: contents: write jobs: - deploy: - name: 'Deploy ${{ github.event.inputs.app }} to ${{ github.event.inputs.environment }} (${{ github.event.inputs.version }})' + map-app-definition: runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment }} - timeout-minutes: 45 - env: - CONTENTFUL_ORG_ID: ${{ secrets.CONTENTFUL_ORG_ID }} - CONTENTFUL_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_MANAGEMENT_API_ACCESS_TOKEN }} - CONTENTFUL_SHARED_CONTENT_PREVIEW_ACCESS_TOKEN: ${{ secrets.NEXT_PUBLIC_CONTENTFUL_SHARED_CONTENT_PREVIEW_ACCESS_TOKEN }} + name: Map inputs to app definition steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Setup PNPM - uses: './.github/actions/setup-pnpm' - - - name: Set environment variables + - name: Determine App Definition ID + id: map-app-definition run: | - if [ "${{ github.event.inputs.app }}" == "dynamic-forms-form-settings" ]; then - echo "CONTENTFUL_APP_DEF_ID=${{ secrets.CONTENTFUL_APP_ID_DYNAMIC_FORMS_FORM_SETTINGS }}" >> $GITHUB_ENV - elif [ "${{ github.event.inputs.app }}" == "dynamic-forms-subscriptions" ]; then - echo "CONTENTFUL_APP_DEF_ID=${{ secrets.CONTENTFUL_APP_ID_DYNAMIC_FORMS_SUBSCRIPTION }}" >> $GITHUB_ENV - echo "VITE_BACKEND_URL=${{ secrets.BACKEND_URL }}" >> $GITHUB_ENV - echo "VITE_TARGET_CONTENTFUL_ENVIRONMENT=master" >> $GITHUB_ENV - elif [ "${{ github.event.inputs.app }}" == "dynamic-forms-preview" ]; then - echo "CONTENTFUL_APP_DEF_ID=${{ secrets.CONTENTFUL_APP_ID_DYNAMIC_FORMS_PREVIEW }}" >> $GITHUB_ENV - echo "NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=${{ secrets.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }}" >> $GITHUB_ENV - echo "CONTENTFUL_APP_DEF_ID=${{ secrets.CONTENTFUL_APP_ID_DYNAMIC_FORMS_PREVIEW }}" >> $GITHUB_ENV - fi + case "${{ github.event.inputs.app-name }}-${{ github.event.inputs.environment }}" in + "dynamic-forms-preview-dev") echo "app-definition=a" >> $GITHUB_ENV ;; + "dynamic-forms-preview-staging") echo "app-definition=b" >> $GITHUB_ENV ;; + "dynamic-forms-preview-production") echo "app-definition=c" >> $GITHUB_ENV ;; + "dynamic-forms-form-settings-dev") echo "app-definition=d" >> $GITHUB_ENV ;; + "dynamic-forms-form-settings-staging") echo "app-definition=e" >> $GITHUB_ENV ;; + "dynamic-forms-form-settings-production") echo "app-definition=f" >> $GITHUB_ENV ;; + "dynamic-forms-subscriptions-dev") echo "app-definition=g" >> $GITHUB_ENV ;; + "dynamic-forms-subscriptions-staging") echo "app-definition=h" >> $GITHUB_ENV ;; + "dynamic-forms-subscriptions-production") echo "app-definition=i" >> $GITHUB_ENV ;; + *) echo "Error: Invalid app-name or environment combination!" && exit 1 ;; + esac - - name: Build and Upload - Contentful App - ${{ github.event.inputs.app }} - run: | - pnpm build:contentful-forms-apps - cd apps/contentful-apps/${{ github.event.inputs.app }} - pnpm upload + deploy: + name: Deploy ${{ github.event.inputs.app-name }} (Contentful ${{ github.event.inputs.environment }}) + uses: ./.github/workflows/reusable-contentful-deploy.yml + needs: map-app-definition + with: + app-name: ${{ github.event.inputs.app-name }} + app-definition: ${{ needs.steps.map-app-definition.outputs.app-definition }} + github-environment: ${{ github.event.inputs.environment }} \ No newline at end of file diff --git a/.github/workflows/manual-deployment-vercel.yml b/.github/workflows/manual-deployment-vercel.yml deleted file mode 100644 index 55de5d7..0000000 --- a/.github/workflows/manual-deployment-vercel.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Vercel apps deployment - -on: - workflow_dispatch: - inputs: - app: - description: 'App to deploy' - type: choice - required: true - options: - - 'unified' - - 'login' - default: 'unified' - version: - description: 'The branch, tag or SHA to checkout' - required: true - default: 'master' - environment: - description: 'Deployment environment' - type: choice - required: true - options: - - 'preview' - - 'production' - default: 'preview' - -permissions: - contents: write - -env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - -jobs: - deploy: - name: 'Deploy ${{ github.event.inputs.app }} to ${{ github.event.inputs.environment }} (${{ github.event.inputs.version }})' - runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install global dependencies - run: | - npm install -g pnpm - npm install -g vercel@canary - - # TODO: investigate if there is a more elegant way to set VERCEL_PROJECT_ID and --prod flag. - - name: Set VERCEL_PROJECT_ID - run: | - if [ "${{ github.event.inputs.app }}" == "unified" ]; then - echo "VERCEL_PROJECT_ID=${{ secrets.VERCEL_UNIFIED_PROJECT_ID }}" >> $GITHUB_ENV - else - echo "VERCEL_PROJECT_ID=${{ secrets.VERCEL_LOGIN_PROJECT_ID }}" >> $GITHUB_ENV - fi - - - name: CD into project directory - run: cd apps/${{ github.event.inputs.app }} - - - name: Install project dependencies - run: pnpm install --frozen-lockfile - - - name: Pull Vercel environment information - run: vercel pull --yes --environment=${{ github.event.inputs.environment }} --token=${{ secrets.VERCEL_TOKEN }} - - - name: Build project artifacts - run: | - if [ "${{ github.event.inputs.environment }}" == "production" ]; then - vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} - else - vercel build --token=${{ secrets.VERCEL_TOKEN }} - fi - - - name: Deploy project artifacts to Vercel - run: | - if [ "${{ github.event.inputs.environment }}" == "production" ]; then - vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} - else - vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} - fi - - # Add slack notification? diff --git a/.github/workflows/manual-vercel-deploy.yml b/.github/workflows/manual-vercel-deploy.yml new file mode 100644 index 0000000..105323d --- /dev/null +++ b/.github/workflows/manual-vercel-deploy.yml @@ -0,0 +1,69 @@ +name: Vercel apps deployment - Manual workflow + +on: + workflow_dispatch: + inputs: + app-name: + description: 'The name of the app to deploy' + type: choice + required: true + options: + - 'dfds-unified-web' + - 'dfds-unified-web-login' + default: 'dfds-unified-web' + environment: + description: 'The environment to deploy to' + type: choice + required: true + options: + - 'preview' + - 'production' + default: 'preview' + +permissions: + contents: write + +jobs: + unified-web-app-preview-deployment: + name: Deploy Unified-web App (Vercel) + if: ${{ github.event.inputs.app-name == 'dfds-unified-web' && github.event.inputs.environment == 'preview' }} + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Unified-web App' + github-environment: 'staging' + vercel-environment: 'preview' + domains: staging-domain-1.vercel.app + secrets: inherit + + unified-web-app-production-deployment: + name: Deploy Unified-web App (Vercel) + if: ${{ github.event.inputs.app-name == 'dfds-unified-web' && github.event.inputs.environment == 'production' }} + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Unified-web App' + github-environment: 'production' + vercel-environment: 'production' + domains: prod-domain-1.vercel.app + secrets: inherit + + login-app-preview-deployment: + name: Deploy Login App (Vercel) + if: ${{ github.event.inputs.app-name == 'dfds-unified-web-login' && github.event.inputs.environment == 'preview' }} + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Login App' + github-environment: 'staging' + vercel-environment: 'preview' + domains: staging-domain-2.vercel.app + secrets: inherit + + login-app-production-deployment: + name: Deploy Login App (Vercel) + if: ${{ github.event.inputs.app-name == 'dfds-unified-web-login' && github.event.inputs.environment == 'production' }} + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Login App' + github-environment: 'production' + vercel-environment: 'production' + domains: prod-domain-2.vercel.app + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f693407..a18e541 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,22 +7,36 @@ on: branches: - master -permissions: - contents: read - pull-requests: write - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + CI: true + HUSKY: 0 + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: + # ---------------------------------------- + # Code quality checks + # ---------------------------------------- + + + # ---------------------------------------- + # Deployments + # ---------------------------------------- check-paths: + name: Check modified paths runs-on: ubuntu-latest + timeout-minutes: 30 outputs: unified: ${{ steps.paths.outputs.unified }} login: ${{ steps.paths.outputs.login }} dynamic-forms-preview: ${{ steps.paths.outputs.dynamic-forms-preview }} dynamic-forms-form-settings: ${{ steps.paths.outputs.dynamic-forms-form-settings }} dynamic-forms-subscriptions: ${{ steps.paths.outputs.dynamic-forms-subscriptions }} + packages: ${{ steps.paths.outputs.packages }} steps: - name: Check out code uses: actions/checkout@v4 @@ -30,45 +44,71 @@ jobs: id: paths uses: ./.github/actions/check-paths - unified-preview-deploy: + get-pr-number: + name: Get pull request number runs-on: ubuntu-latest timeout-minutes: 30 - environment: staging - needs: [check-paths] - if: ${{ needs.check-paths.outputs.unified == 'true' }} + outputs: + pr-number: ${{ steps.pr-number.outputs.pr-number }} steps: - - name: Check out code. + - name: Check out code uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - uses: ./.github/actions/vercel - with: - app-directory: 'apps/unified' - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_UNIFIED_PROJECT_ID }} - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-environment: 'preview' + - uses: ./.github/actions/pr-number + id: pr-number - login-preview-deploy: - runs-on: ubuntu-latest - timeout-minutes: 30 - environment: staging - needs: [check-paths] - if: ${{ needs.check-paths.outputs.login == 'true' }} - steps: - - name: Check out code. - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - uses: ./.github/actions/vercel - with: - app-directory: 'apps/login' - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_LOGIN_PROJECT_ID }} - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-environment: 'preview' + unified-web-app-preview-deployment: + name: Deploy Unified-web App (Vercel Preview) + if: ${{ needs.check-paths.outputs.unified == 'true' || needs.check-paths.outputs.packages == 'true' }} + needs: [check-paths, get-pr-number] + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Unified-web App' + github-environment: 'dev' + vercel-environment: 'preview' + domains: | + ${{ needs.get-pr-number.outputs.pr-number }}-pr-domain-1.vercel.app + secrets: inherit - # TODO: Add deployment steps for the following apps: - # dynamic-forms-preview-dev-deploy - # dynamic-forms-form-settings-dev-deploy - # dynamic-forms-subscriptions-dev-deploy + login-app-preview-deployment: + name: Deploy Login App (Vercel Preview) + if: ${{ needs.check-paths.outputs.login == 'true' || needs.check-paths.outputs.packages == 'true' }} + needs: [check-paths, get-pr-number] + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Login App' + github-environment: 'dev' + vercel-environment: 'preview' + domains: | + ${{ needs.get-pr-number.outputs.pr-number }}-pr-domain-2.vercel.app + other-domain.vercel.app + secrets: inherit + + dynamic-forms-preview-dev-deploy: + name: Deploy Dynamic Forms Preview (Contentful Dev) + if: ${{ needs.check-paths.outputs.dynamic-forms-preview == 'true' }} + needs: [check-paths] + uses: ./.github/workflows/reusable-contentful-deploy.yml + with: + app-name: 'Dynamic Forms Preview' + app-definition: '' + github-environment: 'dev' + + dynamic-forms-form-settings-dev-deploy: + name: Deploy Dynamic Forms Form Settings (Contentful Dev) + if: ${{ needs.check-paths.outputs.dynamic-forms-form-settings == 'true' }} + needs: [check-paths] + uses: ./.github/workflows/reusable-contentful-deploy.yml + with: + app-name: 'Dynamic Forms Form Settings' + app-definition: '' + github-environment: 'dev' + + dynamic-forms-subscriptions-dev-deploy: + name: Deploy Dynamic Forms Subscriptions (Contentful Dev) + if: ${{ needs.check-paths.outputs.dynamic-forms-subscriptions == 'true' }} + needs: [check-paths] + uses: ./.github/workflows/reusable-contentful-deploy.yml + with: + app-name: 'Dynamic Forms Subscriptions' + app-definition: '' + github-environment: 'dev' \ No newline at end of file diff --git a/.github/workflows/reusable-contentful-deploy.yml b/.github/workflows/reusable-contentful-deploy.yml new file mode 100644 index 0000000..f35664f --- /dev/null +++ b/.github/workflows/reusable-contentful-deploy.yml @@ -0,0 +1,48 @@ +name: Deploy to Contentful + +on: + workflow_call: + inputs: + app-name: + description: 'The name of the app to deploy.' + type: string + required: true + app-definition: + description: 'The Contentful app definition id to deploy.' + type: string + required: true + github-environment: + description: 'The GitHub environment to deploy to.' + type: string + required: true + +jobs: + contentful-deploy: + runs-on: ubuntu-latest + environment: ${{ inputs.github-environment }} + env: + # Shared secrets + CONTENTFUL_APP_DEF_ID: ${{ inputs.app-definition }} + CONTENTFUL_ORG_ID: ${{ secrets.CONTENTFUL_ORG_ID }} + CONTENTFUL_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_MANAGEMENT_API_ACCESS_TOKEN }} + + # Dynamic forms preview app secrets + + # Dynamic forms form settings app secrets + + # Dynamic forms subscriptions app secrets + + steps: + - name: Check out code. + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Setup PNPM + uses: './.github/actions/setup-pnpm' + + - name: Build and Upload + run: | + pnpm build --filter=dynamic-forms-preview + cd apps/contentful-apps/dynamic-forms-preview + pnpm upload \ No newline at end of file diff --git a/.github/workflows/reusable-vercel-deploy.yml b/.github/workflows/reusable-vercel-deploy.yml new file mode 100644 index 0000000..4b7ae9b --- /dev/null +++ b/.github/workflows/reusable-vercel-deploy.yml @@ -0,0 +1,117 @@ +name: Deploy to Vercel + +on: + workflow_call: + inputs: + app-name: + description: 'The name of the app to deploy.' + type: string + required: true + github-environment: + description: 'The GitHub environment to deploy to.' + type: string + required: true + contentful-env: + description: 'The Contentful environment to deploy to. Used in data-env.' + type: string + required: false + vercel-environment: + description: 'The Vercel environment to deploy to.' + type: string + required: true + default: 'preview' + domains: + description: 'The list of domains to alias the deployment, one per line.' + type: string + required: true + + outputs: + deployment-url: + description: "Vercel's deployment URL." + value: ${{ github.needs.vercel-deploy.outputs.deployment-url }} + +jobs: + vercel-deploy: + runs-on: ubuntu-latest + environment: ${{ inputs.github-environment }} + env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + CONTENTFUL_SHARED_CONTENT_ENV: ${{ inputs.contentfu-env || vars.CONTENTFUL_SHARED_CONTENT_ENV }} + CONTENTFUL_SHARED_CONTENT_SPACE: ${{ secrets.CONTENTFUL_SHARED_CONTENT_SPACE }} + CONTENTFUL_SHARED_CONTENT_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_SHARED_CONTENT_ACCESS_TOKEN }} + steps: + - name: Check out code. + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Setup PNPM + uses: './.github/actions/setup-pnpm' + + - name: Install Vercel CLI + shell: bash + run: pnpm install -g vercel@latest + + - name: Set VERCEL_PROJECT_ID based on app name + id: set-vercel-project-id + run: | + elif [ "${{ inputs.app-name }}" = "Login App" ]; then + echo "VERCEL_PROJECT_ID=${{ secrets.VERCEL_LOGIN_PROJECT_ID }}" >> $GITHUB_ENV + elif [ "${{ inputs.app-name }}" = "Unified-web App" ]; then + echo "VERCEL_PROJECT_ID=${{ secrets.VERCEL_UNIFIED_PROJECT_ID }}" >> $GITHUB_ENV + else + echo "Unknown app name: ${{ inputs.app-name }}" + exit 1 + fi + + - name: Pull Vercel environment information + shell: bash + run: vercel pull --yes --environment=${{ inputs.vercel-environment }} --token=${{ secrets.VERCEL_TOKEN }} + + - name: Build and Deploy Project Artifacts to Vercel + shell: bash + run: | + # Conditionally add --prod flag if vercel-environment is production + if [[ "${{ inputs.vercel-environment }}" == "production" ]]; then + vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} + vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} > domain.txt + else + vercel build --token=${{ secrets.VERCEL_TOKEN }} + vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} > domain.txt + fi + + # Alias the deployment to all provided domains + while IFS= read -r domain; do + if [[ -n "$domain" ]]; then # Only process non-empty lines + vercel alias set `cat domain.txt` "$domain" --scope=${{ secrets.VERCEL_ORG_ID }} --token=${{ secrets.VERCEL_TOKEN }} + fi + done <<< "${{ inputs.domains }}" + + - name: Set deployment URL output + id: vercel-deploy + shell: bash + run: | + # Extract the first domain from the list of domains + first_domain=$(echo "${{ inputs.domains }}" | head -n 1) + + # Set the deployment URL output using the first domain + echo "deployment-url=https://$first_domain" >> $GITHUB_OUTPUT + + - name: Comment pull request with deployment URL + if: ${{ github.event_name == 'pull_request' }} + uses: thollander/actions-comment-pull-request@v3 + with: + message: | + **${{ inputs.app-name }}** - Deployment succeeded :rocket: + You can access the deployed app here: ${{ steps.vercel-deploy.outputs.deployment-url }} + comment-tag: $${{ steps.vercel-deploy.outputs.deployment-url }} + + - name: Notify deployment outcome to Slack + if: ${{ job.status != 'cancelled' }} + uses: ./.github/actions/slack-notifications + with: + app-name: ${{ inputs.app-name }} + deployment-url: ${{ steps.vercel-deploy.outputs.deployment-url }} + deployment-status: ${{ job.status }} + slack-channel-id: ${{ secrets.SLACK_DEPLOYMENTS_CHANNEL_ID}} + slack-notifications-webhook-url: ${{ secrets.SLACK_NOTIFICATIONS_WEBHOOK_URL }} \ No newline at end of file diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 173e2b0..bc210a9 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -1,22 +1,33 @@ -name: Staging Deployment +name: Staging workflow on: workflow_dispatch: push: - branches: [master] + branches: + - master env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + CI: true + HUSKY: 0 + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: check-paths: + name: Check modified paths runs-on: ubuntu-latest + timeout-minutes: 30 outputs: unified: ${{ steps.paths.outputs.unified }} login: ${{ steps.paths.outputs.login }} dynamic-forms-preview: ${{ steps.paths.outputs.dynamic-forms-preview }} dynamic-forms-form-settings: ${{ steps.paths.outputs.dynamic-forms-form-settings }} dynamic-forms-subscriptions: ${{ steps.paths.outputs.dynamic-forms-subscriptions }} + packages: ${{ steps.paths.outputs.packages }} steps: - name: Check out code uses: actions/checkout@v4 @@ -24,45 +35,59 @@ jobs: id: paths uses: ./.github/actions/check-paths - unified-preview-deploy: - runs-on: ubuntu-latest - timeout-minutes: 30 - environment: staging + unified-web-app-preview-deployment: + name: Deploy Unified-web App (Vercel Preview) + if: ${{ needs.check-paths.outputs.unified == 'true' || needs.check-paths.outputs.packages == 'true' }} + needs: check-paths + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Unified-web App' + github-environment: 'staging' + vercel-environment: 'preview' + domains: staging-domain-1.vercel.app + secrets: inherit + + login-app-preview-deployment: + name: Deploy Login App (Vercel Preview) + if: ${{ needs.check-paths.outputs.login == 'true' || needs.check-paths.outputs.packages == 'true' }} + needs: check-paths + uses: ./.github/workflows/reusable-vercel-deploy.yml + with: + app-name: 'Login App' + github-environment: 'staging' + vercel-environment: 'preview' + domains: | + other-staging-domain-2.vercel.app + other-staging-domain-3.vercel.app + secrets: inherit + + dynamic-forms-preview-staging-deploy: + name: Deploy Dynamic Forms Preview (Contentful Staging) + if: ${{ needs.check-paths.outputs.dynamic-forms-preview == 'true' }} needs: [check-paths] - if: ${{ needs.check-paths.outputs.unified == 'true' }} - steps: - - name: Check out code. - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - uses: ./.github/actions/vercel - with: - app-directory: 'apps/unified' - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_UNIFIED_PROJECT_ID }} - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-environment: 'preview' + uses: ./.github/workflows/reusable-contentful-deploy.yml + with: + app-name: 'Dynamic Forms Preview' + app-definition: '' + github-environment: 'staging' - login-preview-deploy: - runs-on: ubuntu-latest - timeout-minutes: 30 - environment: staging + dynamic-forms-form-settings-staging-deploy: + name: Deploy Dynamic Forms Form Settings (Contentful Staging) + if: ${{ needs.check-paths.outputs.dynamic-forms-form-settings == 'true' }} needs: [check-paths] - if: ${{ needs.check-paths.outputs.login == 'true' }} - steps: - - name: Check out code. - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - uses: ./.github/actions/vercel - with: - app-directory: 'apps/login' - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_LOGIN_PROJECT_ID }} - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-environment: 'preview' + uses: ./.github/workflows/reusable-contentful-deploy.yml + with: + app-name: 'Dynamic Forms Form Settings' + app-definition: '' + github-environment: 'staging' - # TODO: Add deployment steps for the following apps: - # dynamic-forms-preview-staging-deploy - # dynamic-forms-form-settings-staging-deploy - # dynamic-forms-subscriptions-staging-deploy + dynamic-forms-subscriptions-staging-deploy: + name: Deploy Dynamic Forms Subscriptions (Contentful Staging) + if: ${{ needs.check-paths.outputs.dynamic-forms-subscriptions == 'true' }} + needs: [check-paths] + uses: ./.github/workflows/reusable-contentful-deploy.yml + with: + app-name: 'Dynamic Forms Subscriptions' + app-definition: '' + github-environment: 'staging' + \ No newline at end of file