public constructor()

in projenrc/build-workflow.ts [28:429]


  public constructor(project: typescript.TypeScriptProject, opts: BuildWorkflowOptions = {}) {
    const wf = project.github!.addWorkflow('build');
    wf.on({
      mergeGroup: {},
      pullRequest: {},
    });

    if (opts.defaultBranch !== null) {
      wf.on({
        push: {
          branches: [opts.defaultBranch ?? 'main', 'maintenance/*'],
        },
      });
    }

    const nodeVersion = project.minNodeVersion?.split('.', 1).at(0) ?? 'lts/*';

    wf.addJobs({
      'build': {
        env: { CI: 'true' },
        outputs: {
          'self-mutation-needed': {
            stepId: 'self-mutation',
            outputName: 'needed',
          },
          'release-line': {
            stepId: 'version',
            outputName: 'release-line',
          },
        },
        permissions: { contents: github.workflows.JobPermission.READ },
        runsOn: ['ubuntu-latest'],
        steps: [
          ACTIONS_CHECKOUT(undefined, { lfs: true }),
          ACTIONS_SETUP_NODE(),
          {
            name: 'Cache build outputs',
            if: "github.event_name == 'pull_request'",
            uses: 'actions/cache@v3',
            with: {
              'key':
                "build-outputs-${{ hashFiles('tsconfig.json', 'build-tools/**/*.ts', 'src/**/*.ts', 'package.json', 'yarn.lock') }}",
              'path': ['tsconfig.tsbuildinfo', 'lib/**/*'].join('\n'),
              'restore-keys': 'build-outputs-',
            },
          },
          YARN_INSTALL('--check-files'),
          {
            name: 'Compile',
            run: ['npx projen', 'npx projen pre-compile', 'npx projen compile', 'npx projen post-compile'].join(' && '),
          },

          // Run tests to allow self-mutation to be performed if needed...
          { name: 'Test', run: 'npx projen test' },
          {
            name: 'Find mutations',
            id: 'self-mutation',
            run: [
              'git add .',
              'git diff --cached --patch --exit-code > .repo.patch || echo "needed=true" >> $GITHUB_OUTPUT',
            ].join('\n'),
          },
          {
            name: 'Upload patch',
            if: 'steps.self-mutation.outputs.needed',
            uses: 'actions/upload-artifact@v4.3.6',
            with: {
              name: '.repo.patch',
              path: '.repo.patch',
              overwrite: true,
            },
          },
          {
            name: 'Fail if self-mutation is needed',
            if: 'steps.self-mutation.outputs.needed',
            run: [
              'echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch."',
              'cat .repo.patch',
              'exit 1',
            ].join('\n'),
          },

          // Upload artifacts...
          {
            name: 'Upload artifact',
            uses: 'actions/upload-artifact@v4.3.6',
            with: {
              name: 'build-output',
              path: [
                '${{ github.workspace }}',
                '${{ github.workspace }}/dist/private',
                // Exclude node_modules to reduce artifact size (we won't use those anyway)...
                '!${{ github.workspace }}/node_modules',
                '!${{ github.workspace }}/fixtures/node_modules',
              ].join('\n'),
              overwrite: true,
            },
          },
          {
            name: 'Version Line',
            id: 'version',
            run: [
              `VERSION=$(node -p "require('./lib/version.js').RELEASE_LINE")`,
              'echo "release-line=${VERSION}" >> $GITHUB_OUTPUT',
            ].join('\n'),
          },
        ],
      },
      'self-mutation': {
        env: { CI: 'true' },
        needs: ['build'],
        runsOn: ['ubuntu-latest'],
        permissions: { contents: github.workflows.JobPermission.WRITE },
        if: "always() && (github.event_name == 'pull_request') && needs.build.outputs.self-mutation-needed && (github.event.pull_request.head.repo.full_name == github.repository)",
        steps: [
          {
            name: 'Checkout',
            uses: 'actions/checkout@v3',
            with: {
              ref: '${{ github.event.pull_request.head.ref }}',
              repository: '${{ github.event.pull_request.head.repo.full_name }}',
              token: '${{ secrets.PROJEN_GITHUB_TOKEN }}',
            },
          },
          {
            name: 'Download patch',
            uses: 'actions/download-artifact@v4',
            with: {
              name: '.repo.patch',
              path: '${{ runner.temp }}',
            },
          },
          {
            name: 'Apply patch',
            run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."',
          },
          {
            name: 'Set git identity',
            run: ['git config user.name "github-actions"', 'git config user.email "github-actions@github.com"'].join(
              '\n',
            ),
          },
          {
            name: 'Push changes',
            run: [
              'git add .',
              'git commit -s -m "chore: self-mutation"',
              'git push origin HEAD:${{ github.event.pull_request.head.ref }}',
            ].join('\n'),
          },
        ],
      },
      'matrix-test': {
        env: { CI: 'true' },
        if: 'success()',
        strategy: {
          failFast: false,
          matrix: {
            domain: {
              'node-version': NodeRelease.ALL_RELEASES.flatMap((release) => {
                if (!release.supported || isOdd(release.majorVersion)) {
                  return [];
                }
                return [`${release.majorVersion}.x`];
              }),
            },
          },
        },
        name: 'test (node ${{ matrix.node-version }})',
        needs: ['build'],
        permissions: {},
        runsOn: ['ubuntu-latest'],
        steps: [
          {
            name: 'Download artifact',
            uses: 'actions/download-artifact@v4',
            with: { name: 'build-output', path: '${{ github.workspace }}' },
          },
          {
            name: 'Setup Node.js',
            uses: 'actions/setup-node@v4',
            with: {
              'node-version': '${{ matrix.node-version }}',
              'cache': 'yarn',
            },
          },
          {
            name: 'Install dependencies',
            run: 'yarn install --frozen-lockfile',
          },
          // Re-run post-compile to ensure /fixtures/ symlinks are correctly present...
          {
            name: 'Re-run post-compile',
            run: 'npx projen post-compile',
          },
          { name: 'Test', run: 'npx projen test' },
          {
            name: 'Assert clean working directory',
            run: 'git diff --cached --exit-code',
          },
        ],
      },
      'matrix-clear': {
        // This is a simple "join target" to simplify branch protection rules.
        env: { CI: 'true' },
        name: 'Unit Tests',
        needs: ['matrix-test'],
        permissions: {},
        runsOn: ['ubuntu-latest'],
        if: 'always()',
        steps: [
          {
            name: 'Build result',
            run: 'echo ${{needs.matrix-test.result}}',
          },
          {
            if: "${{ needs.matrix-test.result != 'success' }}",
            name: 'Set status based on matrix build',
            run: 'exit 1',
          },
        ],
      },
      'package': {
        env: { CI: 'true' },
        name: 'package',
        needs: ['build'],
        permissions: {},
        runsOn: ['ubuntu-latest'],
        steps: [
          {
            name: 'Download artifact',
            uses: 'actions/download-artifact@v4',
            with: { name: 'build-output', path: '${{ github.workspace }}' },
          },
          {
            name: 'Setup Node.js',
            uses: 'actions/setup-node@v4',
            with: {
              'node-version': nodeVersion,
              'cache': 'yarn',
            },
          },
          {
            name: 'Install dependencies',
            run: 'yarn install --frozen-lockfile',
          },
          {
            name: 'Package',
            run: 'npx projen package',
          },
          {
            name: 'Upload artifact',
            uses: 'actions/upload-artifact@v4.3.6',
            with: {
              name: 'release-package',
              path: '${{ github.workspace }}/dist',
              overwrite: true,
            },
          },
        ],
      },
      //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      // Integration-style tests (via the release tarball)
      //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      'install-test': {
        // Verifies the tarball can be installed & the CLI entry point can start (tested by `jsii --version`)
        env: { CI: 'true' },
        name: 'Install Test (${{ matrix.runs-on }} | node ${{ matrix.node-version }} | ${{ matrix.package-manager }})',
        needs: ['package'],
        permissions: {},
        runsOn: ['${{ matrix.runs-on }}'],
        strategy: {
          failFast: false,
          matrix: {
            domain: {
              'node-version': NodeRelease.ALL_RELEASES.filter((release) => release.supported).map(
                (release) => `${release.majorVersion}.x`,
              ),
              'package-manager': ['npm', 'yarn'],
              'runs-on': ['ubuntu-latest', 'windows-latest', 'macos-latest'],
            },
          },
        },
        steps: [
          ACTIONS_SETUP_NODE('${{ matrix.node-version }}', false),
          {
            name: 'Download Artifact',
            uses: 'actions/download-artifact@v4',
            with: {
              name: 'release-package',
              path: '${{ runner.temp }}/release-package',
            },
          },
          {
            name: 'Install from tarball (npm)',
            if: "runner.os != 'Windows' && matrix.package-manager == 'npm'",
            run: ['npm init -y', 'npm install ${{ runner.temp }}/release-package/js/jsii-*.tgz'].join('\n'),
          },
          {
            name: 'Install from tarball (yarn)',
            if: "runner.os != 'Windows' && matrix.package-manager == 'yarn'",
            run: ['yarn init -y', 'yarn add ${{ runner.temp }}/release-package/js/jsii-*.tgz'].join('\n'),
          },
          {
            name: 'Install from tarball (Windows, npm)',
            if: "runner.os == 'Windows' && matrix.package-manager == 'npm'",
            run: [
              'npm init -y',
              '$TARBALL = Get-ChildItem -Path "${{ runner.temp }}/release-package/js/jsii-*.tgz"',
              'npm install $TARBALL',
            ].join('\n'),
          },
          {
            name: 'Install from tarball (Windows, yarn)',
            if: "runner.os == 'Windows' && matrix.package-manager == 'yarn'",
            run: [
              'yarn init -y',
              '$TARBALL = Get-ChildItem -Path "${{ runner.temp }}/release-package/js/jsii-*.tgz"',
              'yarn add $TARBALL',
            ].join('\n'),
          },
          {
            name: 'Simple command',
            run: `./node_modules/.bin/jsii --version`,
          },
        ],
      },
      'pacmak-test': {
        // Verifies compilation artifacts can be processed by jsii-pacmak@1.X
        env: { CI: 'true' },
        name: 'Pacmak Test',
        needs: ['package'],
        permissions: {},
        runsOn: ['ubuntu-latest'],
        steps: [
          ACTIONS_SETUP_NODE(undefined, false),
          {
            name: 'Download Artifact',
            uses: 'actions/download-artifact@v4',
            with: {
              name: 'release-package',
              path: '${{ runner.temp }}/release-package',
            },
          },
          {
            name: 'Install from tarball',
            run: [
              'npm init -y',
              'npm install jsii-pacmak@1.x ${{ runner.temp }}/release-package/private/*.tgz',
              'npm ls --depth=0',
              './node_modules/.bin/jsii-pacmak --version',
            ].join('\n'),
          },
          {
            name: 'Run jsii-pacmak',
            run: './node_modules/.bin/jsii-pacmak --verbose --recurse ./node_modules/jsii-calc',
          },
        ],
      },
      'integ-clear': {
        // This is a simple "join target" to simplify branch protection rules.
        env: { CI: 'true' },
        name: 'Integration Tests',
        needs: ['install-test', 'pacmak-test'],
        permissions: {},
        runsOn: ['ubuntu-latest'],
        steps: [{ name: 'Done', run: 'echo OK' }],
      },
    });

    new BenchmarkTest(project, wf, { needs: ['build'], artifactName: 'build-output' });

    if (opts.autoMerge ?? true) {
      const autoMerge = project.github!.addWorkflow('auto-merge');
      autoMerge.runName = 'Enable "Merge when ready" on PR #${{ github.event.number }}';
      autoMerge.on({
        pullRequestTarget: {
          types: ['opened', 'reopened', 'ready_for_review'],
        },
      });
      autoMerge.addJob('enable-auto-merge', {
        env: { CI: 'true' },
        if: '!github.event.pull_request.draft',
        name: 'Enable "Merge when ready" for this PR',
        permissions: {
          pullRequests: github.workflows.JobPermission.WRITE,
          contents: github.workflows.JobPermission.WRITE,
        },
        runsOn: ['ubuntu-latest'],
        steps: [
          {
            uses: 'peter-evans/enable-pull-request-automerge@v2',
            with: {
              'token': '${{ secrets.PROJEN_GITHUB_TOKEN }}',
              'pull-request-number': '${{ github.event.number }}',
              'merge-method': 'squash',
            },
          },
        ],
      });
    }
  }