Skip to content

Release

Release #168

Workflow file for this run

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