diff --git a/.github/workflows/browserstack.yml b/.github/workflows/browserstack.yml new file mode 100644 index 00000000..ad567597 --- /dev/null +++ b/.github/workflows/browserstack.yml @@ -0,0 +1,85 @@ +name: Browserstack + +on: + merge_group: + workflow_dispatch: + pull_request_target: + types: + - opened + - synchronize + push: + branches: + - master + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + +env: + NODE_VERSION: 18 + CACHE_KEY: '${{ github.event.pull_request.head.sha || github.ref }}-${{ github.run_id }}-${{ github.run_attempt }}' + +jobs: + authorize: + name: Authorize + environment: ${{ github.actor != 'dependabot[bot]' && github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && 'external' || 'internal' }} + runs-on: ubuntu-latest + steps: + - run: true + + build: + needs: authorize # Require approval before running on forked pull requests + + name: Build Package + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.ref }} + + - name: Build package + uses: ./.github/actions/build + with: + node: ${{ env.NODE_VERSION }} + + - name: Save build artifacts + uses: actions/cache/save@v3 + with: + path: . + key: ${{ env.CACHE_KEY }} + + browserstack: + needs: build # Only run if unit tests pass + + name: BrowserStack Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.ref }} + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + - name: Restore build artifacts + uses: actions/cache/restore@v3 + with: + path: . + key: ${{ env.CACHE_KEY }} + + - name: Run tests + shell: bash + run: npx concurrently --raw --kill-others --success first "npm:start" "wait-on http://127.0.0.1:3000/ && browserstack-cypress run --build-name ${{ github.event.pull_request.head.sha || github.ref }} --no-wrap --specs "cypress/integration/smoke-bs.test.ts"" + env: + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ce70e8f7..2c700ac0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -37,7 +37,7 @@ jobs: run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection. - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 72b2d9de..63ef1418 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,29 +2,97 @@ name: Publish Release on: workflow_dispatch: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' # Release versions - - '[0-9]+.[0-9]+.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' # Beta versions - - '[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' + inputs: + branch: + description: The branch to release from + required: true + default: master + version: + description: The version being published. This should be a valid semver version, such as `1.0.0`. + required: true + default: '' + type: string + dry-run: + type: boolean + description: Perform a publishing dry run. This will not publish the release, but will validate the release and log the commands that would be run. + default: false permissions: contents: read - packages: write + id-token: write # For publishing to NPM with provenance. Allows developers to run `npm audit signatures` and verify release signature of SDK. @see https://github.blog/2023-04-19-introducing-npm-package-provenance/ + packages: write # For cross-publishing to GitHub Packages registry. env: NODE_VERSION: 18 + NODE_ENV: development jobs: + configure: + name: Validate input parameters + runs-on: ubuntu-latest + + outputs: + vtag: ${{ steps.vtag.outputs.vtag }} # The fully constructed release tag to use for publishing + dry-run: ${{ steps.dry-run.outputs.dry-run }} # The dry-run flag to use for publishing, if applicable + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.inputs.branch }} + + # Configure for dry-run, if applicable. @see https://docs.npmjs.com/cli/v9/commands/npm-publish#dry-run + - id: dry-run + if: ${{ github.event.inputs.dry-run == 'true' }} + name: Configure for `--dry-run` + run: | + echo "dry-run=--dry-run" >> $GITHUB_ENV + echo "dry-run=--dry-run" >> $GITHUB_OUTPUT + + # Build the tag string from package.json version and release suffix. Produces something like `1.0.0-beta.1` for a beta, or `1.0.0` for a stable release. + - name: Build tag + id: vtag + run: | + PACKAGE_VERSION="${{ github.event.inputs.version }}" + echo "vtag=${PACKAGE_VERSION}" >> $GITHUB_ENV + echo "vtag=${PACKAGE_VERSION}" >> $GITHUB_OUTPUT + + # Ensure tag does not already exist. + - name: Validate version + uses: actions/github-script@v6 + env: + vtag: ${{ env.vtag }} + with: + script: | + const releaseMeta = github.rest.repos.listReleases.endpoint.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + }); + + const releases = await github.paginate(releaseMeta); + + for (const release of releases) { + if (release.name === process.env.vtag) { + throw new Error(`${process.env.vtag} already exists`); + } + } + + console.log(`${process.env.vtag} does not exist. Proceeding with release.`) + publish-npm: - name: 'NPM' + needs: configure + + name: Publish to NPM runs-on: ubuntu-latest environment: release steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.inputs.branch }} - name: Setup Node uses: actions/setup-node@v3 @@ -33,23 +101,25 @@ jobs: cache: npm - name: Install dependencies - run: npm ci --include=dev + run: npm ci - name: Publish release to NPM - run: npm publish + run: npm publish --provenance --tag ${{ needs.configure.outputs.vtag }} ${{ needs.configure.outputs.dry-run }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} publish-gh: - needs: publish-npm # Don't publish to GitHub Packages until publishing to NPM is successfully completed + needs: + - configure + - publish-npm # Don't publish to GitHub Packages until publishing to NPM is successfully completed - name: 'GitHub Packages' + name: Publish to GitHub Packages runs-on: ubuntu-latest environment: release steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3 @@ -59,9 +129,9 @@ jobs: cache: npm - name: Install dependencies - run: npm ci --include=dev + run: npm ci - name: Publish release to GitHub Packages - run: npm publish + run: npm publish --provenance --tag ${{ needs.configure.outputs.vtag }} ${{ needs.configure.outputs.dry-run }} env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index ce8607f4..a08d4518 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -41,7 +41,7 @@ jobs: - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group' run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection. - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 33f2871d..15b31773 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -39,7 +39,7 @@ jobs: - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group' run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection. - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7bcd6c1a..17c99773 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,10 +3,9 @@ name: Build and Test on: merge_group: workflow_dispatch: - pull_request_target: - types: - - opened - - synchronize + pull_request: + branches: + - master push: branches: - master @@ -20,25 +19,16 @@ concurrency: env: NODE_VERSION: 18 - CACHE_KEY: '${{ github.event.pull_request.head.sha || github.ref }}-${{ github.run_id }}-${{ github.run_attempt }}' + CACHE_KEY: '${{ github.ref }}-${{ github.run_id }}-${{ github.run_attempt }}' jobs: - authorize: - name: Authorize - environment: ${{ github.actor != 'dependabot[bot]' && github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && 'external' || 'internal' }} - runs-on: ubuntu-latest - steps: - - run: true - build: - needs: authorize # Require approval before running on forked pull requests - name: Build Package runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} @@ -61,7 +51,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} @@ -91,7 +81,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} @@ -112,34 +102,3 @@ jobs: - name: Run tests run: npm run test:integration - - browserstack: - needs: unit # Only run if unit tests pass - - name: BrowserStack Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha || github.ref }} - - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: npm - - - name: Restore build artifacts - uses: actions/cache/restore@v3 - with: - path: . - key: ${{ env.CACHE_KEY }} - - - name: Run tests - shell: bash - run: npx concurrently --raw --kill-others --success first "npm:start" "wait-on http://127.0.0.1:3000/ && browserstack-cypress run --build-name ${{ github.event.pull_request.head.sha || github.ref }} --no-wrap --specs "cypress/integration/smoke-bs.test.ts"" - env: - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}