diff --git a/.github/actions/update-version/action.yml b/.github/actions/update-version/action.yml new file mode 100644 index 00000000000..85cc7eded4f --- /dev/null +++ b/.github/actions/update-version/action.yml @@ -0,0 +1,32 @@ +name: Update Version in setup.py +description: "Updates release flag and version string in setup.py." + +inputs: + version: + description: 'The version string to set.' + required: true + release: + description: 'The boolean state for the release flag (true/false).' + required: true + +runs: + using: "composite" + steps: + - name: Update setup.py + shell: bash + run: | + # Convert release input to Python boolean (True/False) + RELEASE_BOOL=$(echo "${{ inputs.release }}" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}') + python3 -c " + import sys, re + version = sys.argv[1] + release_flag = sys.argv[2] + with open('setup.py', 'r') as f: + content = f.read() + content, release_count = re.subn(r'^release = .*', f'release = {release_flag}', content, flags=re.MULTILINE) + content, version_count = re.subn(r'^version = .*', f\"version = '{version}'\", content, flags=re.MULTILINE) + if release_count != 1 or version_count != 1: + raise SystemExit(f\"Expected exactly 1 replacement for each of 'release' and 'version' lines, got release={release_count}, version={version_count}\") + with open('setup.py', 'w') as f: + f.write(content) + " "${{ inputs.version }}" "$RELEASE_BOOL" \ No newline at end of file diff --git a/.github/workflows/build_venv.yml b/.github/workflows/build_venv.yml index 6336f84f52e..c009ab0ad3d 100644 --- a/.github/workflows/build_venv.yml +++ b/.github/workflows/build_venv.yml @@ -12,6 +12,15 @@ jobs: steps: - uses: actions/checkout@v1 + - name: Set release version environment variable + if: startsWith(github.ref, 'refs/tags') + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Update version file for release + if: startsWith(github.ref, 'refs/tags') + uses: ./.github/actions/update-version + with: + version: ${{ env.RELEASE_VERSION }} + release: 'true' - env: OSG_ACCESS: "${{secrets.OSG_ACCESS}}" diff --git a/.github/workflows/distribution.yml b/.github/workflows/distribution.yml index eacb2ccb071..0d29624074a 100644 --- a/.github/workflows/distribution.yml +++ b/.github/workflows/distribution.yml @@ -21,6 +21,15 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.11' + - name: Set release version environment variable + if: startsWith(github.ref, 'refs/tags') + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Update version file for release + if: startsWith(github.ref, 'refs/tags') + uses: ./.github/actions/update-version + with: + version: ${{ env.RELEASE_VERSION }} + release: 'true' - name: Install cibuildwheel run: python -m pip install cibuildwheel - name: Build wheels @@ -51,6 +60,15 @@ jobs: pattern: wheel-* merge-multiple: true path: dist/ + - name: Set release version environment variable + if: startsWith(github.ref, 'refs/tags') + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Update version file for release + if: startsWith(github.ref, 'refs/tags') + uses: ./.github/actions/update-version + with: + version: ${{ env.RELEASE_VERSION }} + release: 'true' - name: Build source distribution run: | python -c 'import setuptools ; print("setuptools version", setuptools.__version__)' @@ -62,3 +80,104 @@ jobs: uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 with: password: ${{ secrets.pypi_password }} + + bump_version: + name: Bump version to development + needs: deploy_pypi + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + fetch-depth: 0 + + - name: Check if the tag is on the default branch + id: check_branch + run: | + if git merge-base --is-ancestor ${{ github.sha }} HEAD; then + echo "is_default=true" >> $GITHUB_OUTPUT + else + echo "Tag ${{ github.ref }} is not on ${{ github.event.repository.default_branch }}. Skipping bump." + echo "is_default=false" >> $GITHUB_OUTPUT + fi + + - name: Calculate the next version + if: steps.check_branch.outputs.is_default == 'true' + id: versioning + run: | + # Extract version from tag (e.g. v1.2.3 -> 1.2.3) + VERSION=${GITHUB_REF#refs/tags/v} + # Ensure the version is in the expected MAJOR.MINOR.PATCH format with numeric components + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Tag '$GITHUB_REF' is not in the supported 'vMAJOR.MINOR.PATCH' format." >&2 + echo "Got VERSION='$VERSION'. Example of a valid tag: v1.2.3" >&2 + exit 1 + fi + IFS='.' read -r -a parts <<< "$VERSION" + MAJOR=${parts[0]} + MINOR=${parts[1]} + PATCH=${parts[2]} + # PATCH is guaranteed numeric by the regex above, but we keep this guard for extra safety + if ! [[ "$PATCH" =~ ^[0-9]+$ ]]; then + echo "Error: Patch component '$PATCH' from tag '$GITHUB_REF' is not numeric." >&2 + exit 1 + fi + NEXT_PATCH=$((PATCH + 1)) + NEXT_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}.dev0" + echo "next_version=${NEXT_VERSION}" >> $GITHUB_OUTPUT + + - name: Update version to next dev + if: steps.check_branch.outputs.is_default == 'true' + uses: ./.github/actions/update-version + with: + version: ${{ steps.versioning.outputs.next_version }} + release: 'false' + + - name: Create bump branch, commit, and push + id: create_branch + if: steps.check_branch.outputs.is_default == 'true' + run: | + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + # Use runner environment variables and shell substring to get the short SHA + BRANCH="bump-version-${GITHUB_RUN_ID}-${GITHUB_SHA::8}" + git checkout -b "${BRANCH}" + git add setup.py + if git diff --staged --quiet; then + echo "No changes to commit." + # Export an empty branch_name so downstream steps can handle it + echo "branch_name=" >> $GITHUB_OUTPUT + exit 0 + fi + git commit -m "Set back to development: ${{ steps.versioning.outputs.next_version }}" + git push --set-upstream origin "${BRANCH}" + # Export the branch name as a step output so other steps can reference it + echo "branch_name=${BRANCH}" >> $GITHUB_OUTPUT + - name: Create Pull Request (github-script) + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const branch = process.env.BUMP_BRANCH; + if (!branch) { + core.info('No branch was created (no changes). Skipping PR creation.'); + return; + } + const title = `Automated version bump to ${process.env.NEXT_VERSION}`; + const body = `This PR was created automatically by CI to set the project back to ${process.env.NEXT_VERSION}.`; + const { data: pr } = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + head: branch, + base: context.payload.repository.default_branch, + body, + }); + core.setOutput('pr_number', pr.number); + env: + NEXT_VERSION: ${{ steps.versioning.outputs.next_version }} + BUMP_BRANCH: ${{ steps.create_branch.outputs.branch_name }} diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index fa7136f9a68..b4976acc2e5 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -10,23 +10,34 @@ jobs: build: runs-on: ubuntu-24.04 steps: - - + - name: Checkout uses: actions/checkout@v1 - - - name: "Preparing a host container" - run: "docker build -t pycbc-docker-tmp ." - - - name: "Installing PyCBC and dependencies" - run: "docker run --privileged --name pycbc_inst -v `pwd`:/scratch pycbc-docker-tmp /bin/bash -c /scratch/docker/etc/docker-install.sh" - - + + - name: Set release version environment variable + if: startsWith(github.ref, 'refs/tags') + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + + - name: Update version file for release + if: startsWith(github.ref, 'refs/tags') + uses: ./.github/actions/update-version + with: + version: ${{ env.RELEASE_VERSION }} + release: 'true' + + - name: Preparing a host container + run: docker build -t pycbc-docker-tmp . + + - name: Installing PyCBC and dependencies + run: docker run --privileged --name pycbc_inst -v ${{ github.workspace }}:/scratch pycbc-docker-tmp /bin/bash -c /scratch/docker/etc/docker-install.sh + + - name: Running docker commit env: DOCKER_IMG: pycbc/pycbc-el8 - name: "Running docker commit" - run: "bash -e docker/etc/docker_commit.sh" - - + run: bash -e docker/etc/docker_commit.sh + + - name: Push docker image env: DOCKER_IMG: pycbc/pycbc-el8 DOCKER_PASSWORD: "${{secrets.DOCKERHUB_PASSWORD}}" DOCKER_USERNAME: "${{secrets.DOCKERHUB_USERNAME}}" - name: "Pushing docker image" run: "bash -e docker/etc/push_image.sh" diff --git a/docs/release.rst b/docs/release.rst index eeeb74b0a94..d520371e7d1 100644 --- a/docs/release.rst +++ b/docs/release.rst @@ -13,16 +13,10 @@ Creating the release on GitHub To create a new PyCBC release: -#. Make sure that the setup.py file contains the correct version number, which should be in the format ``x.y.z`` (where x, y, and z are the major, minor, and patch levels of the release) in PyCBC's setup.py file. -#. Set ``release = True`` in the PyCBC's setup.py file. -#. Commit the changed setup.py file and push to commit to the repository. #. Go to the `PyCBC release page `_ and click on ``Draft a new release``. #. Enter a tag version in the format ``vx.y.z``. Note the ``v`` in front of the major, minor, and patch numbers. #. Enter a descriptive release title and write a description of the release in the text box provided. #. Click on ``Publish release`` to create the release. -#. Update the setup.py file with an incremented major or minor version number and make sure that the string ``dev`` appears in that version. For example, if you just released ``1.2.1`` then change the string to ``1.3.dev0`` or ``2.0.dev0`` as appropriate. This is needed to ensure that if someone is building from source, it always takes precedence over an older release version. -#. Set ``release = False`` in PyCBC's setup.py file. -#. Commit these changes and push them to the repository. .. note:: @@ -30,7 +24,7 @@ To create a new PyCBC release: unless you are back-porting a bug fix from a new release series to an old production release series. -Creating the release will trigger a Travis build that updates CVMFS, Docker, and PyPI with the release. +Creating the release will trigger a CI build that updates CVMFS, Docker, and PyPI with the release, and increments the patch number. Please ensure that you check the outputs of these builds and urgently report any errors to other maintainers, you will be emailed if the builds fails. ------------------------------------------------ @@ -39,7 +33,7 @@ Backporting Bug Fixes to Previous Release Series Branches should only be created when bug fixes from master need to be back ported to an old release series (e.g. adding a bug fix from the 1.4 series to -the 1.3 series. +the 1.3 series. ) To create a branch for the bug fix, make a new branch from the last release tag and cherry pick the changes to that branch. For example, to create a