diff --git a/.github/workflows/conventional-commits-lint.js b/.github/workflows/conventional-commits-lint.js new file mode 100644 index 0000000..da9b388 --- /dev/null +++ b/.github/workflows/conventional-commits-lint.js @@ -0,0 +1,101 @@ +"use strict"; + +const fs = require("fs"); + +const TITLE_PATTERN = + /^(?[^:!(]+)(?\([^)]+\))?(?[!])?:.+$/; +const RELEASE_AS_DIRECTIVE = /^\s*Release-As:/im; +const BREAKING_CHANGE_DIRECTIVE = /^\s*BREAKING[ \t]+CHANGE:/im; + +const ALLOWED_CONVENTIONAL_COMMIT_PREFIXES = [ + "feat", + "fix", + "ci", + "docs", + "chore", +]; + +const object = process.argv[2]; +const payload = JSON.parse(fs.readFileSync(process.stdin.fd, "utf-8")); + +let validate = []; + +if (object === "pr") { + validate.push({ + title: payload.pull_request.title, + content: payload.pull_request.body, + }); +} else if (object === "push") { + validate.push( + ...payload.commits.map((commit) => ({ + title: commit.message.split("\n")[0], + content: commit.message, + })), + ); +} else { + console.error( + `Unknown object for first argument "${object}", use 'pr' or 'push'.`, + ); + process.exit(0); +} + +let failed = false; + +validate.forEach((payload) => { + if (payload.title) { + const { groups } = payload.title.match(TITLE_PATTERN); + + if (groups) { + if (groups.breaking) { + console.error( + `PRs are not allowed to declare breaking changes at this stage of the project. Please remove the ! in your PR title or commit message and adjust the functionality to be backward compatible.`, + ); + failed = true; + } + + if ( + !ALLOWED_CONVENTIONAL_COMMIT_PREFIXES.find( + (prefix) => prefix === groups.prefix, + ) + ) { + console.error( + `PR (or a commit in it) is using a disallowed conventional commit prefix ("${groups.prefix}"). Only ${ALLOWED_CONVENTIONAL_COMMIT_PREFIXES.join(", ")} are allowed. Make sure the prefix is lowercase!`, + ); + failed = true; + } + + if (groups.package && groups.prefix !== "chore") { + console.warn( + "Avoid using package specifications in PR titles or commits except for the `chore` prefix.", + ); + } + } else { + console.error( + "PR or commit title must match conventional commit structure.", + ); + failed = true; + } + } + + if (payload.content) { + if (payload.content.match(RELEASE_AS_DIRECTIVE)) { + console.error( + "PR descriptions or commit messages must not contain Release-As conventional commit directives.", + ); + failed = true; + } + + if (payload.content.match(BREAKING_CHANGE_DIRECTIVE)) { + console.error( + "PR descriptions or commit messages must not contain a BREAKING CHANGE conventional commit directive. Please adjust the functionality to be backward compatible.", + ); + failed = true; + } + } +}); + +if (failed) { + process.exit(1); +} + +process.exit(0); diff --git a/.github/workflows/conventional-commits.yml b/.github/workflows/conventional-commits.yml new file mode 100644 index 0000000..f91c9b0 --- /dev/null +++ b/.github/workflows/conventional-commits.yml @@ -0,0 +1,43 @@ +name: Check pull requests + +on: + push: + branches-ignore: # Run the checks on all branches but the protected ones + - master + - release/* + + pull_request_target: + branches: + - master + - release/* + types: + - opened + - edited + - reopened + - ready_for_review + +jobs: + check-conventional-commits: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: | + .github + + - if: ${{ github.event_name == 'pull_request_target' }} + run: | + set -ex + + node .github/workflows/conventional-commits-lint.js pr < x.match(/[0-9]+[.][0-9]+[.][0-9]+/)))") + + # Use git describe tags to identify the number of commits the branch + # is ahead of the most recent non-release-candidate tag, which is + # part of the rc. value. + RELEASE_VERSION=$MAIN_RELEASE_VERSION-rc.$(node -e "console.log('$(git describe --tags --exclude *rc*)'.split('-')[1])") + + # release-please only ignores releases that have a form like [A-Z0-9], so prefixing with rc + RELEASE_NAME="rc$RELEASE_VERSION" + else + MAIN_RELEASE_VERSION=${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} + RELEASE_VERSION="$MAIN_RELEASE_VERSION" + RELEASE_NAME="v$RELEASE_VERSION" + fi + + echo "MAIN_RELEASE_VERSION=${MAIN_RELEASE_VERSION}" >> "${GITHUB_ENV}" + echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_ENV}" + echo "RELEASE_CANDIDATE=${RELEASE_CANDIDATE}" >> "${GITHUB_ENV}" + echo "RELEASE_NAME=${RELEASE_NAME}" >> "${GITHUB_ENV}" + + echo "MAIN_RELEASE_VERSION=${MAIN_RELEASE_VERSION}" >> "${GITHUB_OUTPUT}" + echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_OUTPUT}" + echo "RELEASE_CANDIDATE=${RELEASE_CANDIDATE}" >> "${GITHUB_OUTPUT}" + echo "RELEASE_NAME=${RELEASE_NAME}" >> "${GITHUB_OUTPUT}" + + - uses: actions/setup-node@v4 + if: ${{ steps.release.outputs.release_created == 'true' || steps.release.outputs.prs_created == 'true' }} + with: + node-version: 20 + + - name: Build release artifacts + if: ${{ steps.release.outputs.release_created == 'true' || steps.release.outputs.prs_created == 'true' }} + run: | + set -ex + + echo "export const VERSION = '$RELEASE_VERSION';" > src/version.ts + + npm ci + npm run build + + for f in package.json package-lock.json + do + sed -i 's|"version": "0.0.0",|"version": "'"$RELEASE_VERSION"'",|g' "$f" + done + + echo '//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}' > ~/.npmrc + + DIST_TAG=patched + if [ "$RELEASE_CANDIDATE" == "true" ] + then + DIST_TAG=rc + elif [ "$GITHUB_REF" == "refs/heads/main" ] && [ "$GITHUB_REF" == "refs/heads/master" ] + then + # This is the main branch and it's not a prerelease, so the dist-tag should be `latest`. + DIST_TAG=latest + fi + + echo "Publishing @supabase/ssr now..." + + npm publish --tag "$DIST_TAG" + + - name: Create GitHub release and branches + if: ${{ steps.release.outputs.release_created == 'true' || steps.release.outputs.prs_created == 'true' }} + run: | + set -ex + + if [ "$RELEASE_CANDIDATE" == "true" ] + then + PR_NUMBER='${{ steps.release.outputs.pr && fromJSON(steps.release.outputs.pr).number }}' + + GH_TOKEN='${{ github.token }}' gh release \ + create $RELEASE_NAME \ + --title "v$RELEASE_VERSION" \ + --prerelease \ + -n "This is a release candidate. See release-please PR #$PR_NUMBER for context." + + GH_TOKEN='${{ github.token }}' gh pr comment "$PR_NUMBER" \ + -b "Release candidate [v$RELEASE_VERSION](https://github.com/supabase/ssr/releases/tag/$RELEASE_NAME) published." + else + if [ "$GITHUB_REF" == "refs/heads/main" ] || [ "$GITHUB_REF" == "refs/heads/master" ] + then + IS_PATCH_ZERO=$(node -e "console.log('$RELEASE_VERSION'.endsWith('.0'))") + + if [ "$IS_PATCH_ZERO" == "true" ] + then + # Only create release branch if patch version is 0, as this + # means that the release can be patched in the future. + + GH_TOKEN='${{ github.token }}' gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/supabase/ssr/git/refs \ + -f "ref=refs/heads/release/${RELEASE_VERSION}" \ + -f "sha=$GITHUB_SHA" + fi + fi + fi diff --git a/package.json b/package.json index a1abafc..36496f8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@supabase/ssr", - "version": "0.3.0", + "version": "0.0.0", "description": "Use the Supabase JavaScript library in popular server-side rendering (SSR) frameworks.", "main": "dist/main/index.js", "module": "dist/module/index.js", diff --git a/src/version.ts b/src/version.ts index 387b59e..2e47a88 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = "0.3.0"; +export const VERSION = "0.0.0";