Prod Build #223
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Prod Build | |
| # NOTE! This is the *PROD* workflow. | |
| # Keep in mind that much of the configuration is repeated in `stage-build.yml` | |
| # and `dev-build.yml` | |
| # | |
| # For a complete picture of all environments, see: | |
| # | |
| # https://docs.google.com/spreadsheets/d/1VnnEl-iTtKYmlyN02FiEXygxZCgE4o_ZO8wSleebne4/edit?usp=sharing | |
| # | |
| on: | |
| schedule: | |
| - cron: "0 0 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| notes: | |
| description: "Notes" | |
| required: false | |
| default: "" | |
| invalidate: | |
| description: "Invalidate CDN (use only in exceptional circumstances)" | |
| type: boolean | |
| required: false | |
| default: false | |
| workflow_call: | |
| secrets: | |
| GCP_PROJECT_NAME: | |
| required: true | |
| WIP_PROJECT_ID: | |
| required: true | |
| permissions: | |
| contents: read | |
| id-token: write | |
| jobs: | |
| build: | |
| environment: prod | |
| runs-on: ubuntu-latest-dex-builder | |
| # Only run the scheduled workflows on the main repo. | |
| if: github.repository == 'mdn/dex' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| path: mdn/dex | |
| persist-credentials: false | |
| - name: Checkout (content) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} | |
| with: | |
| repository: mdn/content | |
| path: mdn/content | |
| # Yes, this means fetch EVERY COMMIT EVER. | |
| # It's probably not sustainable in the far future (e.g. past 2021) | |
| # but for now it's good enough. We'll need all the history | |
| # so we can figure out each document's last-modified date. | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Checkout (blog) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| with: | |
| repository: mdn/blog | |
| path: mdn/blog | |
| lfs: true | |
| token: ${{ secrets.MDN_STUDIO_PAT }} | |
| persist-credentials: false | |
| - name: Checkout (generic-content) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| with: | |
| repository: mdn/generic-content | |
| path: mdn/generic-content | |
| persist-credentials: false | |
| - name: Checkout (curriculum) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| with: | |
| repository: mdn/curriculum | |
| path: mdn/curriculum | |
| persist-credentials: false | |
| - name: Checkout (translated-content) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} | |
| with: | |
| repository: mdn/translated-content | |
| path: mdn/translated-content | |
| # See matching warning for mdn/content checkout step | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Checkout (translated-content-de) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} | |
| with: | |
| repository: mdn/translated-content-de | |
| path: mdn/translated-content-de | |
| persist-credentials: false | |
| - name: Move de into translated-content | |
| if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} | |
| run: | | |
| mv mdn/translated-content-de/files/de mdn/translated-content/files/ | |
| rm -rf mdn/translated-content-de | |
| - name: Clean and commit de | |
| if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} | |
| working-directory: mdn/translated-content | |
| run: | | |
| git add files/de | |
| git -c user.name='MDN' -c user.email='mdn-dev@mozilla.com' commit -m 'de' | |
| - name: Checkout (mdn-contributor-spotlight) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| with: | |
| repository: mdn/mdn-contributor-spotlight | |
| path: mdn/mdn-contributor-spotlight | |
| persist-credentials: false | |
| - name: Checkout (fred) | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| with: | |
| repository: mdn/fred | |
| path: mdn/fred | |
| persist-credentials: false | |
| - name: Setup Node (fred) | |
| if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version-file: mdn/fred/.nvmrc | |
| package-manager-cache: false | |
| - name: Install (fred) | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/fred | |
| env: | |
| # Use a GITHUB_TOKEN to bypass rate limiting for rari. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: npm ci | |
| - name: Setup Python | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.10" | |
| - name: Setup Poetry | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 | |
| - name: Install (deployer) | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/dex/deployer | |
| run: poetry install | |
| - name: Display Python & Poetry version | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/dex/deployer | |
| run: | | |
| python --version | |
| poetry --version | |
| - name: Print information about build | |
| env: | |
| NOTES: ${{ github.event.inputs.notes }} | |
| run: | | |
| echo "notes: $NOTES" | |
| - name: Print information about CPU | |
| run: cat /proc/cpuinfo | |
| - name: Build (rari) | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/fred | |
| env: | |
| # Remember, the mdn/content repo got cloned into `pwd` into a | |
| # sub-folder called "mdn/content" | |
| CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files | |
| CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files | |
| CONTRIBUTOR_SPOTLIGHT_ROOT: ${{ github.workspace }}/mdn/mdn-contributor-spotlight/contributors | |
| BLOG_ROOT: ${{ github.workspace }}/mdn/blog/content/posts | |
| CURRICULUM_ROOT: ${{ github.workspace }}/mdn/curriculum | |
| GENERIC_CONTENT_ROOT: ${{ github.workspace }}/mdn/generic-content/files | |
| # rari | |
| BASE_URL: "https://developer.mozilla.org" | |
| BUILD_OUT_ROOT: "out" | |
| LIVE_SAMPLES_BASE_URL: https://live.mdnplay.dev | |
| ADDITIONAL_LOCALES_FOR_GENERICS_AND_SPAS: de | |
| # Sentry. | |
| SENTRY_DSN_BUILD: ${{ secrets.SENTRY_DSN_BUILD }} | |
| SENTRY_ENVIRONMENT: prod | |
| SENTRY_RELEASE: ${{ github.sha }} | |
| # Increase GitHub API rate limit. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -eo pipefail | |
| npm run rari content sync-translated-content | |
| npm run rari git-history | |
| npm run rari build -- --all --issues "$BUILD_OUT_ROOT/issues.json" --templ-stats | |
| - name: Build (fred) | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/fred | |
| env: | |
| DEX_ROOT: ${{ github.workspace }}/mdn/dex | |
| BUILD_OUT_ROOT: "out" | |
| # This enables the MDN Plus | |
| REACT_APP_ENABLE_PLUS: true | |
| # This removes the ability to sign in | |
| REACT_APP_DISABLE_AUTH: false | |
| # The default is to always set no to robots. This deployment is the only | |
| # exception in the world where we actually want to welcome robots. | |
| BUILD_ALWAYS_ALLOW_ROBOTS: true | |
| FRED_ROBOTS_GLOBAL_ALLOW: true | |
| # Browser-compat data. | |
| REACT_APP_BCD_BASE_URL: https://bcd.developer.mozilla.org | |
| FRED_BCD_BASE_URL: https://bcd.developer.mozilla.org | |
| # Offline updates | |
| REACT_APP_UPDATES_BASE_URL: https://updates.developer.mozilla.org | |
| # Firefox Accounts and SubPlat settings | |
| REACT_APP_FXA_SIGNIN_URL: /users/fxa/login/authenticate/ | |
| REACT_APP_FXA_SETTINGS_URL: https://accounts.firefox.com/settings/ | |
| REACT_APP_MDN_PLUS_SUBSCRIBE_URL: https://accounts.firefox.com/subscriptions/products/prod_LKvr8fYGbBxcaZ | |
| REACT_APP_FXA_MANAGE_SUBSCRIPTIONS_URL: https://subscriptions.firefox.com/subscriptions/ | |
| REACT_APP_MDN_PLUS_5M_PLAN: price_1KeG02JNcmPzuWtR1oBrw8o6 | |
| REACT_APP_MDN_PLUS_5Y_PLAN: price_1KeG02JNcmPzuWtRslZijhQu | |
| REACT_APP_MDN_PLUS_10M_PLAN: price_1KeG02JNcmPzuWtRuAnIgNHh | |
| REACT_APP_MDN_PLUS_10Y_PLAN: price_1KeG02JNcmPzuWtRlrSiLTI6 | |
| # Support for SP3 | |
| REACT_APP_MDN_PLUS_SUBSCRIBE_URL_SP3_BASE: https://payments.firefox.com | |
| REACT_APP_MDN_PLUS_5M_SP3_ID: mdnplus5m | |
| REACT_APP_MDN_PLUS_5Y_SP3_ID: mdnplus5y | |
| REACT_APP_MDN_PLUS_10M_SP3_ID: mdnplus10m | |
| REACT_APP_MDN_PLUS_10Y_SP3_ID: mdnplus10y | |
| # Telemetry. | |
| REACT_APP_GLEAN_CHANNEL: prod | |
| REACT_APP_GLEAN_ENABLED: true | |
| FRED_GLEAN_CHANNEL: prod | |
| FRED_GLEAN_ENABLED: true | |
| # Transcend Consent Management | |
| FRED_TRANSCEND_AIRGAP_URL: https://transcend-cdn.com/cm/${{ secrets.TRANSCEND_BUNDLE_ID }}/airgap.js | |
| FRED_TRANSCEND_BUNDLE_ID: ${{ secrets.TRANSCEND_BUNDLE_ID }} | |
| # Newsletter | |
| REACT_APP_NEWSLETTER_ENABLED: true | |
| # Placement | |
| REACT_APP_PLACEMENT_ENABLED: true | |
| # Playground | |
| REACT_APP_PLAYGROUND_BASE_HOST: mdnplay.dev | |
| FRED_PLAYGROUND_BASE_HOST: mdnplay.dev | |
| # Observatory | |
| REACT_APP_OBSERVATORY_API_URL: https://observatory-api.mdn.mozilla.net | |
| FRED_OBSERVATORY_API_URL: https://observatory-api.mdn.mozilla.net | |
| # Sentry. | |
| SENTRY_DSN_BUILD: ${{ secrets.SENTRY_DSN_BUILD }} | |
| SENTRY_ENVIRONMENT: prod | |
| SENTRY_RELEASE: ${{ github.sha }} | |
| # AI Help. | |
| REACT_APP_AI_FEEDBACK_GITHUB_REPO: mdn/ai-feedback | |
| # Increase GitHub API rate limit. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -eo pipefail | |
| # SSR all pages | |
| npm run build | |
| node build/ssr.js | |
| cp -r "$DEX_ROOT/client/public/assets" "$BUILD_OUT_ROOT" | |
| cp "$DEX_ROOT/assets/prod/robots.txt" "$BUILD_OUT_ROOT/robots.txt" | |
| - name: Setup Node (dex) | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version-file: mdn/dex/.nvmrc | |
| package-manager-cache: false | |
| - name: Install (dex) | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/dex | |
| run: npm ci | |
| env: | |
| # Use a GITHUB_TOKEN to bypass rate limiting for rari. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Run dex scripts | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/dex | |
| env: | |
| FRED_ROOT: ${{ github.workspace }}/mdn/fred | |
| run: | | |
| set -eo pipefail | |
| # Generate whatsdeployed files. | |
| npm run tool:legacy -- whatsdeployed --output "$FRED_ROOT/out/_whatsdeployed/code.json" | |
| npm run tool:legacy -- whatsdeployed $CONTENT_ROOT --output "$FRED_ROOT/out/_whatsdeployed/content.json" | |
| npm run tool:legacy -- whatsdeployed $CONTENT_TRANSLATED_ROOT --output "$FRED_ROOT/out/_whatsdeployed/translated-content.json" | |
| # Sort DE search index by en-US popularity. | |
| node scripts/reorder-search-index.mjs "$FRED_ROOT/out/en-us/search-index.json" "$FRED_ROOT/out/de/search-index.json" | |
| - name: Update search index | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/dex/deployer | |
| env: | |
| DEPLOYER_ELASTICSEARCH_URL: ${{ secrets.DEPLOYER_PROD_ELASTICSEARCH_URL }} | |
| FRED_ROOT: ${{ github.workspace }}/mdn/fred | |
| run: poetry run deployer search-index "$FRED_ROOT/out" | |
| - name: Authenticate with GCP | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 | |
| with: | |
| token_format: access_token | |
| service_account: deploy-prod-content@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com | |
| workload_identity_provider: projects/${{ secrets.WIP_PROJECT_ID }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions | |
| - name: Setup gcloud | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1 | |
| - name: Sync build | |
| if: ${{ ! vars.SKIP_BUILD }} | |
| working-directory: mdn/fred | |
| run: |- | |
| time gsutil -q -m -h "Cache-Control: public, max-age=3600" cp -r out/static gs://${{ vars.GCP_BUCKET_NAME }}/main/ | |
| time gsutil -q -m -h "Cache-Control: public, max-age=3600" rsync -cr gs://${{ vars.GCP_BUCKET_NAME }}/main/static/ gs://${{ vars.GCP_BUCKET_NAME }}/fred/static/ | |
| time gsutil -q -m -h "Cache-Control: public, max-age=3600" rsync -cdrj html,json,txt -y "^static/" out gs://${{ vars.GCP_BUCKET_NAME }}/fred | |
| - name: Authenticate with GCP | |
| if: ${{ ! vars.SKIP_FUNCTION }} | |
| uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 | |
| with: | |
| token_format: access_token | |
| service_account: deploy-prod-prod-mdn-ingress@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com | |
| workload_identity_provider: projects/${{ secrets.WIP_PROJECT_ID }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions | |
| - name: Setup gcloud | |
| if: ${{ ! vars.SKIP_FUNCTION }} | |
| uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1 | |
| with: | |
| install_components: "beta" | |
| - name: Generate redirects map | |
| if: ${{ ! vars.SKIP_FUNCTION }} | |
| working-directory: mdn/dex/cloud-function | |
| env: | |
| CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files | |
| CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files | |
| FRED_ROOT: ${{ github.workspace }}/mdn/fred | |
| run: |- | |
| mkdir -p ../client/build/ | |
| cp "$FRED_ROOT/out/sitemap.txt" ../client/build/ | |
| npm ci | |
| npm run build-redirects | |
| npm run build-canonicals | |
| - name: Deploy Function | |
| if: ${{ ! vars.SKIP_FUNCTION }} | |
| working-directory: mdn/dex | |
| run: |- | |
| set -eo pipefail | |
| for region in europe-west1 us-west1 asia-east1; do | |
| gcloud beta functions deploy mdn-prod-prod-$region \ | |
| --gen2 \ | |
| --runtime=nodejs24 \ | |
| --region=$region \ | |
| --source=cloud-function \ | |
| --trigger-http \ | |
| --allow-unauthenticated \ | |
| --entry-point=mdnHandler \ | |
| --concurrency=100 \ | |
| --min-instances=10 \ | |
| --max-instances=1000 \ | |
| --memory=2GB \ | |
| --timeout=120s \ | |
| --run-service-account=run-prod-prod-functions@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com \ | |
| --set-env-vars="IGNORED_ROUTES=" \ | |
| --set-env-vars="ORIGIN_MAIN=developer.mozilla.org" \ | |
| --set-env-vars="ORIGIN_LIVE_SAMPLES=live.mdnplay.dev" \ | |
| --set-env-vars="ORIGIN_PLAY=mdnplay.dev" \ | |
| --set-env-vars="SOURCE_CONTENT=https://storage.googleapis.com/${{ vars.GCP_BUCKET_NAME }}/fred/" \ | |
| --set-env-vars="SOURCE_API=https://api.developer.mozilla.org/" \ | |
| --set-env-vars="SENTRY_DSN=${{ secrets.SENTRY_DSN_CLOUD_FUNCTION }}" \ | |
| --set-env-vars="SENTRY_ENVIRONMENT=prod" \ | |
| --set-env-vars="SENTRY_TRACES_SAMPLE_RATE=${{ vars.SENTRY_TRACES_SAMPLE_RATE }}" \ | |
| --set-env-vars="SENTRY_RELEASE=${{ github.sha }}" \ | |
| --set-secrets="SIGN_SECRET=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/prod-sign-secret/versions/latest" \ | |
| --set-secrets="BSA_ZONE_KEYS=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/prod-bsa-zone-keys/versions/latest" \ | |
| 2>&1 | sed "s/^/[$region] /" & | |
| pids+=($!) | |
| done | |
| for pid in "${pids[@]}"; do | |
| wait $pid | |
| done | |
| - name: Update AI Help index with macros | |
| working-directory: mdn/dex | |
| run: npm run ai-help-macros -- update-index "$FRED_ROOT/out/en-us/docs" | |
| env: | |
| OPENAI_KEY: ${{ secrets.OPENAI_KEY }} | |
| PG_URI: ${{ secrets.PG_URI }} | |
| FRED_ROOT: ${{ github.workspace }}/mdn/fred | |
| - name: Slack Notification | |
| if: failure() | |
| uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2 | |
| env: | |
| SLACK_CHANNEL: mdn-notifications | |
| SLACK_COLOR: ${{ job.status }} | |
| SLACK_ICON: https://avatars.slack-edge.com/2020-11-17/1513880588420_fedd7f0e9456888e69ff_96.png | |
| SLACK_TITLE: ":rotating_light: Prod :rotating_light:" | |
| SLACK_MESSAGE: "Build failed :collision:" | |
| SLACK_FOOTER: "Powered by prod-build.yml" | |
| SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} | |
| - name: Invalidate Google Cloud CDN | |
| if: ${{ github.event.inputs.invalidate }} | |
| run: gcloud compute url-maps invalidate-cdn-cache ${{ secrets.GCP_LOAD_BALANCER_NAME }} --path "/*" --async |