Release #168
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| # 1. We look at the target branch and figure out whether this should be a BUILD | |
| # release or a patch release | |
| get-update-type: | |
| # If the context.ref is refs/heads/main we want to do a standard release | |
| # If the context.ref is refs/heads/* we want to do a patch release | |
| # If the context.ref is some other ref, we want to fail | |
| # Selecting a tag github.ref becomes e.g.: refs/tags/4.1.9.0 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: # This means this can later be referenced with needs.get-update-type.outputs.update-type | |
| update-type: ${{ steps.update-type.outputs.result }} | |
| steps: | |
| - name: Calculate update type | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| id: update-type | |
| with: | |
| script: | | |
| if (context.ref == "refs/heads/main") { | |
| return "BUILD"; | |
| } else if (context.ref.startsWith("refs/heads/")) { | |
| return "PATCH"; | |
| } else { | |
| throw new Error("Target must be a patch branch or main, but was: " + context.ref); | |
| } | |
| result-encoding: string | |
| - name: Print update type | |
| shell: bash | |
| run: echo "${{steps.update-type.outputs.result}}" | |
| # 2. In parallel, we run: | |
| # a) all static checks | |
| # b) all the single-version tests | |
| # c) the mixed-mode tests, each version in parallel | |
| style: | |
| needs: get-update-type | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Base Environment | |
| uses: ./actions/setup-base-env | |
| # Increment the version here so that the version used in the checks matches the eventually published version. | |
| - name: Increment version | |
| shell: bash | |
| run: python build/versionutils.py gradle.properties --increment -u ${{ needs.get-update-type.outputs.update-type }} | |
| - name: Run Gradle Build | |
| uses: ./actions/run-gradle | |
| with: | |
| gradle_command: build -x test -x destructiveTest -PreleaseBuild=true -PpublishBuild=false -PspotbugsEnableHtmlReport | |
| tests: | |
| needs: get-update-type | |
| strategy: | |
| matrix: | |
| subproject: [fdb-extensions, fdb-record-layer-core, fdb-record-layer-lucene, yaml-tests] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Checkout sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Base Environment | |
| uses: ./actions/setup-base-env | |
| - name: Setup FDB | |
| uses: ./actions/setup-fdb | |
| # Increment the version here so that the version used in the tests matches the eventually published version. | |
| # This is important here to avoid clashing versions when the mixed mode version equals the most recent | |
| # release, as it would otherwise conflict with the locally generated jar | |
| - name: Increment version | |
| shell: bash | |
| run: python build/versionutils.py gradle.properties --increment -u ${{ needs.get-update-type.outputs.update-type }} | |
| - name: Run Gradle Test | |
| uses: ./actions/gradle-test | |
| with: | |
| gradle_command: :${{ matrix.subproject }}:jar :${{ matrix.subproject }}:test :${{ matrix.subproject }}:destructiveTest | |
| gradle_args: -PreleaseBuild=true -PpublishBuild=false | |
| report_name: ${{ matrix.subproject }}-test-reports | |
| - name: Publish Coverage Data | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ matrix.subproject }}-coverage-data | |
| path: | | |
| **/.out/jacoco/*.exec | |
| **/.out/libs/*.jar | |
| include-hidden-files: true | |
| retention-days: 1 | |
| other-tests: | |
| needs: get-update-type | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Base Environment | |
| uses: ./actions/setup-base-env | |
| - name: Setup FDB | |
| uses: ./actions/setup-fdb | |
| # Increment the version here so that the version used in the tests matches the eventually published version. | |
| - name: Increment version | |
| shell: bash | |
| run: python build/versionutils.py gradle.properties --increment -u ${{ needs.get-update-type.outputs.update-type }} | |
| - name: Run Gradle Test | |
| uses: ./actions/gradle-test | |
| with: | |
| gradle_command: >- | |
| jar | |
| test | |
| -x :fdb-extensions:test | |
| -x :fdb-record-layer-core:test | |
| -x :fdb-record-layer-lucene:test | |
| -x :yaml-tests:test | |
| destructiveTest | |
| -x :fdb-extensions:destructiveTest | |
| -x :fdb-record-layer-core:destructiveTest | |
| -x :fdb-record-layer-lucene:destructiveTest | |
| -x :yaml-tests:destructiveTest | |
| gradle_args: -PreleaseBuild=true -PpublishBuild=false | |
| report_name: other-test-reports | |
| - name: Publish Coverage Data | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: other-coverage-data | |
| path: | | |
| **/.out/jacoco/*.exec | |
| **/.out/libs/*.jar | |
| include-hidden-files: true | |
| retention-days: 1 | |
| # 2. b) We run the mixed mode tests, each version in parallel | |
| get-mixed-mode-versions: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| versions: ${{ steps.versions.outputs.versions }} | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Base Environment | |
| uses: ./actions/setup-base-env | |
| - name: Calculate versions | |
| id: versions | |
| uses: ./actions/list-recent-versions | |
| mixed-mode: | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| needs: [get-update-type, get-mixed-mode-versions] | |
| strategy: | |
| matrix: | |
| version: ${{ fromJSON(needs.get-mixed-mode-versions.outputs.versions) }} | |
| permissions: | |
| contents: read | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Checkout sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Base Environment | |
| uses: ./actions/setup-base-env | |
| - name: Setup FDB | |
| uses: ./actions/setup-fdb | |
| # Increment the version here so that the version used in the tests matches the eventually published version. | |
| # This is important here to avoid clashing versions when the mixed mode version equals the most recent | |
| # release, as it would otherwise conflict with the locally generated jar | |
| - name: Increment version | |
| shell: bash | |
| run: python build/versionutils.py gradle.properties --increment -u ${{ needs.get-update-type.outputs.update-type }} | |
| - name: Run Gradle Test | |
| uses: ./actions/gradle-test | |
| with: | |
| gradle_command: mixedModeTest | |
| gradle_args: -PreleaseBuild=true -PpublishBuild=false -Ptests.mixedModeVersion=${{ matrix.version }} | |
| report_name: mixed-mode-${{ matrix.version }}-test-reports | |
| - name: Publish Coverage Data | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: mixed-mode-${{ matrix.version }}-coverage-data | |
| path: | | |
| **/.out/jacoco/*.exec | |
| **/.out/libs/*.jar | |
| include-hidden-files: true | |
| retention-days: 1 | |
| mixed-mode-results: | |
| needs: mixed-mode | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout HEAD sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Base Environment | |
| uses: ./actions/setup-base-env | |
| - name: 'Download results' | |
| uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 | |
| with: | |
| pattern: 'mixed-mode-*-test-reports' | |
| - name: Generate mixed mode results | |
| uses: ./actions/publish-mixed-mode-results | |
| # I think this _needs_ to be done at this level, rather than in the action | |
| # so that "mixed-mode-results" gets passed around correctly | |
| - name: Upload mixed mode results | |
| id: mixed_mode_results | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: mixed-mode-results | |
| path: mixed-mode-results.md | |
| # 3. Update the version in the repo, update the release notes, tag the commit | |
| # and publish the artifacts, and if this is a BUILD release generate the documentation | |
| publish: | |
| needs: [style, tests, other-tests, mixed-mode-results, get-update-type] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| packages: write | |
| pull-requests: write # We create a pull request if committing the release notes updates fails | |
| steps: | |
| - name: Checkout sources | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ssh-key: ${{ secrets.DEPLOY_KEY }} | |
| fetch-tags: true | |
| # fetch all the history to make sure that we have the last release | |
| # I tried fetching part of the history, but I just couldn't get it to work, and fetching all still takes like 5s | |
| fetch-depth: 0 | |
| - name: Configure git | |
| shell: bash | |
| run: | | |
| git config --global user.name 'FoundationDB CI' | |
| git config --global user.email 'foundationdb_ci@apple.com' | |
| - name: Setup Base Environment | |
| id: setup-base | |
| uses: ./actions/setup-base-env | |
| # Push a version bump back to main. There are failure scenarios that can result | |
| # in published artifacts but an erroneous build, so it's safer to bump the version | |
| # at the beginning | |
| - name: Increment version | |
| shell: bash | |
| run: python build/versionutils.py gradle.properties --increment --commit -u ${{ needs.get-update-type.outputs.update-type }} | |
| - name: Get new version | |
| id: get_new_version | |
| shell: bash | |
| run: | | |
| echo "version=$(python build/versionutils.py gradle.properties)" >> "$GITHUB_OUTPUT" | |
| # We also want to push the tag, because that will be used for the next release's release notes | |
| - name: Create tag | |
| shell: bash | |
| run: git tag -m "Release ${{ steps.get_new_version.outputs.version }}" -f "${{ steps.get_new_version.outputs.version }}" | |
| # We want to do this before anything else, because if the later steps fail, we want to make sure that the full | |
| # change log includes all changes, even if they reference a release that was never actually published. | |
| - name: Download mixed mode results | |
| uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 | |
| with: | |
| name: mixed-mode-results | |
| - name: echo results | |
| shell: bash | |
| run: cat mixed-mode-results.md || ls | |
| - name: Update release notes | |
| shell: bash | |
| run: | | |
| python ./build/create_release_notes.py \ | |
| --config ./build/release-notes-config.json \ | |
| --release-notes-md docs/sphinx/source/ReleaseNotes.md \ | |
| --skip-commit $(git log -n 1 --format=%H HEAD) \ | |
| --repository ${{ github.repository }} \ | |
| --commit \ | |
| --mixed-mode-results mixed-mode-results.md \ | |
| --version ${{ steps.get_new_version.outputs.version }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| # We move the tag to after the release notes are updated so that later steps (i.e. sphinx) will pick up the updated | |
| # release notes | |
| - name: Move tag to HEAD | |
| shell: bash | |
| run: git tag -m "Release ${{ steps.get_new_version.outputs.version }}" -f "${{ steps.get_new_version.outputs.version }}" | |
| # Push the tag now before publishing. That way, even if publishing fails or if merging the latest changes into the | |
| # base branch fails, we should at least have the tag and can recover manually | |
| - name: Push tag | |
| shell: bash | |
| run: git push origin "${{ steps.get_new_version.outputs.version }}" | |
| - name: Publish Artifacts | |
| id: publish_artifacts | |
| uses: ./actions/run-gradle | |
| with: | |
| gradle_command: publish closeAndReleaseStagingRepositories -PreleaseBuild=true -PpublishBuild=true -PgithubPublish=true -PcentralPublish=true | |
| env: | |
| ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} | |
| ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }} | |
| ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }} | |
| ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Post release: Update various files which reference version | |
| # Updating the yaml files has to be done after the tests complete, or it will mark tests as failing that aren't | |
| # supported by the previous version. | |
| - name: Update YAML test file versions | |
| uses: ./actions/run-gradle | |
| with: | |
| gradle_command: updateYamsql -PreleaseBuild=true | |
| - name: Commit YAML updates | |
| shell: bash | |
| run: python ./build/commit_yamsql_updates.py "${{ steps.get_new_version.outputs.version }}" | |
| # Push updates back to origin. | |
| # We want to do this regardless of whether publishing actually succeeds or fails, as the | |
| # publishing process itself can fail non-atomically. If that happens, we still want to update | |
| # the gradle.properties version, the release notes, etc. | |
| # | |
| # We pull updates before doing this as there may be concurrent PRs. This can introduce | |
| # non-linearities into the history, but comparing tags should still correctly | |
| # identify all commits that are present in one version but absent in another | |
| - name: Push updates | |
| id: push_updates | |
| if: steps.publish_artifacts.outcome != 'cancelled' | |
| shell: bash | |
| run: | | |
| # Remove the leading "refs/heads/" from the branch name stored in the GITHUB_REF environment variable | |
| branch_name="${GITHUB_REF##*/}" | |
| echo "Fetching and merging latest updates from branch ${branch_name} into HEAD..." | |
| git fetch origin "${branch_name}" | |
| git merge --no-edit "origin/${branch_name}" | |
| git push origin HEAD | |
| # Continue the build (including downstream steps). If the push fails, we'll create a PR | |
| continue-on-error: true | |
| - name: Create Merge PR if conflict | |
| # Only create the PR if we've otherwise been successful, but the push failed. Note that | |
| # we're checking the .outcome of the push step, which is applied before continue-on-error. | |
| if: success() && steps.push_updates.outcome == 'failure' | |
| uses: peter-evans/create-pull-request@bb88e27d3f9cc69c8bc689eba126096c6fe3dded | |
| id: pr_on_conflict | |
| with: | |
| branch: release-build | |
| branch-suffix: timestamp | |
| title: "Updates for ${{ steps.get_new_version.outputs.version }} release" | |
| sign-commits: true | |
| body: | | |
| Updates from release for version ${{ steps.get_new_version.outputs.version }}. Conflicts during the build prevented automatic updating. Please resolve conflicts by checking out the current branch, merging, and then deleting this branch. | |
| # Creating the PR can change the current branch. Explicitly check out the tag here for downstream builds | |
| - name: Revert to tag | |
| shell: bash | |
| run: git checkout "${{ steps.get_new_version.outputs.version }}" | |
| # Build documentation (We don't do any of the remaining steps for patch releases) | |
| - name: LOG update type | |
| shell: bash | |
| run: echo "${{ needs.get-update-type.outputs.update-type }}" | |
| - name: Cache Python Environment | |
| if: needs.get-update-type.outputs.update-type == 'BUILD' | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| with: | |
| path: docs/sphinx/.venv | |
| key: ${{ runner.os }}-sphinx-python-${{ steps.setup-base.outputs.python-version }}-${{ hashFiles('docs/sphinx/requirements.txt') }} | |
| - name: Build Documentation Site | |
| if: needs.get-update-type.outputs.update-type == 'BUILD' | |
| uses: ./actions/run-gradle | |
| with: | |
| gradle_command: documentationSite -PreleaseBuild=true | |
| - name: Upload Documentation | |
| if: needs.get-update-type.outputs.update-type == 'BUILD' | |
| id: doc_upload | |
| uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 | |
| with: | |
| path: docs/sphinx/.out/html/ | |
| # 4. We deploy the documentation from (3) to github pages, unless this is a patch release | |
| # deploy_docs is a separate job so that it can run with different permissions from | |
| # everything else, but it depends on publish so, it will always run last | |
| deploy_docs: | |
| runs-on: ubuntu-latest | |
| needs: [publish, get-update-type] | |
| if: needs.get-update-type.outputs.update-type == 'BUILD' | |
| permissions: | |
| pages: write | |
| id-token: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.doc_upload.outputs.page_url }} | |
| steps: | |
| - name: LOG update type | |
| shell: bash | |
| run: echo "${{ needs.get-update-type.outputs.update-type }}" | |
| - name: Deploy Documentation | |
| uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 |