diff --git a/.github/workflows/pr-validate-changesets.yaml b/.github/workflows/pr-validate-changesets.yaml index 89097f56329..eee82de86fb 100644 --- a/.github/workflows/pr-validate-changesets.yaml +++ b/.github/workflows/pr-validate-changesets.yaml @@ -121,22 +121,44 @@ jobs: env: CHANGESET_FILE: ${{ env.CHANGESET_FILE }} - - name: Validate that there are only patch changes + - name: Validate release/* branch changesets if: startsWith(github.base_ref, 'release/') run: | + LATEST_RELEASE=$(pnpm run --silent changeset:get-latest-release) + pnpm changeset version + RELEASE_VERSION=v$(sed -nE 's/^\s*"version": "(.*?)",$/\1/p' packages/fuels/package.json) + git reset --hard + + pnpm add --global semver + RELEASE_VERSION_HIGHER_THAN_LATEST=$(semver $LATEST_RELEASE $RELEASE_VERSION | tail -n1 | grep ${RELEASE_VERSION#v} --silent && echo true || echo false) + CHANGES=$(sed -n '/---/,/---/p' .changeset/*.md) - echo $CHANGES | grep -E 'patch' --silent && echo "Patch changes found." || (echo "No patch changes found." && exit 1) - echo $CHANGES | grep -E 'minor|major' --silent && echo "Old releases can only be patched; no minor and major versions allowed." && exit 1 || echo "No minor nor major changes." - - name: Validate that there was no release for the next patch version - if: startsWith(github.base_ref, 'release/') + if [ "$RELEASE_VERSION_HIGHER_THAN_LATEST" = "true" ]; then + echo $CHANGES | grep -E 'patch|minor' --silent && echo "Patch or minor changes found." || (echo "No patch nor minor changes found." && exit 1) + echo $CHANGES | grep -E 'major' --silent && echo "The latest release can have a patch or minor version update; no major version updates allowed." && exit 1 || echo "No major changes." + else + echo $CHANGES | grep -E 'patch' --silent && echo "Patch changes found." || (echo "No patch changes found." && exit 1) + echo $CHANGES | grep -E 'minor|major' --silent && echo "Old releases can only be patched; no minor and major version updates allowed." && exit 1 || echo "No minor nor major changes." + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Validate that there was no release for the next version run: | + OLD_VERSION=$(sed -nE 's/^\s*"version": "(.*?)",$/\1/p' packages/fuels/package.json) pnpm changeset version - VERSION=$(sed -nE 's/^\s*"version": "(.*?)",$/\1/p' packages/fuels/package.json) + NEW_VERSION=$(sed -nE 's/^\s*"version": "(.*?)",$/\1/p' packages/fuels/package.json) git reset --hard - STATUS_CODE=$(curl -s -w '%{http_code}\n' "https://www.npmjs.com/package/fuels/v/$VERSION" | tail -n1) + + if [ "$OLD_VERSION" = "$NEW_VERSION" ]; then + # the versions didn't change so we won't be releasing it anyways + exit 0 + fi + + STATUS_CODE=$(curl -s -w '%{http_code}\n' "https://www.npmjs.com/package/fuels/v/$NEW_VERSION" | tail -n1) if [[ $STATUS_CODE != 404 ]]; then - echo "Release for version $VERSION already exists or curl received an unexpected result (result is $STATUS_CODE). Exiting." + echo "Release for version $NEW_VERSION already exists or curl received an unexpected result (result is $STATUS_CODE). Exiting." exit 1 else exit 0 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d8a91401583..929b63fb9d8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -59,7 +59,11 @@ jobs: - name: Get latest release run: | - echo "LATEST_RELEASE=$(pnpm run --silent changeset:get-latest-release)" >> $GITHUB_ENV + LATEST_RELEASE=$(pnpm run --silent changeset:get-latest-release) + echo "LATEST_RELEASE=$LATEST_RELEASE" >> $GITHUB_ENV + + pnpm add --global semver + echo "RELEASE_VERSION_HIGHER_THAN_LATEST=$(semver $LATEST_RELEASE $RELEASE_VERSION | tail -n1 | grep ${RELEASE_VERSION#v} --silent && echo true || echo false)" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -86,12 +90,7 @@ jobs: PUBLISHED: ${{ steps.changesets.outputs.published }} REF_NAME: ${{ github.ref_name }} LATEST_RELEASE: ${{ env.LATEST_RELEASE }} - - - name: Delete the release branch - if: steps.changesets.outputs.published == 'true' && startsWith(github.ref_name, 'release/') - run: git push origin --delete ${{ github.ref_name }} - env: - GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }} + RELEASE_VERSION_HIGHER_THAN_LATEST: ${{ env.RELEASE_VERSION_HIGHER_THAN_LATEST }} - name: Release to @next tag on npm if: github.ref_name == 'master' && steps.changesets.outputs.published != 'true' @@ -123,7 +122,7 @@ jobs: run: echo LAST_COMMIT_MSG=$(git --no-pager log -1 --pretty=%B) >> $GITHUB_ENV - name: Decides if Docs should be deployed - if: github.ref_name == 'master' && startsWith(env.LAST_COMMIT_MSG, 'ci(changesets):') + if: startsWith(env.LAST_COMMIT_MSG, 'ci(changesets):') && env.RELEASE_VERSION_HIGHER_THAN_LATEST == 'true' run: echo SHOULD_DEPLOY_DOCS=true >> $GITHUB_ENV - name: Configure GitHub Pages @@ -153,7 +152,7 @@ jobs: run: | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git config user.name "github-actions[bot]" - git merge origin/master --no-edit + git merge origin/$GITHUB_REF_NAME --no-edit pnpm install pnpm build rm -f apps/docs/.gitignore @@ -164,7 +163,7 @@ jobs: git restore apps/docs/.gitignore - name: Update docs (nightly) - if: env.SHOULD_DEPLOY_DOCS == 'true' + if: github.ref_name == 'master' && env.SHOULD_DEPLOY_DOCS == 'true' uses: benc-uk/workflow-dispatch@v1 with: workflow: update-nightly.yml @@ -172,6 +171,32 @@ jobs: repo: FuelLabs/docs-hub token: ${{ secrets.REPO_TOKEN }} + - name: Create PR to apply latest release to master + if: steps.changesets.outputs.published == 'true' && startsWith(github.ref_name, 'release/') && env.RELEASE_VERSION_HIGHER_THAN_LATEST == 'true' + run: | + PR_TITLE_TEXT='apply `latest` release to `master`' + if [ ${RELEASE_VERSION#v} = "$(semver "$LATEST_VERSION" --increment minor)" ]; then + PR_TITLE="build!: $PR_TITLE_TEXT" + else + PR_TITLE="build: $PR_TITLE_TEXT" + fi + + PR_BODY='Automatically created when `latest` published release is newer than `master` due to publishing done via `release/*` branches.' + + gh pr create -B master -H $GITHUB_REF_NAME --title "$PR_TITLE" --body "$PR_BODY" + env: + GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }} + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + LATEST_VERSION: ${{ env.LATEST_VERSION }} + + - name: Delete the release branch + # We check env.RELEASE_VERSION_HIGHER_THAN_LATEST == 'false' + # because we don't want to delete the branch that is used in the "Create PR to apply latest release to master" step above + if: steps.changesets.outputs.published == 'true' && startsWith(github.ref_name, 'release/') && env.RELEASE_VERSION_HIGHER_THAN_LATEST == 'false' + run: git push origin --delete ${{ github.ref_name }} + env: + GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }} + # Upload assets to S3 - uses: unfor19/install-aws-cli-action@v1.0.3 if: github.ref_name == 'master' && steps.changesets.outputs.published != 'true' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55a5cc2c3ac..fce67e35971 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -244,7 +244,22 @@ The following example is for releasing a patch for `v0.69.0` -> `v0.69.1`. - Create PRs with base set to that release branch - When the PR is merged, a changeset PR is created - When the changeset PR is merged into the release branch, the next patch version is released and the commit is tagged (e.g. `v0.69.1`) -- After release, delete the release branch from GitHub +- After release, the release branch will be automatically deleted + +# Patching latest release + +Imagine the scenario: + +1. We release `v0.80.0` +1. One day later, we have a new changesets PR that will bump things to `v0.81.0` +1. Before releasing `v0.81.0`, we find an issue and need to make a `v0.80.1` patch + +We'd follow the same approach as explained in the [Patching old releases](#patching-old-releases) section above, bearing in mind the following after the release: + +- A PR merging the `latest` release's branch into `master` will be automatically created, +- The automatically-created PR **must** be merged as soon as possible in order to + - have the versions of packages on `master` match the `latest` released package versions, + - have the released functionality on `master` as well # FAQ diff --git a/scripts/changeset/update-changelog.mts b/scripts/changeset/update-changelog.mts index 7ba4c74ece6..dcedcf69649 100644 --- a/scripts/changeset/update-changelog.mts +++ b/scripts/changeset/update-changelog.mts @@ -12,6 +12,7 @@ const { RELEASE_TAG, REF_NAME, LATEST_RELEASE, + RELEASE_VERSION_HIGHER_THAN_LATEST, } = process.env; function sleep(time: number) { @@ -156,7 +157,7 @@ await (async () => { return; } - if (REF_NAME !== "master") { + if (RELEASE_VERSION_HIGHER_THAN_LATEST !== "true") { await reapplyLatestTagToActualLatestRelease(); }