From ac217c120f6e1a3f896c2399588002622987fb25 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Thu, 22 Feb 2024 20:40:39 +0100 Subject: [PATCH] ci: Automate patch releases and pre-releases This work by automatically pushing a new tag on main and release branches after each commit. The tag will be "devel" on main and the version from postgrest.cabal for release branches. The release workflow then runs as a tag pipeline, making the actual release. For release branches, the tag will only be created if a tag for this version doesn't exist, yet. This means to actually make a new patch release, we still need to bump the version in postgrest.cabal. We can automate this later as part of our backport-bot. This is a back-port of the following commits: - dd8d51ab - fe0f2f70 - 58d81334 - 9fe90bf9 - c67f1c39 - 8433f981 - d9ba9a82 - a57d12b1 - b006016d --- .../actions/artifact-from-cirrus/action.yaml | 11 +- .github/scripts/arm/docker-publish.sh | 15 +- .github/workflows/ci.yaml | 143 ++++++++++-------- 3 files changed, 98 insertions(+), 71 deletions(-) diff --git a/.github/actions/artifact-from-cirrus/action.yaml b/.github/actions/artifact-from-cirrus/action.yaml index 2834b4a05..8d4d5265d 100644 --- a/.github/actions/artifact-from-cirrus/action.yaml +++ b/.github/actions/artifact-from-cirrus/action.yaml @@ -99,8 +99,15 @@ runs: } archive="$(mktemp)" artifacts="$(mktemp -d)" - curl --no-progress-meter --fail -o "${archive}" \ - "https://api.cirrus-ci.com/v1/artifact/task/$(get_external_id)/${{ inputs.download }}.zip" + until curl --no-progress-meter --fail -o "${archive}" \ + "https://api.cirrus-ci.com/v1/artifact/task/$(get_external_id)/${{ inputs.download }}.zip" + do + # This happens when a tag is pushed on the same commit. In this case the + # job is immediately marked as "completed" for us, so we end up here after a few + # seconds - but the actual Cirrus CI task is still running and didn't produce its artifact, yet. + echo "Artifact not found on Cirrus CI, yet. Waiting..." + sleep 30 + done unzip "${archive}" -d "${artifacts}" echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT" - name: Save artifact to GitHub Actions diff --git a/.github/scripts/arm/docker-publish.sh b/.github/scripts/arm/docker-publish.sh index d6ebeaf30..9d0bf8df7 100644 --- a/.github/scripts/arm/docker-publish.sh +++ b/.github/scripts/arm/docker-publish.sh @@ -14,8 +14,13 @@ DOCKER_REPO="$2" DOCKER_USER="$3" DOCKER_PASS="$4" SCRIPT_DIR="$5" -PGRST_VERSION="v$6" -IS_PRERELEASE="$7" +PGRST_VERSION="$6" + +if [ "$PGRST_VERSION" == "devel" ]; then + PGRST_TAG="$PGRST_VERSION" +else + PGRST_TAG="v$PGRST_VERSION" +fi DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env" @@ -38,13 +43,13 @@ cd ~/$DOCKER_BUILD_DIR # be added to the manifest if they are not in the registry beforehand. # This image must be manually deleted from Docker Hub at the end of the process. sudo docker buildx build --build-arg PGRST_GITHUB_COMMIT=$PGRST_GITHUB_COMMIT \ - -t $DOCKER_REPO/postgrest:$PGRST_VERSION-arm \ + -t $DOCKER_REPO/postgrest:$PGRST_TAG-arm \ --push . # Add the arm images to the manifest # NOTE: This assumes that there already is a `postgrest:` image # for the amd64 architecture pushed to Docker Hub -sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:$PGRST_VERSION $DOCKER_REPO/postgrest:$PGRST_VERSION-arm -[ -z $IS_PRERELEASE ] && sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:latest $DOCKER_REPO/postgrest:$PGRST_VERSION-arm +sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:$PGRST_TAG $DOCKER_REPO/postgrest:$PGRST_TAG-arm +[ "$PGRST_VERSION" != "devel" ] && sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:latest $DOCKER_REPO/postgrest:$PGRST_TAG-arm sudo docker logout diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e4eb0c53c..66e42dba4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,6 +6,7 @@ on: - main - v[0-9]+ tags: + - devel - v* pull_request: branches: @@ -310,9 +311,11 @@ jobs: if-no-files-found: error - Prepare-Release: - name: Prepare release - if: startsWith(github.ref, 'refs/tags/v') + Tag-Release: + name: Tag Release + if: startsWith(github.ref, 'refs/heads/') + permissions: + contents: write runs-on: ubuntu-22.04 needs: - Lint-Style @@ -321,39 +324,54 @@ jobs: - Test-Memory-Nix - Build-Static-Nix - Build-Stack - #- Get-FreeBSD-CirrusCI + - Get-FreeBSD-CirrusCI - Build-Cabal-Arm - outputs: - version: ${{ steps.Identify-Version.outputs.version }} - isprerelease: ${{ steps.Identify-Version.outputs.isprerelease }} steps: - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - - id: Identify-Version - name: Identify the version to be released + with: + fetch-tags: true + ssh-key: ${{ secrets.POSTGREST_SSH_KEY }} + - name: Tag latest commit run: | - tag_version="${GITHUB_REF##*/}" cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)" - if [ "$tag_version" != "v$cabal_version" ]; then - echo "Tagged version ($tag_version) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..." - exit 1 + if [[ "$cabal_version" == *.*.* ]]; then + if [ -z "$(git tag --list "v$cabal_version")" ]; then + git tag "v$cabal_version" + git push origin "v$cabal_version" + fi else - echo "Version to be released is $cabal_version" - echo "version=$cabal_version" >> "$GITHUB_OUTPUT" + git tag -f "devel" + git push -f origin "devel" fi - if [[ "$cabal_version" != *.*.*.* ]]; then - echo "Version is for a full release (version does not have four components)" - else - echo "Version is for a pre-release (version has four components, e.g., 1.1.1.1)" - echo "isprerelease=1" >> "$GITHUB_OUTPUT" + + Prepare-Release: + name: Prepare release + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-22.04 + needs: + - Lint-Style + - Test-Nix + - Test-Pg-Nix + - Test-Memory-Nix + - Build-Static-Nix + - Build-Stack + - Get-FreeBSD-CirrusCI + - Build-Cabal-Arm + steps: + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - name: Check the version to be released + run: | + cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)" + + if [ "${GITHUB_REF_NAME}" != "devel" ] && [ "${GITHUB_REF_NAME}" != "v$cabal_version" ]; then + echo "Tagged version ($GITHUB_REF_NAME) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..." + exit 1 fi - name: Identify changes from CHANGELOG.md run: | - version="${{ steps.Identify-Version.outputs.version }}" - isprerelease="${{ steps.Identify-Version.outputs.isprerelease }}" - - if [ -n "$isprerelease" ]; then + if [ "${GITHUB_REF_NAME}" == "devel" ]; then echo "Getting unreleased changes..." sed -n "1,/## Unreleased/d;/## \[/q;p" CHANGELOG.md > CHANGES.md else @@ -377,8 +395,6 @@ jobs: contents: write runs-on: ubuntu-22.04 needs: Prepare-Release - env: - VERSION: ${{ needs.Prepare-Release.outputs.version }} steps: - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Download all artifacts @@ -391,24 +407,19 @@ jobs: mkdir -p release-bundle - tar cJvf "release-bundle/postgrest-v$VERSION-linux-static-x64.tar.xz" \ + tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-linux-static-x64.tar.xz" \ -C artifacts/postgrest-linux-static-x64 postgrest - # No need to release Ubuntu, as the static Linux binary built with Nix - # covers all Linux use-cases - #tar cfJv "release-bundle/postgrest-v$VERSION-ubuntu-x64.tar.xz" \ - # -C artifacts/postgrest-ubuntu-x64 postgrest - - tar cJvf "release-bundle/postgrest-v$VERSION-macos-x64.tar.xz" \ + tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-macos-x64.tar.xz" \ -C artifacts/postgrest-macos-x64 postgrest - tar cJvf "release-bundle/postgrest-v$VERSION-freebsd-x64.tar.xz" \ + tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-freebsd-x64.tar.xz" \ -C artifacts/postgrest-freebsd-x64 postgrest - tar cJvf "release-bundle/postgrest-v$VERSION-ubuntu-aarch64.tar.xz" \ + tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-ubuntu-aarch64.tar.xz" \ -C artifacts/postgrest-ubuntu-aarch64 postgrest - zip "release-bundle/postgrest-v$VERSION-windows-x64.zip" \ + zip "release-bundle/postgrest-${GITHUB_REF_NAME}-windows-x64.zip" \ artifacts/postgrest-windows-x64/postgrest.exe - name: Save release bundle @@ -422,14 +433,28 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - isprerelease="${{ needs.Prepare-Release.outputs.isprerelease }}" - echo "Releasing version v$VERSION on GitHub (isprerelease=$isprerelease)..." + echo "Releasing version ${GITHUB_REF_NAME} on GitHub..." - gh release delete "v$VERSION" || true - gh release create "v$VERSION" \ - -F artifacts/release-changes/CHANGES.md \ - ${isprerelease:+"--prerelease"} \ - release-bundle/* + if [ "${GITHUB_REF_NAME}" == "devel" ]; then + # To replace the existing release, we must first delete the old assets, + # then modify the release, then add the new assets. + gh release view devel --json assets \ + | jq -r '.assets[] | .name' \ + | xargs -rn1 \ + gh release delete-asset -y devel + gh release edit devel \ + -t devel \ + --verify-tag \ + -F artifacts/release-changes/CHANGES.md \ + --prerelease + gh release upload --clobber devel release-bundle/* + else + gh release create "${GITHUB_REF_NAME}" \ + -t "${GITHUB_REF_NAME}" \ + --verify-tag \ + -F artifacts/release-changes/CHANGES.md \ + release-bundle/* + fi Release-Docker: @@ -438,18 +463,11 @@ jobs: needs: - Prepare-Release env: - GITHUB_COMMIT: ${{ github.sha }} - DOCKER_REPO: postgrest - DOCKER_USER: stevechavez + DOCKER_REPO: ${{ vars.DOCKER_REPO }} + DOCKER_USER: ${{ vars.DOCKER_USER }} DOCKER_PASS: ${{ secrets.DOCKER_PASS }} - VERSION: ${{ needs.Prepare-Release.outputs.version }} - ISPRERELEASE: ${{ needs.Prepare-Release.outputs.isprerelease }} steps: - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - - name: Setup Nix Environment - uses: ./.github/actions/setup-nix - with: - tools: release - name: Download Docker image uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: @@ -459,16 +477,16 @@ jobs: docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" docker load -i postgrest-docker.tar.gz - docker tag postgrest:latest "$DOCKER_REPO/postgrest:v$VERSION" - docker push "$DOCKER_REPO/postgrest:v$VERSION" + docker tag postgrest:latest "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}" + docker push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}" # Only tag 'latest' for full releases - if [[ -z "$ISPRERELEASE" ]]; then - echo "Pushing to 'latest' tag for full release of v$VERSION ..." + if [ "${GITHUB_REF_NAME}" != "devel" ]; then + echo "Pushing to 'latest' tag for full release of ${GITHUB_REF_NAME} ..." docker tag postgrest:latest "$DOCKER_REPO"/postgrest:latest docker push "$DOCKER_REPO"/postgrest:latest else - echo "Skipping pushing to 'latest' tag for v$VERSION pre-release..." + echo "Skipping push to 'latest' tag for pre-release..." fi # TODO: Enable dockerhub description update again, once a solution for the permission problem is found: # https://github.com/docker/hub-feedback/issues/1927 @@ -488,15 +506,12 @@ jobs: runs-on: ubuntu-22.04 needs: - Build-Cabal-Arm - - Prepare-Release - Release-Docker env: GITHUB_COMMIT: ${{ github.sha }} - DOCKER_REPO: postgrest - DOCKER_USER: stevechavez + DOCKER_REPO: ${{ vars.DOCKER_REPO }} + DOCKER_USER: ${{ vars.DOCKER_USER }} DOCKER_PASS: ${{ secrets.DOCKER_PASS }} - VERSION: ${{ needs.Prepare-Release.outputs.version }} - ISPRERELEASE: ${{ needs.Prepare-Release.outputs.isprerelease }} steps: - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Publish images for ARM builds on Docker Hub @@ -509,8 +524,8 @@ jobs: key: ${{ secrets.SSH_ARM_PRIVATE_KEY }} fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }} script_stop: true - envs: GITHUB_COMMIT,DOCKER_REPO,DOCKER_USER,DOCKER_PASS,REMOTE_DIR,VERSION,ISPRERELEASE - script: bash ~/$REMOTE_DIR/docker-publish.sh "$GITHUB_COMMIT" "$DOCKER_REPO" "$DOCKER_USER" "$DOCKER_PASS" "$REMOTE_DIR" "$VERSION" "$ISPRERELEASE" + envs: GITHUB_COMMIT,DOCKER_REPO,DOCKER_USER,DOCKER_PASS,REMOTE_DIR,GITHUB_REF_NAME + script: bash ~/$REMOTE_DIR/docker-publish.sh "$GITHUB_COMMIT" "$DOCKER_REPO" "$DOCKER_USER" "$DOCKER_PASS" "$REMOTE_DIR" "$GITHUB_REF_NAME" Clean-Arm-Server: name: Remove copied files from server