diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml new file mode 100644 index 000000000..00e0930a4 --- /dev/null +++ b/.github/workflows/build-and-deploy.yml @@ -0,0 +1,95 @@ +name: Build and deploy + +on: + push: + branches: [main] + + workflow_dispatch: + inputs: + environment: + description: 'Environment to deploy (leave empty for all)' + required: false + default: '' + type: choice + options: + - '' + - dev + - staging + - prod + +jobs: + setup-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - id: set-matrix + run: | + if [ -z "${{ inputs.environment }}" ]; then + echo 'matrix={"include":[{"env":"dev"},{"env":"staging"},{"env":"prod"}]}' >> $GITHUB_OUTPUT + else + echo 'matrix={"include":[{"env":"${{ inputs.environment }}"}]}' >> $GITHUB_OUTPUT + fi + + build: + needs: setup-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + uses: ./.github/workflows/reusable-build.yml + secrets: inherit + with: + environment: ${{ matrix.env }} + build-command: ${{ matrix.env == 'prod' && 'build' || format('build:{0}', matrix.env) }} + artifact-name: ${{ matrix.env }} + + build-storybook: + needs: setup-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + uses: ./.github/workflows/reusable-build-storybook.yml + secrets: inherit + with: + environment: ${{ matrix.env }} + artifact-name: ${{ matrix.env }} + + upload-sourcemaps: + needs: [setup-matrix, build] + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + uses: ./.github/workflows/reusable-upload-sourcemaps.yml + secrets: inherit + with: + environment: ${{ matrix.env }} + artifact-name: ${{ matrix.env }} + + upload-code: + needs: [setup-matrix, build, build-storybook] + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + uses: ./.github/workflows/reusable-upload-code.yml + secrets: inherit + with: + environment: ${{ matrix.env }} + artifact-name: ${{ matrix.env }} + s3-destination: ${{ format('s3://permanent-repos/{0}/mdot.tar.gz', matrix.env) }} + + deploy: + needs: [setup-matrix, upload-code, upload-sourcemaps] + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + runs-on: ubuntu-latest + environment: ${{ matrix.env }} + steps: + - name: Trigger ${{ matrix.env }} deploy + if: matrix.env == 'dev' + run: | + curl -X POST \ + -H 'Accept: application/vnd.github.v3+json' \ + -H 'Authorization: Bearer ${{ secrets.PERMANENT_DEPLOY_PAT }}' \ + https://api.github.com/repos/PermanentOrg/infrastructure/actions/workflows/deploy-dev.yml/dispatches \ + -d '{"ref":"main"}' diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml deleted file mode 100644 index 1dda4e855..000000000 --- a/.github/workflows/build-dev.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Dev build and deploy - -on: - push: - branches: [main] - - workflow_dispatch: - -jobs: - build: - uses: ./.github/workflows/build.yml - secrets: - google-api-key: ${{ secrets.GOOGLE_API_KEY_DEV }} - firebase-api-key: ${{ secrets.FIREBASE_API_KEY_DEV }} - stripe-api-key: ${{ secrets.STRIPE_TEST_KEY }} - recaptcha-api-key: ${{ secrets.RECAPTCHA_API_KEY_DEV }} - stela-domain: ${{ secrets.STELA_DOMAIN_DEV }} - mixpanel-token: ${{ secrets.MIXPANEL_TOKEN_DEV }} - fusionauth-host: ${{ secrets.FUSIONAUTH_HOST_DEV }} - fontawesome-token: ${{ secrets.FONTAWESOME_PACKAGE_TOKEN }} - with: - build-command: 'build:dev' - artifact-name: 'dev' - - build-storybook: - uses: ./.github/workflows/build-storybook.yml - secrets: inherit - with: - artifact-name: 'dev' - - upload-sourcemaps: - needs: build - uses: ./.github/workflows/upload-sourcemaps.yml - secrets: inherit - with: - artifact-name: 'dev' - - upload-code: - needs: [build, build-storybook] - uses: ./.github/workflows/upload-code.yml - secrets: inherit - with: - artifact-name: 'dev' - s3-destination: 's3://permanent-repos/dev/mdot.tar.gz' - - deploy: - needs: [upload-code, upload-sourcemaps] - runs-on: ubuntu-latest - steps: - - name: Trigger dev deploy - run: curl -X POST -H 'Accept:application/vnd.github.v3+json' -H 'Authorization:Bearer ${{ secrets.PERMANENT_DEPLOY_PAT }}' https://api.github.com/repos/PermanentOrg/infrastructure/actions/workflows/deploy-dev.yml/dispatches -d '{"ref":"main"}' diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml deleted file mode 100644 index 1c73fd867..000000000 --- a/.github/workflows/build-prod.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Prod build and upload - -on: - push: - branches: [main] - workflow_dispatch: - -jobs: - build: - uses: ./.github/workflows/build.yml - secrets: - google-api-key: ${{ secrets.GOOGLE_API_KEY_PROD }} - firebase-api-key: ${{ secrets.FIREBASE_API_KEY_PROD }} - stripe-api-key: ${{ secrets.STRIPE_LIVE_KEY }} - recaptcha-api-key: ${{ secrets.RECAPTCHA_API_KEY }} - fusionauth-host: ${{ secrets.FUSIONAUTH_PROD_HOST }} - stela-domain: ${{ secrets.STELA_DOMAIN_PROD }} - mixpanel-token: ${{ secrets.MIXPANEL_TOKEN_PROD }} - fontawesome-token: ${{ secrets.FONTAWESOME_PACKAGE_TOKEN }} - with: - build-command: 'build' - artifact-name: 'prod' - - build-storybook: - uses: ./.github/workflows/build-storybook.yml - secrets: inherit - with: - artifact-name: 'prod' - - upload-sourcemaps: - needs: build - uses: ./.github/workflows/upload-sourcemaps.yml - secrets: inherit - with: - artifact-name: 'prod' - - upload-code: - needs: [build, build-storybook] - uses: ./.github/workflows/upload-code.yml - secrets: inherit - with: - artifact-name: 'prod' - s3-destination: 's3://permanent-repos/prod/mdot.tar.gz' diff --git a/.github/workflows/build-staging.yml b/.github/workflows/build-staging.yml deleted file mode 100644 index 27cb959f4..000000000 --- a/.github/workflows/build-staging.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Staging build and upload - -on: - push: - branches: [main] - - workflow_dispatch: - -jobs: - build: - uses: ./.github/workflows/build.yml - secrets: - google-api-key: ${{ secrets.GOOGLE_API_KEY_STAGING }} - firebase-api-key: ${{ secrets.FIREBASE_API_KEY_STAGING }} - stripe-api-key: ${{ secrets.STRIPE_TEST_KEY }} - recaptcha-api-key: ${{ secrets.RECAPTCHA_API_KEY_DEV }} - stela-domain: ${{ secrets.STELA_DOMAIN_STAGING }} - mixpanel-token: ${{ secrets.MIXPANEL_TOKEN_DEV }} - fusionauth-host: ${{ secrets.FUSIONAUTH_HOST_DEV }} - fontawesome-token: ${{ secrets.FONTAWESOME_PACKAGE_TOKEN }} - with: - build-command: 'build:staging' - artifact-name: 'staging' - - build-storybook: - uses: ./.github/workflows/build-storybook.yml - secrets: inherit - with: - artifact-name: 'staging' - - upload-sourcemaps: - needs: build - uses: ./.github/workflows/upload-sourcemaps.yml - secrets: inherit - with: - artifact-name: 'staging' - - upload-code: - needs: [build, build-storybook] - uses: ./.github/workflows/upload-code.yml - secrets: inherit - with: - artifact-name: 'staging' - s3-destination: 's3://permanent-repos/staging/mdot.tar.gz' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 40660247e..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Build deployable images -on: - workflow_call: - secrets: - google-api-key: - description: 'Google API key' - required: true - firebase-api-key: - description: 'Firebase API key' - required: true - stripe-api-key: - description: 'Stripe API key' - required: true - recaptcha-api-key: - description: 'Recaptcha API key' - required: true - stela-domain: - description: 'Domain to route Stela requests to' - required: true - mixpanel-token: - description: 'Mixpanel API key' - required: true - fusionauth-host: - description: 'FusionAuth host' - required: false - fontawesome-token: - description: 'FontAwesome package token' - required: true - inputs: - build-command: - description: 'The npm action that is run to build the Angular application.' - default: 'build' - required: false - type: string - artifact-name: - description: 'Artifact name to be downloaded later' - required: true - type: string -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - - uses: actions/setup-node@v6 - with: - node-version-file: .node-version - - - uses: actions/cache@v5 - with: - path: node_modules - key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node_modules- - - - name: Install dependencies - run: FONTAWESOME_PACKAGE_TOKEN="${{ secrets.fontawesome-token }}" npm install - - - name: Build - env: - GOOGLE_API_KEY: ${{ secrets.google-api-key }} - FIREBASE_API_KEY: ${{ secrets.firebase-api-key }} - STRIPE_API_KEY: ${{ secrets.stripe-api-key }} - RECAPTCHA_API_KEY: ${{ secrets.recaptcha-api-key }} - STELA_DOMAIN: ${{ secrets.stela-domain }} - MIXPANEL_TOKEN: ${{ secrets.mixpanel-token }} - FUSIONAUTH_HOST: ${{ secrets.fusionauth-host }} - run: npm run ${{ inputs.build-command }} - - - name: Archive `dist` - uses: actions/upload-artifact@v6 - with: - name: ${{ inputs.artifact-name }} - path: dist - retention-days: 5 diff --git a/.github/workflows/lint.yml b/.github/workflows/ci.yml similarity index 61% rename from .github/workflows/lint.yml rename to .github/workflows/ci.yml index 8d557e2fc..3414ef23b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ -name: lint +name: CI + on: pull_request: workflow_dispatch: @@ -19,6 +20,7 @@ jobs: node-version-file: .node-version - name: Lint GitHub Action workflows uses: raven-actions/actionlint@v2 + eslint: runs-on: ubuntu-latest steps: @@ -26,8 +28,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version-file: .node-version + cache: 'npm' - run: npm ci - run: npm run lint:eslint + prettier: runs-on: ubuntu-latest steps: @@ -35,8 +39,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version-file: .node-version + cache: 'npm' - run: npm ci - run: npm run lint:prettier + tsc: runs-on: ubuntu-latest steps: @@ -44,5 +50,26 @@ jobs: - uses: actions/setup-node@v6 with: node-version-file: .node-version + cache: 'npm' - run: npm ci - run: npm run lint:tsc + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v6 + with: + node-version-file: .node-version + cache: 'npm' + - run: npm ci + - name: Run unit tests + env: + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY_DEV }} + FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY_DEV }} + STRIPE_API_KEY: ${{ secrets.STRIPE_TEST_KEY }} + run: npm run coverage:ci + - name: Upload report to codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/build-storybook.yml b/.github/workflows/reusable-build-storybook.yml similarity index 69% rename from .github/workflows/build-storybook.yml rename to .github/workflows/reusable-build-storybook.yml index bb91cbb43..cc8edbf02 100644 --- a/.github/workflows/build-storybook.yml +++ b/.github/workflows/reusable-build-storybook.yml @@ -2,29 +2,30 @@ name: Build static Storybook on: workflow_call: inputs: + environment: + description: 'GitHub Environment to use for secrets' + required: true + type: string artifact-name: description: 'Artifact name to be downloaded later. "-storybook" is appended to the end.' required: true type: string + jobs: build-storybook: runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + env: + FONTAWESOME_PACKAGE_TOKEN: ${{ secrets.FONTAWESOME_PACKAGE_TOKEN }} steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v6 with: node-version-file: .node-version + cache: 'npm' - - uses: actions/cache@v5 - with: - path: node_modules - key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node_modules- - - - name: Install dependencies - run: FONTAWESOME_PACKAGE_TOKEN="${{ secrets.FONTAWESOME_PACKAGE_TOKEN }}" npm install + - run: npm ci - name: Build Storybook run: npm run build-storybook diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml new file mode 100644 index 000000000..26af51ee9 --- /dev/null +++ b/.github/workflows/reusable-build.yml @@ -0,0 +1,60 @@ +name: Build deployable images +on: + workflow_call: + inputs: + environment: + description: 'GitHub Environment to use for secrets' + required: true + type: string + build-command: + description: 'The npm action that is run to build the Angular application.' + default: 'build' + required: false + type: string + artifact-name: + description: 'Artifact name to be downloaded later' + required: true + type: string + +jobs: + build: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + env: + FONTAWESOME_PACKAGE_TOKEN: ${{ secrets.FONTAWESOME_PACKAGE_TOKEN }} + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v6 + with: + node-version-file: .node-version + cache: 'npm' + + - run: npm ci + + - name: Cache Angular build + uses: actions/cache@v5 + with: + path: .angular/cache + key: ${{ runner.os }}-angular-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('src/**') }} + restore-keys: | + ${{ runner.os }}-angular-${{ hashFiles('**/package-lock.json') }}- + ${{ runner.os }}-angular- + + - name: Build + env: + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} + FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }} + STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} + RECAPTCHA_API_KEY: ${{ secrets.RECAPTCHA_API_KEY }} + STELA_DOMAIN: ${{ secrets.STELA_DOMAIN }} + MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }} + FUSIONAUTH_HOST: ${{ secrets.FUSIONAUTH_HOST }} + run: npm run ${{ inputs.build-command }} + + - name: Archive `dist` + uses: actions/upload-artifact@v6 + with: + name: ${{ inputs.artifact-name }} + path: dist + retention-days: 5 diff --git a/.github/workflows/upload-code.yml b/.github/workflows/reusable-upload-code.yml similarity index 88% rename from .github/workflows/upload-code.yml rename to .github/workflows/reusable-upload-code.yml index e73c9fb68..a3eaecb5f 100644 --- a/.github/workflows/upload-code.yml +++ b/.github/workflows/reusable-upload-code.yml @@ -2,6 +2,10 @@ name: Upload Built Artifacts on: workflow_call: inputs: + environment: + description: 'GitHub Environment to use for secrets' + required: true + type: string artifact-name: description: 'Name of artifact to download' required: true @@ -14,6 +18,7 @@ on: jobs: upload-code: runs-on: ubuntu-latest + environment: ${{ inputs.environment }} steps: - name: Download dist uses: actions/download-artifact@v7 diff --git a/.github/workflows/upload-sourcemaps.yml b/.github/workflows/reusable-upload-sourcemaps.yml similarity index 88% rename from .github/workflows/upload-sourcemaps.yml rename to .github/workflows/reusable-upload-sourcemaps.yml index 68cf50481..fea119c96 100644 --- a/.github/workflows/upload-sourcemaps.yml +++ b/.github/workflows/reusable-upload-sourcemaps.yml @@ -2,6 +2,10 @@ name: Upload Sourcemaps on: workflow_call: inputs: + environment: + description: 'GitHub Environment to use for secrets' + required: true + type: string artifact-name: description: 'Name of artifact to download' required: true @@ -10,6 +14,7 @@ on: jobs: upload-sourcemaps: runs-on: ubuntu-latest + environment: ${{ inputs.environment }} steps: - uses: actions/checkout@v5 with: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml deleted file mode 100644 index ea90e9fe5..000000000 --- a/.github/workflows/unit-tests.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Unit tests - -on: - pull_request: - - workflow_dispatch: - - push: - branches: - - main - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - - uses: actions/setup-node@v6 - with: - node-version-file: .node-version - - - uses: actions/cache@v5 - with: - path: node_modules - key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node_modules- - - - name: Install dependencies - run: FONTAWESOME_PACKAGE_TOKEN="${{ secrets.FONTAWESOME_PACKAGE_TOKEN }}" npm install - - - name: Run unit tests - env: - GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY_DEV }} - FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY_DEV }} - STRIPE_API_KEY: ${{ secrets.STRIPE_TEST_KEY }} - run: npm run coverage:ci - - - name: Upload report to codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/package.json b/package.json index 94864c2d1..a807ebd8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "mdot", "version": "2.13.5", + "packageManager": "npm", "engines": { "node": "24" },