From 08f06f219cad4ae96b90586b7521226217dabeb7 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 13:53:39 +1200 Subject: [PATCH 001/117] Add set_version python and shell scripts. --- .github/scripts/set_version.py | 39 ++++++++++++++++++++++++++++++++++ .github/scripts/set_version.sh | 29 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 .github/scripts/set_version.py create mode 100644 .github/scripts/set_version.sh diff --git a/.github/scripts/set_version.py b/.github/scripts/set_version.py new file mode 100644 index 000000000..1b2433b39 --- /dev/null +++ b/.github/scripts/set_version.py @@ -0,0 +1,39 @@ +import argparse +import os +import subprocess + +here = os.path.abspath(os.path.dirname(__file__)) + + +def run_set_version(location, core, developer): + current_dir = os.path.abspath(os.path.curdir) + working_dir = os.path.join(current_dir, location) + os.chdir(working_dir) + + subprocess.call(f"{os.path.join(here, 'set_version.sh')} {core} {developer}", shell=True) + + os.chdir(current_dir) + + +def _process_arguments(): + parser = argparse.ArgumentParser(description="Update all version numbers in the libCellML codebase.", + prefix_chars='/') + parser.add_argument("directory", + help="Relative directory from current working directory" + " to directory of libCellML source code.") + parser.add_argument("core", + help="Core version number in the form X.Y.Z where X, Y, and Z are whole numbers.") + parser.add_argument("developer", + nargs="?", default="", + help="Developer version number in the form -{dev|rc}.W where W is a whole number.") + + return parser.parse_args() + + +def main(): + args = _process_arguments() + run_set_version(args.directory, args.core, args.developer) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh new file mode 100644 index 000000000..a54315942 --- /dev/null +++ b/.github/scripts/set_version.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +version=$1 +developer_version=$2 + +IFS='.' read -ra version_array <<< "$version" +numeric_version=`printf %02d "${version_array[@]}"` +sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +sed -i '' 's@ EXPECT_EQ(0x[[:digit:]]*U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp + +sed -i '' 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt +sed -i '' 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt + +files=`grep -rl "^LIBCELLML_VERSION = \"\d*.\d*.\d*\"" *` +sed -i '' 's@LIBCELLML_VERSION = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\"@LIBCELLML_VERSION = \"'${version}'\"@' $files + +files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` +sed -i '' -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files + +files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` +sed -i '' -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files + +files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` +sed -i '' 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files + +files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` +sed -i '' "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files + +exit 0 From 56842db06e6c12a69d82736ba390c3dd64f50127 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 14:57:07 +1200 Subject: [PATCH 002/117] Add initial prepare release GitHub action script. --- .github/workflows/release-prepare.yml | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/release-prepare.yml diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml new file mode 100644 index 000000000..2622b30bf --- /dev/null +++ b/.github/workflows/release-prepare.yml @@ -0,0 +1,46 @@ +name: Prepare Release + +on: + workflow_dispatch: + inputs: + source_branch: + description: "Base branch (main or tag)" + required: true + version: + description: "Release version (e.g. 0.7.1)" + required: true + +jobs: + prepare: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.source_branch }} + + - name: Create staging branch + run: | + git checkout -b release-staging-v${{ inputs.version }} + + - name: Update version numbers + run: | + python3 ./scripts/set_version.py ${{ inputs.version }} + + - name: Commit version bump + run: | + git config user.name "github-actions" + git config user.email "actions@github.com" + git commit -am "Prepare release v${{ inputs.version }}" + + - name: Push branch + run: git push origin HEAD + + - name: Open PR + uses: peter-evans/create-pull-request@v6 + with: + base: release + head: release-staging-v${{ inputs.version }} + title: "Release v${{ inputs.version }}" + body: | + Automated release PR for v${{ inputs.version }} From 817a4adf9782d264a9dfe0ef08d64da085547c14 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:16:10 +1200 Subject: [PATCH 003/117] Update setting version numbers script parameters. --- .github/workflows/release-prepare.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 2622b30bf..297a53645 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -25,7 +25,11 @@ jobs: - name: Update version numbers run: | - python3 ./scripts/set_version.py ${{ inputs.version }} + version=${{ inputs.version }} + core=$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$version") + dev="${version#"$core"}" + echo "Core version: $core, Dev suffix: $dev" + python3 .github/scripts/set_version.py . $core $dev - name: Commit version bump run: | From f04eabadaf7fc4ca8c4ba3a52f622c68a37c0757 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:37:15 +1200 Subject: [PATCH 004/117] Use actions/checkout@v6 in release-prepare.yml. --- .github/workflows/release-prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 297a53645..3bf1efec0 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ inputs.source_branch }} From 7d1d71e127a46f46e19ed51b59dd3b71fc1a289a Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:38:10 +1200 Subject: [PATCH 005/117] Set set_version.sh as executable. --- .github/scripts/set_version.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/set_version.sh diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh old mode 100644 new mode 100755 From c00457aa2f3466fb0a5b285465714d03f319a209 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:42:14 +1200 Subject: [PATCH 006/117] Set a defulat value of 'main' for the source branch. --- .github/workflows/release-prepare.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3bf1efec0..b0911ea7f 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -5,6 +5,7 @@ on: inputs: source_branch: description: "Base branch (main or tag)" + default: 'main' required: true version: description: "Release version (e.g. 0.7.1)" From 33cb8e06c8630a8a1144856b0ea566c1bf27033c Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:46:05 +1200 Subject: [PATCH 007/117] Add debug printing to set_version.sh. --- .github/scripts/set_version.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index a54315942..1d4cfddba 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -3,6 +3,9 @@ version=$1 developer_version=$2 +echo "Setting version to ${version} and developer version to ${developer_version}." +pwd + IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp From a41d407aec0ca148d663d1d0281e2f96e60b6cb8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:49:04 +1200 Subject: [PATCH 008/117] Add more debug printing to set_version.sh. --- .github/scripts/set_version.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 1d4cfddba..2626064a7 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -6,6 +6,10 @@ developer_version=$2 echo "Setting version to ${version} and developer version to ${developer_version}." pwd +ls tests/version/version.cpp + +echo 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' + IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp From 984f6ef153f92b9d5b9ecea98a3e35ba89cf4282 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 17:03:27 +1200 Subject: [PATCH 009/117] Update set_version.sh to run on GNU bash, improve version match regexp. --- .github/scripts/set_version.sh | 35 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 2626064a7..78212e946 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -3,34 +3,27 @@ version=$1 developer_version=$2 -echo "Setting version to ${version} and developer version to ${developer_version}." -pwd - -ls tests/version/version.cpp - -echo 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' - IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` -sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp -sed -i '' 's@ EXPECT_EQ(0x[[:digit:]]*U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp +sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp -sed -i '' 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt -sed -i '' 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt +sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt +sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt -files=`grep -rl "^LIBCELLML_VERSION = \"\d*.\d*.\d*\"" *` -sed -i '' 's@LIBCELLML_VERSION = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\"@LIBCELLML_VERSION = \"'${version}'\"@' $files +files=`grep -rl "^LIBCELLML_VERSION = \"\d+.\d+.\d+\"" *` +sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files -files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` -sed -i '' -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files +files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files -files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` -sed -i '' -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files +files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d+\.\d+\.\d+\." *` +sed -i -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files -files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` -sed -i '' 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files +files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d+\.\d+\.\d+\";" *` +sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files -files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` -sed -i '' "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files +files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d+\.\d+\.\d+');" *` +sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files exit 0 From 029b20bc85b35a32afb54023dd2680dc6ae4e7d4 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 17:09:35 +1200 Subject: [PATCH 010/117] Update peter-evans/create-pull-request in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index b0911ea7f..5e0e6fdd7 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -42,10 +42,10 @@ jobs: run: git push origin HEAD - name: Open PR - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v8 with: base: release - head: release-staging-v${{ inputs.version }} + branch: release-staging-v${{ inputs.version }} title: "Release v${{ inputs.version }}" body: | Automated release PR for v${{ inputs.version }} From 49045182c6fb7cbdc24a4520bc897bc67a8c1b48 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 20:34:07 +1200 Subject: [PATCH 011/117] Add debug echo statements to release-prepare.yml. --- .github/scripts/set_version.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 78212e946..ccf3b3cf4 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -5,6 +5,7 @@ developer_version=$2 IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` + sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp @@ -12,9 +13,11 @@ sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMak sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt files=`grep -rl "^LIBCELLML_VERSION = \"\d+.\d+.\d+\"" *` +echo "files 0: $files" sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files -files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +echo "files 1: $files" sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d+\.\d+\.\d+\." *` @@ -26,4 +29,6 @@ sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:di files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d+\.\d+\.\d+');" *` sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files +git status + exit 0 From 013cd6a4900e41e9b4254e19e71a9a7e488bd906 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 21:53:52 +1200 Subject: [PATCH 012/117] Change find regexp in release-prepare.yml. --- .github/scripts/set_version.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index ccf3b3cf4..c47d3c72f 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -12,21 +12,21 @@ sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_ve sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt -files=`grep -rl "^LIBCELLML_VERSION = \"\d+.\d+.\d+\"" *` +files=`grep -rl "^LIBCELLML_VERSION = \"\d*\.\d*\.\d*\"" *` echo "files 0: $files" sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files -files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` echo "files 1: $files" sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files -files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d+\.\d+\.\d+\." *` +files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` sed -i -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files -files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d+\.\d+\.\d+\";" *` +files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files -files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d+\.\d+\.\d+');" *` +files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files git status From e1decd666881e66068978a249fc2325dda008188 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 22:27:46 +1200 Subject: [PATCH 013/117] Add debug messages in release-prepare.yml. --- .github/scripts/set_version.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index c47d3c72f..e97717120 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -7,7 +7,11 @@ IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +echo "1" +git status sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp +echo "2" +git status sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt From eaed2c946d3b79f0fc6bbd60d48c8557a005b5ba Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:02:04 +1200 Subject: [PATCH 014/117] Ignore Act resource files. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index df4e47f0e..57613db32 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ CMakeLists.txt.user # VS Code system files .vscode/ + +# Act resource files +.actrc +act-payload.json From c68bef2fcc21761e7c69dd30107434fa1ceb4af3 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:02:52 +1200 Subject: [PATCH 015/117] Debug 2. --- .github/scripts/set_version.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index e97717120..a99cbf12b 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -6,8 +6,9 @@ developer_version=$2 IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` -sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +sed -i 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp echo "1" +ls git status sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp echo "2" @@ -15,6 +16,7 @@ git status sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt +git status files=`grep -rl "^LIBCELLML_VERSION = \"\d*\.\d*\.\d*\"" *` echo "files 0: $files" @@ -34,5 +36,7 @@ files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()). sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files git status - +echo "===================" +git diff +echo "===================" exit 0 From b3b5ac5c432059bd05ac4bc07695f3385524c3f3 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:08:59 +1200 Subject: [PATCH 016/117] Debug 2. --- .github/scripts/set_version.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index a99cbf12b..2fab301cc 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -18,7 +18,7 @@ sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMak sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt git status -files=`grep -rl "^LIBCELLML_VERSION = \"\d*\.\d*\.\d*\"" *` +files=$(grep -rl '^LIBCELLML_VERSION = "[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+"' .) echo "files 0: $files" sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files @@ -37,6 +37,6 @@ sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]] git status echo "===================" -git diff +git diff | cat echo "===================" exit 0 From 230f22e87908fce216a50f7f7bea38f2ce304182 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:28:43 +1200 Subject: [PATCH 017/117] Debug 3. --- .github/scripts/set_version.sh | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 2fab301cc..277e28776 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -4,9 +4,10 @@ version=$1 developer_version=$2 IFS='.' read -ra version_array <<< "$version" -numeric_version=`printf %02d "${version_array[@]}"` +numeric_version=$(printf %02d "${version_array[@]}") +version_regex='[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*' -sed -i 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +sed -i "s@ EXPECT_EQ(\"${version_regex}\", versionString);@ EXPECT_EQ(\"${version}\", versionString);@" tests/version/version.cpp echo "1" ls git status @@ -18,22 +19,23 @@ sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMak sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt git status -files=$(grep -rl '^LIBCELLML_VERSION = "[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+"' .) -echo "files 0: $files" -sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files +files=$(grep -rl "^LIBCELLML_VERSION = \"${version_regex}\"" .) +echo "files 0:" +echo $files +sed -i "s@LIBCELLML_VERSION = \"${version_regex}\"@LIBCELLML_VERSION = \"${version}\"@" $files -files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` +files=$(grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML ${version_regex}\. \*/" *) echo "files 1: $files" -sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files +sed -i -E "s@/\* The content of this file was generated using (the|a modified) C profile of libCellML ${version_regex}\. \*/@/\* The content of this file was generated using \1 C profile of libCellML ${version}. \*/@" $files -files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` -sed -i -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files +files=$(grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML ${version_regex}\." *) +sed -i -E "s@# The content of this file was generated using (the|a modified) Python profile of libCellML ${version_regex}\.@# The content of this file was generated using \1 Python profile of libCellML ${version}.@" $files -files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` -sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files +files=$(grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"${version_regex}\";" *) +sed -i "s@const char LIBCELLML_VERSION\[\] = \"${version_regex}\";@const char LIBCELLML_VERSION\[\] = \"${version}\";@" $files -files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` -sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files +files=$(grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('${version_regex}');" *) +sed -i "s@ expect(libcellml\.versionString()).toBe('${version_regex}');@ expect(libcellml\.versionString()).toBe('${version}');@" $files git status echo "===================" From f523fd35bca404e4ea6723e84e07bf8cb52f0d5d Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:34:03 +1200 Subject: [PATCH 018/117] Debug 4. --- .github/scripts/set_version.sh | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 277e28776..a8637aa55 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -8,24 +8,16 @@ numeric_version=$(printf %02d "${version_array[@]}") version_regex='[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*' sed -i "s@ EXPECT_EQ(\"${version_regex}\", versionString);@ EXPECT_EQ(\"${version}\", versionString);@" tests/version/version.cpp -echo "1" -ls -git status sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp -echo "2" -git status sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt -git status + files=$(grep -rl "^LIBCELLML_VERSION = \"${version_regex}\"" .) -echo "files 0:" -echo $files sed -i "s@LIBCELLML_VERSION = \"${version_regex}\"@LIBCELLML_VERSION = \"${version}\"@" $files files=$(grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML ${version_regex}\. \*/" *) -echo "files 1: $files" sed -i -E "s@/\* The content of this file was generated using (the|a modified) C profile of libCellML ${version_regex}\. \*/@/\* The content of this file was generated using \1 C profile of libCellML ${version}. \*/@" $files files=$(grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML ${version_regex}\." *) @@ -37,8 +29,4 @@ sed -i "s@const char LIBCELLML_VERSION\[\] = \"${version_regex}\";@const char LI files=$(grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('${version_regex}');" *) sed -i "s@ expect(libcellml\.versionString()).toBe('${version_regex}');@ expect(libcellml\.versionString()).toBe('${version}');@" $files -git status -echo "===================" -git diff | cat -echo "===================" exit 0 From 6d8dc6305a3067fe2a76225dab6b4008ed396048 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:53:00 +1200 Subject: [PATCH 019/117] Debug 5. --- .github/scripts/set_version.sh | 1 - .github/workflows/release-prepare.yml | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index a8637aa55..886dedf27 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -13,7 +13,6 @@ sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_ve sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt - files=$(grep -rl "^LIBCELLML_VERSION = \"${version_regex}\"" .) sed -i "s@LIBCELLML_VERSION = \"${version_regex}\"@LIBCELLML_VERSION = \"${version}\"@" $files diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 5e0e6fdd7..3cc1cb66a 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -39,13 +39,14 @@ jobs: git commit -am "Prepare release v${{ inputs.version }}" - name: Push branch - run: git push origin HEAD + run: git push origin release-staging-v${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 with: base: release branch: release-staging-v${{ inputs.version }} - title: "Release v${{ inputs.version }}" + title: "Proposed release v${{ inputs.version }}" + draft: true body: | Automated release PR for v${{ inputs.version }} From f613c092faee54952294c0610daddf3879d136f9 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:00:03 +1200 Subject: [PATCH 020/117] Debug 6. --- .github/workflows/release-prepare.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3cc1cb66a..06c7090af 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -41,12 +41,12 @@ jobs: - name: Push branch run: git push origin release-staging-v${{ inputs.version }} - - name: Open PR - uses: peter-evans/create-pull-request@v8 - with: - base: release - branch: release-staging-v${{ inputs.version }} - title: "Proposed release v${{ inputs.version }}" - draft: true - body: | - Automated release PR for v${{ inputs.version }} +# - name: Open PR +# uses: peter-evans/create-pull-request@v8 +# with: +# base: release +# branch: release-staging-v${{ inputs.version }} +# title: "Proposed release v${{ inputs.version }}" +# draft: true +# body: | +# Automated release PR for v${{ inputs.version }} From a26dad677f4cf3f4eba7508f309c7ce2f14c1243 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:09:23 +1200 Subject: [PATCH 021/117] Re-instate creation of PR when a release process is started. --- .github/workflows/release-prepare.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 06c7090af..17927c8ce 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -34,19 +34,20 @@ jobs: - name: Commit version bump run: | - git config user.name "github-actions" - git config user.email "actions@github.com" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" git commit -am "Prepare release v${{ inputs.version }}" - name: Push branch run: git push origin release-staging-v${{ inputs.version }} -# - name: Open PR -# uses: peter-evans/create-pull-request@v8 -# with: -# base: release -# branch: release-staging-v${{ inputs.version }} -# title: "Proposed release v${{ inputs.version }}" -# draft: true -# body: | -# Automated release PR for v${{ inputs.version }} + - name: Open PR + uses: peter-evans/create-pull-request@v8 + with: + base: release + branch: release-staging-v${{ inputs.version }} + title: "Proposed release changes for v${{ inputs.version }}" + draft: true + body: | + Automated release PR for v${{ inputs.version }} + **Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised. From 5c184b27bd5bfa64cb1fb4c071d85bac90acd4c3 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:34:39 +1200 Subject: [PATCH 022/117] Add PR write permissions to release-prepare workflow. --- .github/workflows/release-prepare.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 17927c8ce..5c2b082d3 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -8,9 +8,13 @@ on: default: 'main' required: true version: - description: "Release version (e.g. 0.7.1)" + description: "Release version (e.g. 0.7.1, 0.7.0-post.1)" required: true +permissions: + contents: write + pull-requests: write + jobs: prepare: runs-on: ubuntu-latest From dd2b7f801932b6cd65113d42dbbdded327f4dcd5 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:48:30 +1200 Subject: [PATCH 023/117] Try basing changes off the release branch. --- .github/workflows/release-prepare.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 5c2b082d3..e45a71187 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -36,14 +36,24 @@ jobs: echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev + - uses: actions/checkout@v6 + with: + ref: release + path: release-repo + - name: Commit version bump run: | + rm -rf .git/ + mv release-repo/.git/ ./ + rm -rf release-repo + git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git commit -am "Prepare release v${{ inputs.version }}" - name: Push branch - run: git push origin release-staging-v${{ inputs.version }} + run: | + git push origin release:release-staging-v${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 From bd68aef3195f0106205b28437d29c0d49a34bd3b Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 17:01:09 +1200 Subject: [PATCH 024/117] Try and fix creation of PR against release branch. --- .github/workflows/release-prepare.yml | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index e45a71187..38dc17242 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -19,33 +19,38 @@ jobs: prepare: runs-on: ubuntu-latest + env: + BRANCH_PREFIX: release-staging-v + steps: + - uses: actions/checkout@v6 + with: + ref: release + path: release-repo + - uses: actions/checkout@v6 with: ref: ${{ inputs.source_branch }} + path: source-repo - name: Create staging branch run: | - git checkout -b release-staging-v${{ inputs.version }} + cd release-repo + git checkout -b $BRANCH_PREFIX${{ inputs.version }} - name: Update version numbers run: | + cd source-repo + version=${{ inputs.version }} core=$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$version") dev="${version#"$core"}" echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev - - uses: actions/checkout@v6 - with: - ref: release - path: release-repo - - name: Commit version bump run: | - rm -rf .git/ - mv release-repo/.git/ ./ - rm -rf release-repo + cd source-repo git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -53,13 +58,17 @@ jobs: - name: Push branch run: | - git push origin release:release-staging-v${{ inputs.version }} + rm -rf source-repo/.git/ + mv release-repo/.git/ source-repo/ + + cd source-repo + git push origin $BRANCH_PREFIX${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 with: base: release - branch: release-staging-v${{ inputs.version }} + branch: ${{ env.BRANCH_PREFIX }}${{ inputs.version }} title: "Proposed release changes for v${{ inputs.version }}" draft: true body: | From 222ab5a7ece2e448ba08df0ff024c245289d2aee Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 10:39:55 +1200 Subject: [PATCH 025/117] Fully qualify environment BRANCH_PREFIX in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 38dc17242..7a321f39f 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -36,7 +36,7 @@ jobs: - name: Create staging branch run: | cd release-repo - git checkout -b $BRANCH_PREFIX${{ inputs.version }} + git checkout -b ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Update version numbers run: | @@ -62,7 +62,7 @@ jobs: mv release-repo/.git/ source-repo/ cd source-repo - git push origin $BRANCH_PREFIX${{ inputs.version }} + git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 From 63ed75ff37dc5a2496e484a01e0696184bc6c273 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 10:49:55 +1200 Subject: [PATCH 026/117] Use rsync to copy changes to release staging branch. --- .github/workflows/release-prepare.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 7a321f39f..3afbe860b 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -48,9 +48,13 @@ jobs: echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev + - name: Copy changes to release repo + run: | + rsync -av --exclude='.git' source-repo/ release-repo/ + - name: Commit version bump run: | - cd source-repo + cd release-repo git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -58,10 +62,7 @@ jobs: - name: Push branch run: | - rm -rf source-repo/.git/ - mv release-repo/.git/ source-repo/ - - cd source-repo + cd release-repo git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR From d3f9e2bbaa0c47be770d829de05244483c67acc5 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 11:31:14 +1200 Subject: [PATCH 027/117] Improve commit message for release changes. --- .github/workflows/release-prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3afbe860b..4cbda0068 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -58,7 +58,7 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git commit -am "Prepare release v${{ inputs.version }}" + git commit -am "Changes to update the codebase to release v${{ inputs.version }}." - name: Push branch run: | From eaa7ad4f30ab6b71a967a6c29af28cd1541fe0f2 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 11:34:00 +1200 Subject: [PATCH 028/117] Set release repo path for peter-evans/create-pull-request. --- .github/workflows/release-prepare.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4cbda0068..3fb6dda98 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -68,6 +68,7 @@ jobs: - name: Open PR uses: peter-evans/create-pull-request@v8 with: + path: release-repo base: release branch: ${{ env.BRANCH_PREFIX }}${{ inputs.version }} title: "Proposed release changes for v${{ inputs.version }}" From fb77b562941ae8ddd161092e0f9599897e822a04 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:14:39 +1200 Subject: [PATCH 029/117] Use gh instead of peter-evans/create-pull-request in release-prepare.yml. --- .github/workflows/release-prepare.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3fb6dda98..e853e0809 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -66,13 +66,10 @@ jobs: git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR - uses: peter-evans/create-pull-request@v8 - with: - path: release-repo - base: release - branch: ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - title: "Proposed release changes for v${{ inputs.version }}" - draft: true - body: | - Automated release PR for v${{ inputs.version }} - **Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised. + run: | + gh pr create \ + --base release \ + --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ + --title "Proposed release changes for v${{ inputs.version }}" \ + --body "Automated release PR for v${{ inputs.version }}\n**Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised." \ + --draft From 36bbb34567f1d505f0e7650d90c5bbe938e8fd17 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:16:17 +1200 Subject: [PATCH 030/117] Add Github token to Create PR step in env: --- .github/workflows/release-prepare.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index e853e0809..c76070a88 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -66,6 +66,8 @@ jobs: git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR + env: + GH_TOKEN: ${{ github.token }} run: | gh pr create \ --base release \ From d56fa3d6e09259ca16ed065445b0560355510f44 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:17:54 +1200 Subject: [PATCH 031/117] Change into release repo. before creating PR. --- .github/workflows/release-prepare.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index c76070a88..fe7882548 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -69,6 +69,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | + cd release-repo gh pr create \ --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ From d8114846b4d84a715f952bc910de200befb62072 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:27:18 +1200 Subject: [PATCH 032/117] Try using here document to set PR body text in release-prepare.yml. --- .github/workflows/release-prepare.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index fe7882548..333649299 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -66,6 +66,7 @@ jobs: git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR + id: open_pr env: GH_TOKEN: ${{ github.token }} run: | @@ -74,5 +75,15 @@ jobs: --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ --title "Proposed release changes for v${{ inputs.version }}" \ - --body "Automated release PR for v${{ inputs.version }}\n**Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised." \ --draft + --body "$(cat <<'EOF' +Automated release PR for v${{ inputs.version }} + +**Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. +EOF)" + + - name: Delete remote branch if PR creation failed + if: failure() && steps.open_pr.outcome == 'failure' + run: | + cd release-repo + git push origin --delete ${{ env.BRANCH_PREFIX }}${{ inputs.version }} From bc70c8743f5fb69cfeaf44a4950057fa09969292 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:29:31 +1200 Subject: [PATCH 033/117] Try indenting the here document body differently. --- .github/workflows/release-prepare.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 333649299..a76054d76 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -77,10 +77,10 @@ jobs: --title "Proposed release changes for v${{ inputs.version }}" \ --draft --body "$(cat <<'EOF' -Automated release PR for v${{ inputs.version }} + Automated release PR for v${{ inputs.version }} -**Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. -EOF)" + **Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. + EOF)" - name: Delete remote branch if PR creation failed if: failure() && steps.open_pr.outcome == 'failure' From 174a64f29ee5ccb315e0837d4e5d7af16989a4a1 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:34:19 +1200 Subject: [PATCH 034/117] Use a text file to store body of PR text. --- .github/workflows/release-prepare.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index a76054d76..cb3600800 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -70,17 +70,18 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | + echo "Automated release PR for v${{ inputs.version }}." > pr_body.txt + echo "" >> pr_body.txt + echo "**Do not** merge this PR, it is only for review and testing." >> pr_body.txt + echo "The release process will deal with it when the release is finalised." >> pr_body.txt + cd release-repo gh pr create \ --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ --title "Proposed release changes for v${{ inputs.version }}" \ + --body-file ../pr_body.txt \ --draft - --body "$(cat <<'EOF' - Automated release PR for v${{ inputs.version }} - - **Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. - EOF)" - name: Delete remote branch if PR creation failed if: failure() && steps.open_pr.outcome == 'failure' From 7221d2b53e0595b9350e78c3ff00d5316edd7a6d Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 14:20:36 +1200 Subject: [PATCH 035/117] Improve the title of the created PR. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index cb3600800..a62451cd3 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -8,7 +8,7 @@ on: default: 'main' required: true version: - description: "Release version (e.g. 0.7.1, 0.7.0-post.1)" + description: "Release version (e.g. 0.7.1, 0.7.0-rc.1)" required: true permissions: @@ -79,7 +79,7 @@ jobs: gh pr create \ --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ - --title "Proposed release changes for v${{ inputs.version }}" \ + --title "Proposed changes to create release v${{ inputs.version }} of libCellML" \ --body-file ../pr_body.txt \ --draft From c45d16778d758c4306a8ed85d7e0a4df5efe282c Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:36:39 +1200 Subject: [PATCH 036/117] Add initial generate changelog GitHub Actions scripts/workflow. --- .github/scripts/generate_changelog.py | 190 ++++++++++++++++++++++++ .github/workflows/release-changelog.yml | 69 +++++++++ 2 files changed, 259 insertions(+) create mode 100644 .github/scripts/generate_changelog.py create mode 100644 .github/workflows/release-changelog.yml diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py new file mode 100644 index 000000000..b3dafbb3a --- /dev/null +++ b/.github/scripts/generate_changelog.py @@ -0,0 +1,190 @@ +import json +import os +import re +import subprocess +import requests +from packaging import version as semver + +GITHUB_API = "https://api.github.com" +IGNORED_CONTRIBUTORS = ['abi-git-user', 'github-actions[bot]'] + +HEADERS = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {os.environ['GH_TOKEN']}" +} + +TAG_PATTERN = re.compile(r"^source-v(\d+\.\d+\.\d+)$") +LABEL_PRIORITY = [] + + +class Contributor: + + def __init__(self, avatar_url, user_url): + self._avatar_url = avatar_url + self._user_url = user_url + + @property + def avatar_url(self): + return self._avatar_url + + @property + def user_url(self): + return self._user_url + + def __eq__(self, other): + return isinstance(other, Contributor) and \ + self.user_url == other.user_url and \ + self.avatar_url == other.avatar_url + + def __hash__(self): + return hash((self.user_url, self.avatar_url)) + + +def run(cmd): + return subprocess.check_output(cmd, text=True).strip() + + +def find_previous_source_tag(): + tags = run(["git", "tag"]).splitlines() + valid = [] + + for t in tags: + m = TAG_PATTERN.match(t) + if m: + valid.append((semver.parse(m.group(1)), t)) + + if not valid: + raise RuntimeError("No valid source-vX.Y.Z tags found") + + valid.sort(reverse=True) + return valid[0][1] + + +def get_merge_commits(start, end="HEAD"): + log = run([ + "git", "log", + f"{start}..{end}", + "--merges", + "--first-parent", + "--pretty=%s" + ]) + return log.splitlines() + + +def extract_pr_numbers(messages): + prs = [] + for msg in messages: + m = re.search(r"#(\d+)", msg) + if m: + prs.append(int(m.group(1))) + return prs + + +def fetch_pr(org_repo, pr_number): + url = f"{GITHUB_API}/repos/{org_repo}/pulls/{pr_number}" + r = requests.get(url, headers=HEADERS) + r.raise_for_status() + return r.json() + + +def choose_primary_label(labels): + names = [l["name"] for l in labels] + for p in LABEL_PRIORITY: + if p in names: + return p + return names[0] if names else "No category" + + +def extract_summary(pr): + label = choose_primary_label(pr["labels"]) + secondary = [l["name"] for l in pr["labels"] if l["name"] != primary] + return { + "title": pr["title"], + "label": label, + "secondary_labels": secondary, + "number": pr["number"], + "url": pr["html_url"], + "user": pr["user"]["login"], + "user_url": pr["user"]["html_url"], + "avatar_url": pr["user"]["avatar_url"], + } + + +def write_out_to_changelog_file(sorted_summaries, tag_end): + current_label = '' + file_name = f'changelog_{tag_end}.rst' + with open(file_name, 'w') as f: + + changelog_title = f'libCellML {tag_end} Changelog' + f.write(f'{changelog_title}\n') + f.write('=' * len(changelog_title)) + f.write('\n') + + contributors = [] + for summary in sorted_summaries: + if current_label != summary['label']: + current_label = summary['label'] + f.write(f'\n{current_label}\n') + f.write('-' * len(current_label)) + f.write('\n\n') + + title = summary['title'][:-1] if summary['title'].endswith('.') else summary['title'] + user_link = f'`@{summary["user"]} <{summary["user_url"]}>`_' + pr_link = f'`#{summary["number"]} <{summary["url"]}>`_' + suffix = "" + if summary.get("secondary_labels"): + suffix = f" (also: {', '.join(summary['secondary_labels'])})" + f.write(f'* {title}{suffix} by {user_link} [{pr_link}].\n') + contributors.append(Contributor(summary['avatar_url'], summary['user_url'])) + + contributors = list(set(contributors)) + if contributors: + section_title = 'Contributors' + f.write(f'\n{section_title}\n') + f.write('-' * len(section_title)) + f.write('\n\n') + for contributor in contributors: + f.write(f'.. image:: {contributor.avatar_url}\n :target: {contributor.user_url}' + f'\n :height: 32\n :width: 32\n') + + print(f'Changelog written to: {file_name}.') + + +def process_arguments(): + parser = argparse.ArgumentParser(description="Create a simple change log from merged pull requests from a GitHub " + "project.") + parser.add_argument("-p", "--project", + help="GitHub project to work with, default 'cellml/libcellml'.", default="cellml/libcellml") + # parser.add_argument("-r", "--local-repo", + # help="The location of the project repository. Absolute path or relative path relative to the " + # "current working directory.", default=None) + parser.add_argument("-t", "--tag-end-display-name", + help="Override the tag end label display name.", default=None) + parser.add_argument("tag_start", nargs='?', default="PREV",) + parser.add_argument("tag_end", nargs='?', default="HEAD") + + return parser.parse_args() + + +if __name__ == "__main__": + args = process_arguments() + previous_source_tag = find_previous_source_tag() if args.tag_start == "PREV" else args.tag_start + messages = get_merge_commits(previous_source_tag) + prs = extract_pr_numbers(messages) + pr_numbers = extract_pr_numbers(get_merge_commits()) + + summaries = [] + for pr_number in prs: + pr = fetch_pr(args.project, pr_number) + + if pr["user"]["login"] in IGNORED_CONTRIBUTORS: + continue + + if pr["merged"]: + summaries.append(extract_summary(pr)) + + sorted_summaries = sorted(summaries, key=lambda x: x["label"]) + + tag_end_label = 'latest' if args.tag_end == "HEAD" else args.tag_end + tag_end_label = tag_end_label if tag_end_display_name is None else tag_end_display_name + write_out_to_changelog_file(sorted_summaries, tag_end="latest") diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml new file mode 100644 index 000000000..9f06748fc --- /dev/null +++ b/.github/workflows/release-changelog.yml @@ -0,0 +1,69 @@ +name: Generate and Commit Changelog + +on: + workflow_dispatch: + inputs: + source_branch: + description: "Working branch (usually main)" + default: "main" + required: true + +permissions: + contents: write + pull-requests: read + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Determine latest release staging branch + id: determine_release + run: | + branch=$(git branch -r \ + | grep 'origin/release-staging-v' \ + | sed 's|origin/||' \ + | sort -V \ + | tail -1) + + if [ -z "$branch" ]; then + echo "::error::No release staging branch found" + exit 1 + fi + + echo "Using release branch: $branch" + echo "RELEASE_BRANCH=$branch" >> "$GITHUB_ENV" + + # Extract release tag by removing prefix + tag="${branch#release-staging-v}" + echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" + + - name: Checkout release staging branch + run: | + git checkout "$RELEASE_BRANCH" + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Generate changelog + env: + GH_TOKEN: ${{ github.token }} + SOURCE_BRANCH: ${{ inputs.source_branch }} + RELEASE_TAG: ${{ env.RELEASE_TAG }} + run: | + python .github/scripts/generate_changelog.py -p hsorby/libcellml + + - name: Commit changelog + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add changelog_*.rst + git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" + git push From 45dc79e38420954872ac7d1d419cb05bdd5ccbcd Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:44:53 +1200 Subject: [PATCH 037/117] Remove whitespace before grepped output of staging branch. --- .github/workflows/release-changelog.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9f06748fc..399724ceb 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -25,9 +25,10 @@ jobs: - name: Determine latest release staging branch id: determine_release run: | + git branch -r branch=$(git branch -r \ | grep 'origin/release-staging-v' \ - | sed 's|origin/||' \ + | sed 's|[ \t]*origin/||' \ | sort -V \ | tail -1) From a79d0a5088d71dd83968dbd226414d55ea390477 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:45:55 +1200 Subject: [PATCH 038/117] Use version 3.12 in release-changelog. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 399724ceb..724821928 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -50,7 +50,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Generate changelog env: From cf52090199dd862b28acd3605f4c7e6219172662 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:47:34 +1200 Subject: [PATCH 039/117] Try an not install python if running workflow using act. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 724821928..5959d12d7 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -25,7 +25,6 @@ jobs: - name: Determine latest release staging branch id: determine_release run: | - git branch -r branch=$(git branch -r \ | grep 'origin/release-staging-v' \ | sed 's|[ \t]*origin/||' \ @@ -49,6 +48,7 @@ jobs: git checkout "$RELEASE_BRANCH" - uses: actions/setup-python@v5 + if: !env.ACT with: python-version: "3.12" From 9d035486f1fba977c2ded070d5ea6521702e7f27 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:49:55 +1200 Subject: [PATCH 040/117] Use proper if statement syntax for Github actions. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 5959d12d7..9f70416f7 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -48,7 +48,7 @@ jobs: git checkout "$RELEASE_BRANCH" - uses: actions/setup-python@v5 - if: !env.ACT + if: ${{ env.ACT != 'true' }} with: python-version: "3.12" From 1290080c04b81abcf16cb568e6bb94aaecbfadbc Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 17:17:27 +1200 Subject: [PATCH 041/117] Look at error with rsync copying files in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index a62451cd3..0eceea0a8 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -50,12 +50,14 @@ jobs: - name: Copy changes to release repo run: | - rsync -av --exclude='.git' source-repo/ release-repo/ + rsync -avW --exclude='.git' source-repo/ release-repo/ - name: Commit version bump run: | cd release-repo + git status + git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git commit -am "Changes to update the codebase to release v${{ inputs.version }}." From b983c840eda7ed1e9f60ca2b28c521996d630118 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:02:26 +1200 Subject: [PATCH 042/117] Improve creation of code in release staging branch. --- .github/workflows/release-prepare.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 0eceea0a8..e9214b075 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -50,17 +50,16 @@ jobs: - name: Copy changes to release repo run: | - rsync -avW --exclude='.git' source-repo/ release-repo/ + rsync -avW --delete --exclude='.git' source-repo/ release-repo/ - name: Commit version bump run: | cd release-repo - git status - git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git commit -am "Changes to update the codebase to release v${{ inputs.version }}." + git add -A + git commit -m "Changes to update the codebase to release v${{ inputs.version }}." - name: Push branch run: | From 34a7d7ad65b1c9804789f704d009f8b1638475d1 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:41:35 +1200 Subject: [PATCH 043/117] Fix up generate_changelog.py and release-changelog.yml to a working state. --- .github/scripts/generate_changelog.py | 20 ++++++++++---------- .github/workflows/release-changelog.yml | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py index b3dafbb3a..9731561ca 100644 --- a/.github/scripts/generate_changelog.py +++ b/.github/scripts/generate_changelog.py @@ -1,3 +1,4 @@ +import argparse import json import os import re @@ -96,12 +97,12 @@ def choose_primary_label(labels): def extract_summary(pr): - label = choose_primary_label(pr["labels"]) - secondary = [l["name"] for l in pr["labels"] if l["name"] != primary] + primary_label = choose_primary_label(pr["labels"]) + secondary_labels = [l["name"] for l in pr["labels"] if l["name"] != primary_label] return { "title": pr["title"], - "label": label, - "secondary_labels": secondary, + "label": primary_label, + "secondary_labels": secondary_labels, "number": pr["number"], "url": pr["html_url"], "user": pr["user"]["login"], @@ -170,11 +171,10 @@ def process_arguments(): args = process_arguments() previous_source_tag = find_previous_source_tag() if args.tag_start == "PREV" else args.tag_start messages = get_merge_commits(previous_source_tag) - prs = extract_pr_numbers(messages) - pr_numbers = extract_pr_numbers(get_merge_commits()) + pr_numbers = extract_pr_numbers(messages) summaries = [] - for pr_number in prs: + for pr_number in pr_numbers: pr = fetch_pr(args.project, pr_number) if pr["user"]["login"] in IGNORED_CONTRIBUTORS: @@ -185,6 +185,6 @@ def process_arguments(): sorted_summaries = sorted(summaries, key=lambda x: x["label"]) - tag_end_label = 'latest' if args.tag_end == "HEAD" else args.tag_end - tag_end_label = tag_end_label if tag_end_display_name is None else tag_end_display_name - write_out_to_changelog_file(sorted_summaries, tag_end="latest") + tag_end_label = "latest" if args.tag_end == "HEAD" else f"v{args.tag_end}" + tag_end_label = tag_end_label if args.tag_end_display_name is None else args.tag_end_display_name + write_out_to_changelog_file(sorted_summaries, tag_end=tag_end_label) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9f70416f7..baa4d1ade 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -56,15 +56,15 @@ jobs: env: GH_TOKEN: ${{ github.token }} SOURCE_BRANCH: ${{ inputs.source_branch }} - RELEASE_TAG: ${{ env.RELEASE_TAG }} run: | - python .github/scripts/generate_changelog.py -p hsorby/libcellml + python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} - name: Commit changelog run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add changelog_*.rst + mv changelog_*.rst docs/changelogs/ + git add docs/changelogs/changelog_*.rst git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" git push From 269a35527853630787e802dccde819aac63db1c8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:46:52 +1200 Subject: [PATCH 044/117] Add clean and reindexing script to release-changelog.yml. --- .../scripts/clean_and_reindex_changelogs.py | 126 ++++++++++++++++++ .github/workflows/release-changelog.yml | 3 +- 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/clean_and_reindex_changelogs.py diff --git a/.github/scripts/clean_and_reindex_changelogs.py b/.github/scripts/clean_and_reindex_changelogs.py new file mode 100644 index 000000000..83fcb5dab --- /dev/null +++ b/.github/scripts/clean_and_reindex_changelogs.py @@ -0,0 +1,126 @@ +import argparse +import os +import sys + +from packaging.version import Version + +from abi.buildbotscripts.libcellml.utilities import unwanted_versions + +CHANGELOG_PREFIX = "changelog_v" +CHANGELOG_TOC_START_MARKER = " # CHANGELOG_TOC_START_MARKER\n" +CHANGELOG_TOC_END_MARKER = " # CHANGELOG_TOC_END_MARKER\n" + + +def _get_changelog_file_names(location): + current_dir = os.path.abspath(os.path.curdir) + working_dir = os.path.join(current_dir, location) + os.chdir(working_dir) + + changelogs = [] + for root, dirs, files in os.walk(".", topdown=False): + for name in files: + parts = os.path.splitext(name) + if len(parts) == 2 and parts[0].startswith(CHANGELOG_PREFIX) and parts[1] == ".rst": + # changelogs.append(os.path.join(root, name)) + changelogs.append(parts[0].replace(CHANGELOG_PREFIX, '')) + + os.chdir(current_dir) + + return changelogs + + +def _remove_changelogs(location, versions): + for version_ in versions: + os.remove(os.path.join(location, f"{CHANGELOG_PREFIX}{version_}.rst")) + + +def _create_text(versions, with_directory=False): + if with_directory: + changelog_dir = "changelogs/" + else: + changelog_dir = "" + + text = [ + "\n", + "Changelogs\n", + "==========\n", + "\n", + ".. toctree::\n", + "\n", + ] + for v in versions: + text.append(f" {changelog_dir}{CHANGELOG_PREFIX}{v}\n") + text.append("\n") + + return text + + +def _write_changelog_index_file(file_name, versions): + text = _create_text(versions) + + with open(file_name, 'w') as f: + for line in text: + f.write(line) + + +def _write_main_index_file(file_name, versions): + with open(file_name) as f: + lines = f.readlines() + + start_index = lines.index(CHANGELOG_TOC_START_MARKER) + # We subtract one line to include the restructured text comment marker. + end_index = lines.index(CHANGELOG_TOC_END_MARKER) - 1 + target_lines = [j for i, j in enumerate(lines) + if not start_index < i < end_index] + + text = _create_text(versions, with_directory=True) + + # Insert our generated changelog toc into current file content. + insert_index = start_index + 1 + target_lines[insert_index:insert_index] = text + + with open(file_name, 'w') as f: + for line in target_lines: + f.write(line) + + +def process_arguments(): + parser = argparse.ArgumentParser(description="Update the table of contents to match existing changelogs.") + parser.add_argument("local_repo", + help="The location of the project repository. Absolute path or relative path relative to the " + "current working directory.") + + return parser + + +def main(): + parser = process_arguments() + args = parser.parse_args() + repo_relative_path = args.local_repo + + cur_dir = os.path.abspath(os.curdir) + + repo_path = os.path.join(cur_dir, repo_relative_path) + if not os.path.isfile(os.path.join(repo_path, 'CMakeLists.txt')): + sys.exit(2) + + if not os.path.isdir(os.path.join(repo_path, 'docs', 'changelogs')): + sys.exit(3) + + changelogs = _get_changelog_file_names(os.path.join(repo_path, 'docs', 'changelogs')) + sorted_changelogs = sorted(changelogs, key=Version) + sorted_changelogs.reverse() + + remove_versions = unwanted_versions(sorted_changelogs, depth=3) + wanted_changelogs = list(filter(lambda x: x not in remove_versions, sorted_changelogs)) + _remove_changelogs(os.path.join(repo_path, 'docs', 'changelogs'), remove_versions) + + main_index_file = os.path.join(repo_path, 'docs', 'index.rst') + _write_main_index_file(main_index_file, wanted_changelogs) + + changelog_index_file = os.path.join(repo_path, 'docs', 'changelogs', 'index.rst') + _write_changelog_index_file(changelog_index_file, wanted_changelogs) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index baa4d1ade..37ef1d43c 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -58,6 +58,7 @@ jobs: SOURCE_BRANCH: ${{ inputs.source_branch }} run: | python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} + python .github/scripts/clean_and_reindex_changelogs.py . - name: Commit changelog run: | @@ -65,6 +66,6 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" mv changelog_*.rst docs/changelogs/ - git add docs/changelogs/changelog_*.rst + git add docs/changelogs/changelog_*.rst docs/changelogs/index.rst docs/index.rst git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" git push From fb146aa93ed889cb1b5ca39a42f540090e9987d9 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:51:26 +1200 Subject: [PATCH 045/117] Add utilities Python file for unwanted_versions function. --- .../scripts/clean_and_reindex_changelogs.py | 2 +- .github/scripts/utilities.py | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/utilities.py diff --git a/.github/scripts/clean_and_reindex_changelogs.py b/.github/scripts/clean_and_reindex_changelogs.py index 83fcb5dab..4192474cb 100644 --- a/.github/scripts/clean_and_reindex_changelogs.py +++ b/.github/scripts/clean_and_reindex_changelogs.py @@ -4,7 +4,7 @@ from packaging.version import Version -from abi.buildbotscripts.libcellml.utilities import unwanted_versions +from .utilities import unwanted_versions CHANGELOG_PREFIX = "changelog_v" CHANGELOG_TOC_START_MARKER = " # CHANGELOG_TOC_START_MARKER\n" diff --git a/.github/scripts/utilities.py b/.github/scripts/utilities.py new file mode 100644 index 000000000..3ca933d3b --- /dev/null +++ b/.github/scripts/utilities.py @@ -0,0 +1,36 @@ +from packaging.version import parse + + +def _match_to_depth(version_1, version_2, depth): + major_equal = version_1.major == version_2.major + minor_equal = version_1.minor == version_2.minor + patch_equal = version_1.micro == version_2.micro + if depth == 1: + return major_equal + elif depth == 2: + return major_equal and minor_equal + + return major_equal and minor_equal and patch_equal + + +def unwanted_versions(versions, depth=2): + remove_versions = [] + index = 0 + while index < len(versions): + next_ = index + 1 + v = parse(versions[index]) + v_next = None + if next_ < len(versions): + v_next = parse(versions[next_]) + + if v_next is None: + index += 1 + else: + if _match_to_depth(v, v_next, depth): + remove_versions.append(versions[next_]) + versions.pop(next_) + else: + index += 1 + + return remove_versions + From 07dad530b683d9f519bec236f270bbd886312ae8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:53:00 +1200 Subject: [PATCH 046/117] Don't do relative import from non-package in clean_and_reindex_changelogs.py. --- .github/scripts/clean_and_reindex_changelogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/clean_and_reindex_changelogs.py b/.github/scripts/clean_and_reindex_changelogs.py index 4192474cb..f88cf3edb 100644 --- a/.github/scripts/clean_and_reindex_changelogs.py +++ b/.github/scripts/clean_and_reindex_changelogs.py @@ -4,7 +4,7 @@ from packaging.version import Version -from .utilities import unwanted_versions +from utilities import unwanted_versions CHANGELOG_PREFIX = "changelog_v" CHANGELOG_TOC_START_MARKER = " # CHANGELOG_TOC_START_MARKER\n" From e46850d35184b9fab2ddda5ce7c1cf1fb7c56a37 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 19:03:36 +1200 Subject: [PATCH 047/117] Move generated changelog into changelogs directory so it can be indexed. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 37ef1d43c..dcd7332a1 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -58,6 +58,7 @@ jobs: SOURCE_BRANCH: ${{ inputs.source_branch }} run: | python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} + mv changelog_*.rst docs/changelogs/ python .github/scripts/clean_and_reindex_changelogs.py . - name: Commit changelog @@ -65,7 +66,6 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - mv changelog_*.rst docs/changelogs/ git add docs/changelogs/changelog_*.rst docs/changelogs/index.rst docs/index.rst git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" git push From 74cbc60c8c064d4e3d50b39c69d1887b356af7e4 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 19:15:27 +1200 Subject: [PATCH 048/117] Install requests package for generating changelogs. --- .github/workflows/release-changelog.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index dcd7332a1..9927ae0fd 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -47,7 +47,7 @@ jobs: run: | git checkout "$RELEASE_BRANCH" - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 if: ${{ env.ACT != 'true' }} with: python-version: "3.12" @@ -57,6 +57,7 @@ jobs: GH_TOKEN: ${{ github.token }} SOURCE_BRANCH: ${{ inputs.source_branch }} run: | + pip install requests python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} mv changelog_*.rst docs/changelogs/ python .github/scripts/clean_and_reindex_changelogs.py . From 8b884e603e61c9c8ceca77d1bd73f475e8cc8e26 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 19:17:09 +1200 Subject: [PATCH 049/117] Also install packaging package for generating changelogs. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9927ae0fd..33247e339 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -57,7 +57,7 @@ jobs: GH_TOKEN: ${{ github.token }} SOURCE_BRANCH: ${{ inputs.source_branch }} run: | - pip install requests + pip install requests packaging python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} mv changelog_*.rst docs/changelogs/ python .github/scripts/clean_and_reindex_changelogs.py . From 532edbd175d7997385038e0f3f6401053de7a45e Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 13:53:39 +1200 Subject: [PATCH 050/117] Add set_version python and shell scripts. --- .github/scripts/set_version.py | 39 ++++++++++++++++++++++++++++++++++ .github/scripts/set_version.sh | 29 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 .github/scripts/set_version.py create mode 100644 .github/scripts/set_version.sh diff --git a/.github/scripts/set_version.py b/.github/scripts/set_version.py new file mode 100644 index 000000000..1b2433b39 --- /dev/null +++ b/.github/scripts/set_version.py @@ -0,0 +1,39 @@ +import argparse +import os +import subprocess + +here = os.path.abspath(os.path.dirname(__file__)) + + +def run_set_version(location, core, developer): + current_dir = os.path.abspath(os.path.curdir) + working_dir = os.path.join(current_dir, location) + os.chdir(working_dir) + + subprocess.call(f"{os.path.join(here, 'set_version.sh')} {core} {developer}", shell=True) + + os.chdir(current_dir) + + +def _process_arguments(): + parser = argparse.ArgumentParser(description="Update all version numbers in the libCellML codebase.", + prefix_chars='/') + parser.add_argument("directory", + help="Relative directory from current working directory" + " to directory of libCellML source code.") + parser.add_argument("core", + help="Core version number in the form X.Y.Z where X, Y, and Z are whole numbers.") + parser.add_argument("developer", + nargs="?", default="", + help="Developer version number in the form -{dev|rc}.W where W is a whole number.") + + return parser.parse_args() + + +def main(): + args = _process_arguments() + run_set_version(args.directory, args.core, args.developer) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh new file mode 100644 index 000000000..a54315942 --- /dev/null +++ b/.github/scripts/set_version.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +version=$1 +developer_version=$2 + +IFS='.' read -ra version_array <<< "$version" +numeric_version=`printf %02d "${version_array[@]}"` +sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +sed -i '' 's@ EXPECT_EQ(0x[[:digit:]]*U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp + +sed -i '' 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt +sed -i '' 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt + +files=`grep -rl "^LIBCELLML_VERSION = \"\d*.\d*.\d*\"" *` +sed -i '' 's@LIBCELLML_VERSION = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\"@LIBCELLML_VERSION = \"'${version}'\"@' $files + +files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` +sed -i '' -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files + +files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` +sed -i '' -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files + +files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` +sed -i '' 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files + +files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` +sed -i '' "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files + +exit 0 From c7415df9dfcee06b5c123e0235bdeac3c80a0034 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:16:10 +1200 Subject: [PATCH 051/117] Update setting version numbers script parameters. --- .github/workflows/release-prepare.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 2622b30bf..297a53645 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -25,7 +25,11 @@ jobs: - name: Update version numbers run: | - python3 ./scripts/set_version.py ${{ inputs.version }} + version=${{ inputs.version }} + core=$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$version") + dev="${version#"$core"}" + echo "Core version: $core, Dev suffix: $dev" + python3 .github/scripts/set_version.py . $core $dev - name: Commit version bump run: | From 94f93474f48e8c13bd1fdd1f61ce32a9bc1ff738 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:37:15 +1200 Subject: [PATCH 052/117] Use actions/checkout@v6 in release-prepare.yml. --- .github/workflows/release-prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 297a53645..3bf1efec0 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ inputs.source_branch }} From 4b63196a81ac74176270fc3023e742ad7a61fa72 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:38:10 +1200 Subject: [PATCH 053/117] Set set_version.sh as executable. --- .github/scripts/set_version.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/set_version.sh diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh old mode 100644 new mode 100755 From 1abcbaa56c43f14dc159547e3d75efebd0908717 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:42:14 +1200 Subject: [PATCH 054/117] Set a defulat value of 'main' for the source branch. --- .github/workflows/release-prepare.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3bf1efec0..b0911ea7f 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -5,6 +5,7 @@ on: inputs: source_branch: description: "Base branch (main or tag)" + default: 'main' required: true version: description: "Release version (e.g. 0.7.1)" From 51d08f7a5f406ef3a16f585072b08aba9e38adc4 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:46:05 +1200 Subject: [PATCH 055/117] Add debug printing to set_version.sh. --- .github/scripts/set_version.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index a54315942..1d4cfddba 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -3,6 +3,9 @@ version=$1 developer_version=$2 +echo "Setting version to ${version} and developer version to ${developer_version}." +pwd + IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp From 969a7694cb6550d004eb9d5edda2bce66ea9461f Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 16:49:04 +1200 Subject: [PATCH 056/117] Add more debug printing to set_version.sh. --- .github/scripts/set_version.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 1d4cfddba..2626064a7 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -6,6 +6,10 @@ developer_version=$2 echo "Setting version to ${version} and developer version to ${developer_version}." pwd +ls tests/version/version.cpp + +echo 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' + IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp From e3f182f9f5e7cde2d2a6c3eec6a705f5f909beb9 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 17:03:27 +1200 Subject: [PATCH 057/117] Update set_version.sh to run on GNU bash, improve version match regexp. --- .github/scripts/set_version.sh | 35 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 2626064a7..78212e946 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -3,34 +3,27 @@ version=$1 developer_version=$2 -echo "Setting version to ${version} and developer version to ${developer_version}." -pwd - -ls tests/version/version.cpp - -echo 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' - IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` -sed -i '' 's@ EXPECT_EQ(\"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp -sed -i '' 's@ EXPECT_EQ(0x[[:digit:]]*U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp +sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp -sed -i '' 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt -sed -i '' 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt +sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt +sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt -files=`grep -rl "^LIBCELLML_VERSION = \"\d*.\d*.\d*\"" *` -sed -i '' 's@LIBCELLML_VERSION = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\"@LIBCELLML_VERSION = \"'${version}'\"@' $files +files=`grep -rl "^LIBCELLML_VERSION = \"\d+.\d+.\d+\"" *` +sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files -files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` -sed -i '' -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files +files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files -files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` -sed -i '' -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files +files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d+\.\d+\.\d+\." *` +sed -i -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files -files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` -sed -i '' 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files +files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d+\.\d+\.\d+\";" *` +sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files -files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` -sed -i '' "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files +files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d+\.\d+\.\d+');" *` +sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files exit 0 From 871400bcbff25556516e94720f4f87890260aee5 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 17:09:35 +1200 Subject: [PATCH 058/117] Update peter-evans/create-pull-request in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index b0911ea7f..5e0e6fdd7 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -42,10 +42,10 @@ jobs: run: git push origin HEAD - name: Open PR - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v8 with: base: release - head: release-staging-v${{ inputs.version }} + branch: release-staging-v${{ inputs.version }} title: "Release v${{ inputs.version }}" body: | Automated release PR for v${{ inputs.version }} From 4f02bce1d77021b7438fff54ed9f39e0f744b77c Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 20:34:07 +1200 Subject: [PATCH 059/117] Add debug echo statements to release-prepare.yml. --- .github/scripts/set_version.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 78212e946..ccf3b3cf4 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -5,6 +5,7 @@ developer_version=$2 IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` + sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp @@ -12,9 +13,11 @@ sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMak sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt files=`grep -rl "^LIBCELLML_VERSION = \"\d+.\d+.\d+\"" *` +echo "files 0: $files" sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files -files=`grep -rl --exclude=simple-sed.sh "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +echo "files 1: $files" sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d+\.\d+\.\d+\." *` @@ -26,4 +29,6 @@ sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:di files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d+\.\d+\.\d+');" *` sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files +git status + exit 0 From 0c3a2d09f58ac6acdd14197c2f151dcea790b0c8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 21:53:52 +1200 Subject: [PATCH 060/117] Change find regexp in release-prepare.yml. --- .github/scripts/set_version.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index ccf3b3cf4..c47d3c72f 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -12,21 +12,21 @@ sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_ve sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt -files=`grep -rl "^LIBCELLML_VERSION = \"\d+.\d+.\d+\"" *` +files=`grep -rl "^LIBCELLML_VERSION = \"\d*\.\d*\.\d*\"" *` echo "files 0: $files" sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files -files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d+\.\d+\.\d+\. \*/" *` +files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` echo "files 1: $files" sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files -files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d+\.\d+\.\d+\." *` +files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` sed -i -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files -files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d+\.\d+\.\d+\";" *` +files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files -files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d+\.\d+\.\d+');" *` +files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files git status From 0e6662302ac87ff911f90fce8d966e74e8cffbed Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 28 Apr 2026 22:27:46 +1200 Subject: [PATCH 061/117] Add debug messages in release-prepare.yml. --- .github/scripts/set_version.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index c47d3c72f..e97717120 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -7,7 +7,11 @@ IFS='.' read -ra version_array <<< "$version" numeric_version=`printf %02d "${version_array[@]}"` sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp +echo "1" +git status sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp +echo "2" +git status sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt From 06f464796f38bcb23a623b1a0cd064d01a6c3820 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 14:02:04 +1200 Subject: [PATCH 062/117] Ignore Act resource files. --- .github/scripts/set_version.sh | 33 +++++++++++---------------- .github/workflows/release-prepare.yml | 21 +++++++++-------- .gitignore | 4 ++++ 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index e97717120..886dedf27 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -4,35 +4,28 @@ version=$1 developer_version=$2 IFS='.' read -ra version_array <<< "$version" -numeric_version=`printf %02d "${version_array[@]}"` +numeric_version=$(printf %02d "${version_array[@]}") +version_regex='[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*' -sed -i 's@ EXPECT_EQ(\"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\", versionString);@ EXPECT_EQ(\"'${version}'\", versionString);@' tests/version/version.cpp -echo "1" -git status +sed -i "s@ EXPECT_EQ(\"${version_regex}\", versionString);@ EXPECT_EQ(\"${version}\", versionString);@" tests/version/version.cpp sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp -echo "2" -git status sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt -files=`grep -rl "^LIBCELLML_VERSION = \"\d*\.\d*\.\d*\"" *` -echo "files 0: $files" -sed -i 's@LIBCELLML_VERSION = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\"@LIBCELLML_VERSION = \"'${version}'\"@' $files +files=$(grep -rl "^LIBCELLML_VERSION = \"${version_regex}\"" .) +sed -i "s@LIBCELLML_VERSION = \"${version_regex}\"@LIBCELLML_VERSION = \"${version}\"@" $files -files=`grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML \d*\.\d*\.\d*\. \*/" *` -echo "files 1: $files" -sed -i -E 's@/\* The content of this file was generated using (the|a modified) C profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\. \*/@/\* The content of this file was generated using \1 C profile of libCellML '${version}'. \*/@' $files +files=$(grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML ${version_regex}\. \*/" *) +sed -i -E "s@/\* The content of this file was generated using (the|a modified) C profile of libCellML ${version_regex}\. \*/@/\* The content of this file was generated using \1 C profile of libCellML ${version}. \*/@" $files -files=`grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML \d*\.\d*\.\d*\." *` -sed -i -E 's@# The content of this file was generated using (the|a modified) Python profile of libCellML [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.@# The content of this file was generated using \1 Python profile of libCellML '${version}'.@' $files +files=$(grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML ${version_regex}\." *) +sed -i -E "s@# The content of this file was generated using (the|a modified) Python profile of libCellML ${version_regex}\.@# The content of this file was generated using \1 Python profile of libCellML ${version}.@" $files -files=`grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"\d*\.\d*\.\d*\";" *` -sed -i 's@const char LIBCELLML_VERSION\[\] = \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\";@const char LIBCELLML_VERSION\[\] = \"'${version}'\";@' $files +files=$(grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"${version_regex}\";" *) +sed -i "s@const char LIBCELLML_VERSION\[\] = \"${version_regex}\";@const char LIBCELLML_VERSION\[\] = \"${version}\";@" $files -files=`grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('\d*\.\d*\.\d*');" *` -sed -i "s@ expect(libcellml\.versionString()).toBe('[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+');@ expect(libcellml\.versionString()).toBe('"${version}"');@" $files - -git status +files=$(grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('${version_regex}');" *) +sed -i "s@ expect(libcellml\.versionString()).toBe('${version_regex}');@ expect(libcellml\.versionString()).toBe('${version}');@" $files exit 0 diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 5e0e6fdd7..06c7090af 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -39,13 +39,14 @@ jobs: git commit -am "Prepare release v${{ inputs.version }}" - name: Push branch - run: git push origin HEAD - - - name: Open PR - uses: peter-evans/create-pull-request@v8 - with: - base: release - branch: release-staging-v${{ inputs.version }} - title: "Release v${{ inputs.version }}" - body: | - Automated release PR for v${{ inputs.version }} + run: git push origin release-staging-v${{ inputs.version }} + +# - name: Open PR +# uses: peter-evans/create-pull-request@v8 +# with: +# base: release +# branch: release-staging-v${{ inputs.version }} +# title: "Proposed release v${{ inputs.version }}" +# draft: true +# body: | +# Automated release PR for v${{ inputs.version }} diff --git a/.gitignore b/.gitignore index df4e47f0e..57613db32 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ CMakeLists.txt.user # VS Code system files .vscode/ + +# Act resource files +.actrc +act-payload.json From f6a7542c31605836023689ac9ce792e73ccfae40 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:09:23 +1200 Subject: [PATCH 063/117] Re-instate creation of PR when a release process is started. --- .github/workflows/release-prepare.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 06c7090af..17927c8ce 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -34,19 +34,20 @@ jobs: - name: Commit version bump run: | - git config user.name "github-actions" - git config user.email "actions@github.com" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" git commit -am "Prepare release v${{ inputs.version }}" - name: Push branch run: git push origin release-staging-v${{ inputs.version }} -# - name: Open PR -# uses: peter-evans/create-pull-request@v8 -# with: -# base: release -# branch: release-staging-v${{ inputs.version }} -# title: "Proposed release v${{ inputs.version }}" -# draft: true -# body: | -# Automated release PR for v${{ inputs.version }} + - name: Open PR + uses: peter-evans/create-pull-request@v8 + with: + base: release + branch: release-staging-v${{ inputs.version }} + title: "Proposed release changes for v${{ inputs.version }}" + draft: true + body: | + Automated release PR for v${{ inputs.version }} + **Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised. From e6ef1199019f2a9483d707b6625221a4bb2ef404 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:34:39 +1200 Subject: [PATCH 064/117] Add PR write permissions to release-prepare workflow. --- .github/workflows/release-prepare.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 17927c8ce..5c2b082d3 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -8,9 +8,13 @@ on: default: 'main' required: true version: - description: "Release version (e.g. 0.7.1)" + description: "Release version (e.g. 0.7.1, 0.7.0-post.1)" required: true +permissions: + contents: write + pull-requests: write + jobs: prepare: runs-on: ubuntu-latest From 407ada9a2ad14cd05bd9b343b41cae92dd296725 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 15:48:30 +1200 Subject: [PATCH 065/117] Try basing changes off the release branch. --- .github/workflows/release-prepare.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 5c2b082d3..e45a71187 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -36,14 +36,24 @@ jobs: echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev + - uses: actions/checkout@v6 + with: + ref: release + path: release-repo + - name: Commit version bump run: | + rm -rf .git/ + mv release-repo/.git/ ./ + rm -rf release-repo + git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git commit -am "Prepare release v${{ inputs.version }}" - name: Push branch - run: git push origin release-staging-v${{ inputs.version }} + run: | + git push origin release:release-staging-v${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 From cd3025004f2fc765caf2b34f696c22aa3bf1f0bc Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Wed, 29 Apr 2026 17:01:09 +1200 Subject: [PATCH 066/117] Try and fix creation of PR against release branch. --- .github/workflows/release-prepare.yml | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index e45a71187..38dc17242 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -19,33 +19,38 @@ jobs: prepare: runs-on: ubuntu-latest + env: + BRANCH_PREFIX: release-staging-v + steps: + - uses: actions/checkout@v6 + with: + ref: release + path: release-repo + - uses: actions/checkout@v6 with: ref: ${{ inputs.source_branch }} + path: source-repo - name: Create staging branch run: | - git checkout -b release-staging-v${{ inputs.version }} + cd release-repo + git checkout -b $BRANCH_PREFIX${{ inputs.version }} - name: Update version numbers run: | + cd source-repo + version=${{ inputs.version }} core=$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$version") dev="${version#"$core"}" echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev - - uses: actions/checkout@v6 - with: - ref: release - path: release-repo - - name: Commit version bump run: | - rm -rf .git/ - mv release-repo/.git/ ./ - rm -rf release-repo + cd source-repo git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -53,13 +58,17 @@ jobs: - name: Push branch run: | - git push origin release:release-staging-v${{ inputs.version }} + rm -rf source-repo/.git/ + mv release-repo/.git/ source-repo/ + + cd source-repo + git push origin $BRANCH_PREFIX${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 with: base: release - branch: release-staging-v${{ inputs.version }} + branch: ${{ env.BRANCH_PREFIX }}${{ inputs.version }} title: "Proposed release changes for v${{ inputs.version }}" draft: true body: | From 953a18484ffb014064d13d146a5c8e36019fd5e8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 10:39:55 +1200 Subject: [PATCH 067/117] Fully qualify environment BRANCH_PREFIX in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 38dc17242..7a321f39f 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -36,7 +36,7 @@ jobs: - name: Create staging branch run: | cd release-repo - git checkout -b $BRANCH_PREFIX${{ inputs.version }} + git checkout -b ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Update version numbers run: | @@ -62,7 +62,7 @@ jobs: mv release-repo/.git/ source-repo/ cd source-repo - git push origin $BRANCH_PREFIX${{ inputs.version }} + git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR uses: peter-evans/create-pull-request@v8 From 7dfbe3e2bb279da2d0579f8c690bb57a1324b333 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 10:49:55 +1200 Subject: [PATCH 068/117] Use rsync to copy changes to release staging branch. --- .github/workflows/release-prepare.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 7a321f39f..3afbe860b 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -48,9 +48,13 @@ jobs: echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev + - name: Copy changes to release repo + run: | + rsync -av --exclude='.git' source-repo/ release-repo/ + - name: Commit version bump run: | - cd source-repo + cd release-repo git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -58,10 +62,7 @@ jobs: - name: Push branch run: | - rm -rf source-repo/.git/ - mv release-repo/.git/ source-repo/ - - cd source-repo + cd release-repo git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR From 7647a746d8483332c5f68088f1ba4b83906618ab Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 11:31:14 +1200 Subject: [PATCH 069/117] Improve commit message for release changes. --- .github/workflows/release-prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3afbe860b..4cbda0068 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -58,7 +58,7 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git commit -am "Prepare release v${{ inputs.version }}" + git commit -am "Changes to update the codebase to release v${{ inputs.version }}." - name: Push branch run: | From 3f7c36e35ca0ca409c785e5059d294e406994e6f Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 11:34:00 +1200 Subject: [PATCH 070/117] Set release repo path for peter-evans/create-pull-request. --- .github/workflows/release-prepare.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4cbda0068..3fb6dda98 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -68,6 +68,7 @@ jobs: - name: Open PR uses: peter-evans/create-pull-request@v8 with: + path: release-repo base: release branch: ${{ env.BRANCH_PREFIX }}${{ inputs.version }} title: "Proposed release changes for v${{ inputs.version }}" From 0e29e6f504a132d7f2454b38803054d8725553ad Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:14:39 +1200 Subject: [PATCH 071/117] Use gh instead of peter-evans/create-pull-request in release-prepare.yml. --- .github/workflows/release-prepare.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 3fb6dda98..e853e0809 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -66,13 +66,10 @@ jobs: git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR - uses: peter-evans/create-pull-request@v8 - with: - path: release-repo - base: release - branch: ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - title: "Proposed release changes for v${{ inputs.version }}" - draft: true - body: | - Automated release PR for v${{ inputs.version }} - **Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised. + run: | + gh pr create \ + --base release \ + --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ + --title "Proposed release changes for v${{ inputs.version }}" \ + --body "Automated release PR for v${{ inputs.version }}\n**Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised." \ + --draft From f1770bb9ae6c6129f0f8b7a04d31009faeaf86f2 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:16:17 +1200 Subject: [PATCH 072/117] Add Github token to Create PR step in env: --- .github/workflows/release-prepare.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index e853e0809..c76070a88 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -66,6 +66,8 @@ jobs: git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR + env: + GH_TOKEN: ${{ github.token }} run: | gh pr create \ --base release \ From 2e6af9e948a5440c5bf032d6e4cde012ffbffee2 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:17:54 +1200 Subject: [PATCH 073/117] Change into release repo. before creating PR. --- .github/workflows/release-prepare.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index c76070a88..fe7882548 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -69,6 +69,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | + cd release-repo gh pr create \ --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ From 38870a8e6f7f70c464619269cf06cb173090e2d9 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:27:18 +1200 Subject: [PATCH 074/117] Try using here document to set PR body text in release-prepare.yml. --- .github/workflows/release-prepare.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index fe7882548..333649299 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -66,6 +66,7 @@ jobs: git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} - name: Open PR + id: open_pr env: GH_TOKEN: ${{ github.token }} run: | @@ -74,5 +75,15 @@ jobs: --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ --title "Proposed release changes for v${{ inputs.version }}" \ - --body "Automated release PR for v${{ inputs.version }}\n**Do not** merge this PR, it is only for review and testing. The release process will deal with it when the release is finalised." \ --draft + --body "$(cat <<'EOF' +Automated release PR for v${{ inputs.version }} + +**Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. +EOF)" + + - name: Delete remote branch if PR creation failed + if: failure() && steps.open_pr.outcome == 'failure' + run: | + cd release-repo + git push origin --delete ${{ env.BRANCH_PREFIX }}${{ inputs.version }} From fb1fc64baa53b8bec6663bb77705ac3543b3e65c Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:29:31 +1200 Subject: [PATCH 075/117] Try indenting the here document body differently. --- .github/workflows/release-prepare.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 333649299..a76054d76 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -77,10 +77,10 @@ jobs: --title "Proposed release changes for v${{ inputs.version }}" \ --draft --body "$(cat <<'EOF' -Automated release PR for v${{ inputs.version }} + Automated release PR for v${{ inputs.version }} -**Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. -EOF)" + **Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. + EOF)" - name: Delete remote branch if PR creation failed if: failure() && steps.open_pr.outcome == 'failure' From ee9b5873b7d71bc7b1aa220accfa01d3bcad2b16 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 12:34:19 +1200 Subject: [PATCH 076/117] Use a text file to store body of PR text. --- .github/workflows/release-prepare.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index a76054d76..cb3600800 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -70,17 +70,18 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | + echo "Automated release PR for v${{ inputs.version }}." > pr_body.txt + echo "" >> pr_body.txt + echo "**Do not** merge this PR, it is only for review and testing." >> pr_body.txt + echo "The release process will deal with it when the release is finalised." >> pr_body.txt + cd release-repo gh pr create \ --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ --title "Proposed release changes for v${{ inputs.version }}" \ + --body-file ../pr_body.txt \ --draft - --body "$(cat <<'EOF' - Automated release PR for v${{ inputs.version }} - - **Do not** merge this PR, it is only for review and testing.The release process will deal with it when the release is finalised. - EOF)" - name: Delete remote branch if PR creation failed if: failure() && steps.open_pr.outcome == 'failure' From 27672e4d0ec31d4eedc75ad463467f75296f7c27 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 14:20:36 +1200 Subject: [PATCH 077/117] Improve the title of the created PR. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index cb3600800..a62451cd3 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -8,7 +8,7 @@ on: default: 'main' required: true version: - description: "Release version (e.g. 0.7.1, 0.7.0-post.1)" + description: "Release version (e.g. 0.7.1, 0.7.0-rc.1)" required: true permissions: @@ -79,7 +79,7 @@ jobs: gh pr create \ --base release \ --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ - --title "Proposed release changes for v${{ inputs.version }}" \ + --title "Proposed changes to create release v${{ inputs.version }} of libCellML" \ --body-file ../pr_body.txt \ --draft From 4c846bd4a4aa93db602de2019bb8eaa55b56395a Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:36:39 +1200 Subject: [PATCH 078/117] Add initial generate changelog GitHub Actions scripts/workflow. --- .github/scripts/generate_changelog.py | 190 ++++++++++++++++++++++++ .github/workflows/release-changelog.yml | 69 +++++++++ 2 files changed, 259 insertions(+) create mode 100644 .github/scripts/generate_changelog.py create mode 100644 .github/workflows/release-changelog.yml diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py new file mode 100644 index 000000000..b3dafbb3a --- /dev/null +++ b/.github/scripts/generate_changelog.py @@ -0,0 +1,190 @@ +import json +import os +import re +import subprocess +import requests +from packaging import version as semver + +GITHUB_API = "https://api.github.com" +IGNORED_CONTRIBUTORS = ['abi-git-user', 'github-actions[bot]'] + +HEADERS = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {os.environ['GH_TOKEN']}" +} + +TAG_PATTERN = re.compile(r"^source-v(\d+\.\d+\.\d+)$") +LABEL_PRIORITY = [] + + +class Contributor: + + def __init__(self, avatar_url, user_url): + self._avatar_url = avatar_url + self._user_url = user_url + + @property + def avatar_url(self): + return self._avatar_url + + @property + def user_url(self): + return self._user_url + + def __eq__(self, other): + return isinstance(other, Contributor) and \ + self.user_url == other.user_url and \ + self.avatar_url == other.avatar_url + + def __hash__(self): + return hash((self.user_url, self.avatar_url)) + + +def run(cmd): + return subprocess.check_output(cmd, text=True).strip() + + +def find_previous_source_tag(): + tags = run(["git", "tag"]).splitlines() + valid = [] + + for t in tags: + m = TAG_PATTERN.match(t) + if m: + valid.append((semver.parse(m.group(1)), t)) + + if not valid: + raise RuntimeError("No valid source-vX.Y.Z tags found") + + valid.sort(reverse=True) + return valid[0][1] + + +def get_merge_commits(start, end="HEAD"): + log = run([ + "git", "log", + f"{start}..{end}", + "--merges", + "--first-parent", + "--pretty=%s" + ]) + return log.splitlines() + + +def extract_pr_numbers(messages): + prs = [] + for msg in messages: + m = re.search(r"#(\d+)", msg) + if m: + prs.append(int(m.group(1))) + return prs + + +def fetch_pr(org_repo, pr_number): + url = f"{GITHUB_API}/repos/{org_repo}/pulls/{pr_number}" + r = requests.get(url, headers=HEADERS) + r.raise_for_status() + return r.json() + + +def choose_primary_label(labels): + names = [l["name"] for l in labels] + for p in LABEL_PRIORITY: + if p in names: + return p + return names[0] if names else "No category" + + +def extract_summary(pr): + label = choose_primary_label(pr["labels"]) + secondary = [l["name"] for l in pr["labels"] if l["name"] != primary] + return { + "title": pr["title"], + "label": label, + "secondary_labels": secondary, + "number": pr["number"], + "url": pr["html_url"], + "user": pr["user"]["login"], + "user_url": pr["user"]["html_url"], + "avatar_url": pr["user"]["avatar_url"], + } + + +def write_out_to_changelog_file(sorted_summaries, tag_end): + current_label = '' + file_name = f'changelog_{tag_end}.rst' + with open(file_name, 'w') as f: + + changelog_title = f'libCellML {tag_end} Changelog' + f.write(f'{changelog_title}\n') + f.write('=' * len(changelog_title)) + f.write('\n') + + contributors = [] + for summary in sorted_summaries: + if current_label != summary['label']: + current_label = summary['label'] + f.write(f'\n{current_label}\n') + f.write('-' * len(current_label)) + f.write('\n\n') + + title = summary['title'][:-1] if summary['title'].endswith('.') else summary['title'] + user_link = f'`@{summary["user"]} <{summary["user_url"]}>`_' + pr_link = f'`#{summary["number"]} <{summary["url"]}>`_' + suffix = "" + if summary.get("secondary_labels"): + suffix = f" (also: {', '.join(summary['secondary_labels'])})" + f.write(f'* {title}{suffix} by {user_link} [{pr_link}].\n') + contributors.append(Contributor(summary['avatar_url'], summary['user_url'])) + + contributors = list(set(contributors)) + if contributors: + section_title = 'Contributors' + f.write(f'\n{section_title}\n') + f.write('-' * len(section_title)) + f.write('\n\n') + for contributor in contributors: + f.write(f'.. image:: {contributor.avatar_url}\n :target: {contributor.user_url}' + f'\n :height: 32\n :width: 32\n') + + print(f'Changelog written to: {file_name}.') + + +def process_arguments(): + parser = argparse.ArgumentParser(description="Create a simple change log from merged pull requests from a GitHub " + "project.") + parser.add_argument("-p", "--project", + help="GitHub project to work with, default 'cellml/libcellml'.", default="cellml/libcellml") + # parser.add_argument("-r", "--local-repo", + # help="The location of the project repository. Absolute path or relative path relative to the " + # "current working directory.", default=None) + parser.add_argument("-t", "--tag-end-display-name", + help="Override the tag end label display name.", default=None) + parser.add_argument("tag_start", nargs='?', default="PREV",) + parser.add_argument("tag_end", nargs='?', default="HEAD") + + return parser.parse_args() + + +if __name__ == "__main__": + args = process_arguments() + previous_source_tag = find_previous_source_tag() if args.tag_start == "PREV" else args.tag_start + messages = get_merge_commits(previous_source_tag) + prs = extract_pr_numbers(messages) + pr_numbers = extract_pr_numbers(get_merge_commits()) + + summaries = [] + for pr_number in prs: + pr = fetch_pr(args.project, pr_number) + + if pr["user"]["login"] in IGNORED_CONTRIBUTORS: + continue + + if pr["merged"]: + summaries.append(extract_summary(pr)) + + sorted_summaries = sorted(summaries, key=lambda x: x["label"]) + + tag_end_label = 'latest' if args.tag_end == "HEAD" else args.tag_end + tag_end_label = tag_end_label if tag_end_display_name is None else tag_end_display_name + write_out_to_changelog_file(sorted_summaries, tag_end="latest") diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml new file mode 100644 index 000000000..9f06748fc --- /dev/null +++ b/.github/workflows/release-changelog.yml @@ -0,0 +1,69 @@ +name: Generate and Commit Changelog + +on: + workflow_dispatch: + inputs: + source_branch: + description: "Working branch (usually main)" + default: "main" + required: true + +permissions: + contents: write + pull-requests: read + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Determine latest release staging branch + id: determine_release + run: | + branch=$(git branch -r \ + | grep 'origin/release-staging-v' \ + | sed 's|origin/||' \ + | sort -V \ + | tail -1) + + if [ -z "$branch" ]; then + echo "::error::No release staging branch found" + exit 1 + fi + + echo "Using release branch: $branch" + echo "RELEASE_BRANCH=$branch" >> "$GITHUB_ENV" + + # Extract release tag by removing prefix + tag="${branch#release-staging-v}" + echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" + + - name: Checkout release staging branch + run: | + git checkout "$RELEASE_BRANCH" + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Generate changelog + env: + GH_TOKEN: ${{ github.token }} + SOURCE_BRANCH: ${{ inputs.source_branch }} + RELEASE_TAG: ${{ env.RELEASE_TAG }} + run: | + python .github/scripts/generate_changelog.py -p hsorby/libcellml + + - name: Commit changelog + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add changelog_*.rst + git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" + git push From 3427b6db643570a801c829e644e4aba9670d20b8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:44:53 +1200 Subject: [PATCH 079/117] Remove whitespace before grepped output of staging branch. --- .github/workflows/release-changelog.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9f06748fc..399724ceb 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -25,9 +25,10 @@ jobs: - name: Determine latest release staging branch id: determine_release run: | + git branch -r branch=$(git branch -r \ | grep 'origin/release-staging-v' \ - | sed 's|origin/||' \ + | sed 's|[ \t]*origin/||' \ | sort -V \ | tail -1) From a3bd9365dc88a8c4aaa341b714b52f0abab67c66 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:45:55 +1200 Subject: [PATCH 080/117] Use version 3.12 in release-changelog. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 399724ceb..724821928 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -50,7 +50,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Generate changelog env: From 2120a565d61733913f6c8a35f4c117cfdbd8ef5a Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:47:34 +1200 Subject: [PATCH 081/117] Try an not install python if running workflow using act. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 724821928..5959d12d7 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -25,7 +25,6 @@ jobs: - name: Determine latest release staging branch id: determine_release run: | - git branch -r branch=$(git branch -r \ | grep 'origin/release-staging-v' \ | sed 's|[ \t]*origin/||' \ @@ -49,6 +48,7 @@ jobs: git checkout "$RELEASE_BRANCH" - uses: actions/setup-python@v5 + if: !env.ACT with: python-version: "3.12" From b953618b21cf44ddc1add8beddf8135e7aafb513 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 16:49:55 +1200 Subject: [PATCH 082/117] Use proper if statement syntax for Github actions. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 5959d12d7..9f70416f7 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -48,7 +48,7 @@ jobs: git checkout "$RELEASE_BRANCH" - uses: actions/setup-python@v5 - if: !env.ACT + if: ${{ env.ACT != 'true' }} with: python-version: "3.12" From 8da4621b264e38c86f41e3d8b95657e1d1cf6d1b Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 17:17:27 +1200 Subject: [PATCH 083/117] Look at error with rsync copying files in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index a62451cd3..0eceea0a8 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -50,12 +50,14 @@ jobs: - name: Copy changes to release repo run: | - rsync -av --exclude='.git' source-repo/ release-repo/ + rsync -avW --exclude='.git' source-repo/ release-repo/ - name: Commit version bump run: | cd release-repo + git status + git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git commit -am "Changes to update the codebase to release v${{ inputs.version }}." From 350fb16ca52a117cdc6e94ee0af3f165799e64c5 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:02:26 +1200 Subject: [PATCH 084/117] Improve creation of code in release staging branch. --- .github/workflows/release-prepare.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 0eceea0a8..e9214b075 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -50,17 +50,16 @@ jobs: - name: Copy changes to release repo run: | - rsync -avW --exclude='.git' source-repo/ release-repo/ + rsync -avW --delete --exclude='.git' source-repo/ release-repo/ - name: Commit version bump run: | cd release-repo - git status - git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git commit -am "Changes to update the codebase to release v${{ inputs.version }}." + git add -A + git commit -m "Changes to update the codebase to release v${{ inputs.version }}." - name: Push branch run: | From 70dad8243148efc890873baedadcd7cf6dc68697 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:41:35 +1200 Subject: [PATCH 085/117] Fix up generate_changelog.py and release-changelog.yml to a working state. --- .github/scripts/generate_changelog.py | 20 ++++++++++---------- .github/workflows/release-changelog.yml | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py index b3dafbb3a..9731561ca 100644 --- a/.github/scripts/generate_changelog.py +++ b/.github/scripts/generate_changelog.py @@ -1,3 +1,4 @@ +import argparse import json import os import re @@ -96,12 +97,12 @@ def choose_primary_label(labels): def extract_summary(pr): - label = choose_primary_label(pr["labels"]) - secondary = [l["name"] for l in pr["labels"] if l["name"] != primary] + primary_label = choose_primary_label(pr["labels"]) + secondary_labels = [l["name"] for l in pr["labels"] if l["name"] != primary_label] return { "title": pr["title"], - "label": label, - "secondary_labels": secondary, + "label": primary_label, + "secondary_labels": secondary_labels, "number": pr["number"], "url": pr["html_url"], "user": pr["user"]["login"], @@ -170,11 +171,10 @@ def process_arguments(): args = process_arguments() previous_source_tag = find_previous_source_tag() if args.tag_start == "PREV" else args.tag_start messages = get_merge_commits(previous_source_tag) - prs = extract_pr_numbers(messages) - pr_numbers = extract_pr_numbers(get_merge_commits()) + pr_numbers = extract_pr_numbers(messages) summaries = [] - for pr_number in prs: + for pr_number in pr_numbers: pr = fetch_pr(args.project, pr_number) if pr["user"]["login"] in IGNORED_CONTRIBUTORS: @@ -185,6 +185,6 @@ def process_arguments(): sorted_summaries = sorted(summaries, key=lambda x: x["label"]) - tag_end_label = 'latest' if args.tag_end == "HEAD" else args.tag_end - tag_end_label = tag_end_label if tag_end_display_name is None else tag_end_display_name - write_out_to_changelog_file(sorted_summaries, tag_end="latest") + tag_end_label = "latest" if args.tag_end == "HEAD" else f"v{args.tag_end}" + tag_end_label = tag_end_label if args.tag_end_display_name is None else args.tag_end_display_name + write_out_to_changelog_file(sorted_summaries, tag_end=tag_end_label) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9f70416f7..baa4d1ade 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -56,15 +56,15 @@ jobs: env: GH_TOKEN: ${{ github.token }} SOURCE_BRANCH: ${{ inputs.source_branch }} - RELEASE_TAG: ${{ env.RELEASE_TAG }} run: | - python .github/scripts/generate_changelog.py -p hsorby/libcellml + python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} - name: Commit changelog run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add changelog_*.rst + mv changelog_*.rst docs/changelogs/ + git add docs/changelogs/changelog_*.rst git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" git push From 6648a10e3732e6b808be847340e5454f163c80e3 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:46:52 +1200 Subject: [PATCH 086/117] Add clean and reindexing script to release-changelog.yml. --- .../scripts/clean_and_reindex_changelogs.py | 126 ++++++++++++++++++ .github/workflows/release-changelog.yml | 3 +- 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/clean_and_reindex_changelogs.py diff --git a/.github/scripts/clean_and_reindex_changelogs.py b/.github/scripts/clean_and_reindex_changelogs.py new file mode 100644 index 000000000..83fcb5dab --- /dev/null +++ b/.github/scripts/clean_and_reindex_changelogs.py @@ -0,0 +1,126 @@ +import argparse +import os +import sys + +from packaging.version import Version + +from abi.buildbotscripts.libcellml.utilities import unwanted_versions + +CHANGELOG_PREFIX = "changelog_v" +CHANGELOG_TOC_START_MARKER = " # CHANGELOG_TOC_START_MARKER\n" +CHANGELOG_TOC_END_MARKER = " # CHANGELOG_TOC_END_MARKER\n" + + +def _get_changelog_file_names(location): + current_dir = os.path.abspath(os.path.curdir) + working_dir = os.path.join(current_dir, location) + os.chdir(working_dir) + + changelogs = [] + for root, dirs, files in os.walk(".", topdown=False): + for name in files: + parts = os.path.splitext(name) + if len(parts) == 2 and parts[0].startswith(CHANGELOG_PREFIX) and parts[1] == ".rst": + # changelogs.append(os.path.join(root, name)) + changelogs.append(parts[0].replace(CHANGELOG_PREFIX, '')) + + os.chdir(current_dir) + + return changelogs + + +def _remove_changelogs(location, versions): + for version_ in versions: + os.remove(os.path.join(location, f"{CHANGELOG_PREFIX}{version_}.rst")) + + +def _create_text(versions, with_directory=False): + if with_directory: + changelog_dir = "changelogs/" + else: + changelog_dir = "" + + text = [ + "\n", + "Changelogs\n", + "==========\n", + "\n", + ".. toctree::\n", + "\n", + ] + for v in versions: + text.append(f" {changelog_dir}{CHANGELOG_PREFIX}{v}\n") + text.append("\n") + + return text + + +def _write_changelog_index_file(file_name, versions): + text = _create_text(versions) + + with open(file_name, 'w') as f: + for line in text: + f.write(line) + + +def _write_main_index_file(file_name, versions): + with open(file_name) as f: + lines = f.readlines() + + start_index = lines.index(CHANGELOG_TOC_START_MARKER) + # We subtract one line to include the restructured text comment marker. + end_index = lines.index(CHANGELOG_TOC_END_MARKER) - 1 + target_lines = [j for i, j in enumerate(lines) + if not start_index < i < end_index] + + text = _create_text(versions, with_directory=True) + + # Insert our generated changelog toc into current file content. + insert_index = start_index + 1 + target_lines[insert_index:insert_index] = text + + with open(file_name, 'w') as f: + for line in target_lines: + f.write(line) + + +def process_arguments(): + parser = argparse.ArgumentParser(description="Update the table of contents to match existing changelogs.") + parser.add_argument("local_repo", + help="The location of the project repository. Absolute path or relative path relative to the " + "current working directory.") + + return parser + + +def main(): + parser = process_arguments() + args = parser.parse_args() + repo_relative_path = args.local_repo + + cur_dir = os.path.abspath(os.curdir) + + repo_path = os.path.join(cur_dir, repo_relative_path) + if not os.path.isfile(os.path.join(repo_path, 'CMakeLists.txt')): + sys.exit(2) + + if not os.path.isdir(os.path.join(repo_path, 'docs', 'changelogs')): + sys.exit(3) + + changelogs = _get_changelog_file_names(os.path.join(repo_path, 'docs', 'changelogs')) + sorted_changelogs = sorted(changelogs, key=Version) + sorted_changelogs.reverse() + + remove_versions = unwanted_versions(sorted_changelogs, depth=3) + wanted_changelogs = list(filter(lambda x: x not in remove_versions, sorted_changelogs)) + _remove_changelogs(os.path.join(repo_path, 'docs', 'changelogs'), remove_versions) + + main_index_file = os.path.join(repo_path, 'docs', 'index.rst') + _write_main_index_file(main_index_file, wanted_changelogs) + + changelog_index_file = os.path.join(repo_path, 'docs', 'changelogs', 'index.rst') + _write_changelog_index_file(changelog_index_file, wanted_changelogs) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index baa4d1ade..37ef1d43c 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -58,6 +58,7 @@ jobs: SOURCE_BRANCH: ${{ inputs.source_branch }} run: | python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} + python .github/scripts/clean_and_reindex_changelogs.py . - name: Commit changelog run: | @@ -65,6 +66,6 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" mv changelog_*.rst docs/changelogs/ - git add docs/changelogs/changelog_*.rst + git add docs/changelogs/changelog_*.rst docs/changelogs/index.rst docs/index.rst git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" git push From 071edc28756e50c6edfc678045b53c8058926a10 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:51:26 +1200 Subject: [PATCH 087/117] Add utilities Python file for unwanted_versions function. --- .../scripts/clean_and_reindex_changelogs.py | 2 +- .github/scripts/utilities.py | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/utilities.py diff --git a/.github/scripts/clean_and_reindex_changelogs.py b/.github/scripts/clean_and_reindex_changelogs.py index 83fcb5dab..4192474cb 100644 --- a/.github/scripts/clean_and_reindex_changelogs.py +++ b/.github/scripts/clean_and_reindex_changelogs.py @@ -4,7 +4,7 @@ from packaging.version import Version -from abi.buildbotscripts.libcellml.utilities import unwanted_versions +from .utilities import unwanted_versions CHANGELOG_PREFIX = "changelog_v" CHANGELOG_TOC_START_MARKER = " # CHANGELOG_TOC_START_MARKER\n" diff --git a/.github/scripts/utilities.py b/.github/scripts/utilities.py new file mode 100644 index 000000000..3ca933d3b --- /dev/null +++ b/.github/scripts/utilities.py @@ -0,0 +1,36 @@ +from packaging.version import parse + + +def _match_to_depth(version_1, version_2, depth): + major_equal = version_1.major == version_2.major + minor_equal = version_1.minor == version_2.minor + patch_equal = version_1.micro == version_2.micro + if depth == 1: + return major_equal + elif depth == 2: + return major_equal and minor_equal + + return major_equal and minor_equal and patch_equal + + +def unwanted_versions(versions, depth=2): + remove_versions = [] + index = 0 + while index < len(versions): + next_ = index + 1 + v = parse(versions[index]) + v_next = None + if next_ < len(versions): + v_next = parse(versions[next_]) + + if v_next is None: + index += 1 + else: + if _match_to_depth(v, v_next, depth): + remove_versions.append(versions[next_]) + versions.pop(next_) + else: + index += 1 + + return remove_versions + From 1839f8790336d918332f84d2da6a41850918dd87 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 18:53:00 +1200 Subject: [PATCH 088/117] Don't do relative import from non-package in clean_and_reindex_changelogs.py. --- .github/scripts/clean_and_reindex_changelogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/clean_and_reindex_changelogs.py b/.github/scripts/clean_and_reindex_changelogs.py index 4192474cb..f88cf3edb 100644 --- a/.github/scripts/clean_and_reindex_changelogs.py +++ b/.github/scripts/clean_and_reindex_changelogs.py @@ -4,7 +4,7 @@ from packaging.version import Version -from .utilities import unwanted_versions +from utilities import unwanted_versions CHANGELOG_PREFIX = "changelog_v" CHANGELOG_TOC_START_MARKER = " # CHANGELOG_TOC_START_MARKER\n" From f59ccb6d4b12d638aa4878bfa45fecd76f2886df Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 19:03:36 +1200 Subject: [PATCH 089/117] Move generated changelog into changelogs directory so it can be indexed. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 37ef1d43c..dcd7332a1 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -58,6 +58,7 @@ jobs: SOURCE_BRANCH: ${{ inputs.source_branch }} run: | python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} + mv changelog_*.rst docs/changelogs/ python .github/scripts/clean_and_reindex_changelogs.py . - name: Commit changelog @@ -65,7 +66,6 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - mv changelog_*.rst docs/changelogs/ git add docs/changelogs/changelog_*.rst docs/changelogs/index.rst docs/index.rst git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" git push From aa0ce062d08343bde1e98f2da4454701678c4fb8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 19:15:27 +1200 Subject: [PATCH 090/117] Install requests package for generating changelogs. --- .github/workflows/release-changelog.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index dcd7332a1..9927ae0fd 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -47,7 +47,7 @@ jobs: run: | git checkout "$RELEASE_BRANCH" - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 if: ${{ env.ACT != 'true' }} with: python-version: "3.12" @@ -57,6 +57,7 @@ jobs: GH_TOKEN: ${{ github.token }} SOURCE_BRANCH: ${{ inputs.source_branch }} run: | + pip install requests python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} mv changelog_*.rst docs/changelogs/ python .github/scripts/clean_and_reindex_changelogs.py . From a24722dec3a9e6431e04accfc16b9e0b9173eead Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 19:17:09 +1200 Subject: [PATCH 091/117] Also install packaging package for generating changelogs. --- .github/workflows/release-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9927ae0fd..33247e339 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -57,7 +57,7 @@ jobs: GH_TOKEN: ${{ github.token }} SOURCE_BRANCH: ${{ inputs.source_branch }} run: | - pip install requests + pip install requests packaging python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} mv changelog_*.rst docs/changelogs/ python .github/scripts/clean_and_reindex_changelogs.py . From dcad3449ff3c652e910551897e8384e8b9a90576 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Thu, 30 Apr 2026 22:26:48 +1200 Subject: [PATCH 092/117] Separate out release codebase changes from version change in prepare release workflow. --- .github/workflows/release-prepare.yml | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index e9214b075..f5490fb6b 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -28,6 +28,14 @@ jobs: ref: release path: release-repo + + - name: Set GitHub Actions bot identity + run: | + cd release-repo + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + - uses: actions/checkout@v6 with: ref: ${{ inputs.source_branch }} @@ -38,6 +46,17 @@ jobs: cd release-repo git checkout -b ${{ env.BRANCH_PREFIX }}${{ inputs.version }} + - name: Copy release changes to release repo + run: | + rsync -avW --delete --exclude='.git' source-repo/ release-repo/ + + - name: Commit changes + run: | + cd release-repo + + git add -A + git commit -m "Changes to update the codebase to release v${{ inputs.version }}." + - name: Update version numbers run: | cd source-repo @@ -48,18 +67,16 @@ jobs: echo "Core version: $core, Dev suffix: $dev" python3 .github/scripts/set_version.py . $core $dev - - name: Copy changes to release repo + - name: Copy version number changes to release repo run: | rsync -avW --delete --exclude='.git' source-repo/ release-repo/ - - name: Commit version bump + - name: Commit version change run: | cd release-repo - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" git add -A - git commit -m "Changes to update the codebase to release v${{ inputs.version }}." + git commit -m "Set the version number throughout the codebase to ${{ inputs.version }}." - name: Push branch run: | From f2847a5a6e2654dde9c4d90529c8629fd0321f61 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 16:31:06 +1200 Subject: [PATCH 093/117] Work on creating three distinct commits when preparing for a new release. --- .github/scripts/generate_changelog.py | 17 +++++++-- .github/workflows/release-changelog.yml | 26 +++---------- .github/workflows/release-prepare.yml | 45 +++++++++-------------- .github/workflows/set-version.yml | 49 +++++++++++++++++++++++++ docs/release_process.rst | 47 +++++++++++++++++++----- 5 files changed, 124 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/set-version.yml diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py index 9731561ca..fbeb6aa65 100644 --- a/.github/scripts/generate_changelog.py +++ b/.github/scripts/generate_changelog.py @@ -45,14 +45,23 @@ def run(cmd): return subprocess.check_output(cmd, text=True).strip() -def find_previous_source_tag(): +def find_previous_source_tag(end_tag): tags = run(["git", "tag"]).splitlines() valid = [] + m = None if end_tag == "HEAD" else TAG_PATTERN.match(end_tag) + end_version = semver.parse(m.group(1)) if m else None + for t in tags: m = TAG_PATTERN.match(t) - if m: - valid.append((semver.parse(m.group(1)), t)) + if not m: + continue + + v = semver.parse(m.group(1)) + if end_version and v >= semver.parse(end_version): + continue + + valid.append((v, t)) if not valid: raise RuntimeError("No valid source-vX.Y.Z tags found") @@ -169,7 +178,7 @@ def process_arguments(): if __name__ == "__main__": args = process_arguments() - previous_source_tag = find_previous_source_tag() if args.tag_start == "PREV" else args.tag_start + previous_source_tag = find_previous_source_tag(args.tag_end) if args.tag_start == "PREV" else args.tag_start messages = get_merge_commits(previous_source_tag) pr_numbers = extract_pr_numbers(messages) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 33247e339..fdbeee941 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -3,9 +3,8 @@ name: Generate and Commit Changelog on: workflow_dispatch: inputs: - source_branch: - description: "Working branch (usually main)" - default: "main" + changelog_branch: + description: "The target branch for adding the changelog to, a release staging branch" required: true permissions: @@ -22,30 +21,18 @@ jobs: with: fetch-depth: 0 - - name: Determine latest release staging branch + - name: Extract release tag from release staging branch id: determine_release run: | - branch=$(git branch -r \ - | grep 'origin/release-staging-v' \ - | sed 's|[ \t]*origin/||' \ - | sort -V \ - | tail -1) - - if [ -z "$branch" ]; then - echo "::error::No release staging branch found" - exit 1 - fi - - echo "Using release branch: $branch" - echo "RELEASE_BRANCH=$branch" >> "$GITHUB_ENV" + branch=${{ inputs.changelog_branch }} # Extract release tag by removing prefix - tag="${branch#release-staging-v}" + tag="${branch#${{ vars.RELEASE_STAGING_BRANCH_STEM }}}" echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" - name: Checkout release staging branch run: | - git checkout "$RELEASE_BRANCH" + git checkout "${{ inputs.changelog_branch }}" - uses: actions/setup-python@v6 if: ${{ env.ACT != 'true' }} @@ -55,7 +42,6 @@ jobs: - name: Generate changelog env: GH_TOKEN: ${{ github.token }} - SOURCE_BRANCH: ${{ inputs.source_branch }} run: | pip install requests packaging python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index f5490fb6b..531ae08ce 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -19,9 +19,6 @@ jobs: prepare: runs-on: ubuntu-latest - env: - BRANCH_PREFIX: release-staging-v - steps: - uses: actions/checkout@v6 with: @@ -44,7 +41,7 @@ jobs: - name: Create staging branch run: | cd release-repo - git checkout -b ${{ env.BRANCH_PREFIX }}${{ inputs.version }} + git checkout -b ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - name: Copy release changes to release repo run: | @@ -57,31 +54,25 @@ jobs: git add -A git commit -m "Changes to update the codebase to release v${{ inputs.version }}." - - name: Update version numbers - run: | - cd source-repo - - version=${{ inputs.version }} - core=$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$version") - dev="${version#"$core"}" - echo "Core version: $core, Dev suffix: $dev" - python3 .github/scripts/set_version.py . $core $dev - - - name: Copy version number changes to release repo - run: | - rsync -avW --delete --exclude='.git' source-repo/ release-repo/ - - - name: Commit version change + - name: Push branch run: | cd release-repo + git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - git add -A - git commit -m "Set the version number throughout the codebase to ${{ inputs.version }}." + - name: Set version numbers + uses: ./.github/workflows/set-version.yml + with: + target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + version: ${{ inputs.version }} + secrets: + GH_TOKEN: ${{ github.token }} - - name: Push branch - run: | - cd release-repo - git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} + - name: Generate Changelog + uses: ./.github/workflows/release-changelog.yml + with: + changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + secrets: + GH_TOKEN: ${{ github.token }} - name: Open PR id: open_pr @@ -96,7 +87,7 @@ jobs: cd release-repo gh pr create \ --base release \ - --head ${{ env.BRANCH_PREFIX }}${{ inputs.version }} \ + --head ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} \ --title "Proposed changes to create release v${{ inputs.version }} of libCellML" \ --body-file ../pr_body.txt \ --draft @@ -105,4 +96,4 @@ jobs: if: failure() && steps.open_pr.outcome == 'failure' run: | cd release-repo - git push origin --delete ${{ env.BRANCH_PREFIX }}${{ inputs.version }} + git push origin --delete ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} diff --git a/.github/workflows/set-version.yml b/.github/workflows/set-version.yml new file mode 100644 index 000000000..296e73aae --- /dev/null +++ b/.github/workflows/set-version.yml @@ -0,0 +1,49 @@ +name: Set Version + +on: + workflow_dispatch: + inputs: + target_branch: + description: "Target branch (main or release staging branch)" + default: 'main' + required: true + version: + description: "Version to set (e.g. 0.7.1, 0.7.0-rc.1)" + required: true + +permissions: + contents: write + pull-requests: write + +jobs: + set-version: + runs-on: ubuntu-latest + + steps: + + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.target_branch }} + + + - name: Set GitHub Actions bot identity + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Update version numbers + run: | + version=${{ inputs.version }} + core=$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$version") + dev="${version#"$core"}" + echo "Core version: $core, Dev suffix: $dev" + python3 .github/scripts/set_version.py . $core $dev + + - name: Commit version change + run: | + git add -A + git commit -m "Set the version number throughout the codebase to ${{ inputs.version }}." + + - name: Push branch + run: | + git push diff --git a/docs/release_process.rst b/docs/release_process.rst index e914473d6..bf99f7d2b 100644 --- a/docs/release_process.rst +++ b/docs/release_process.rst @@ -5,26 +5,54 @@ Release process for *libCellML* =============================== The target audience of this document are the developers of *libCellML*, who have write authority to the `cellml/libcellml `__ repository. -Releases are made using builders from the Buildbot Continuous Integration (CI). +Releases are made using GitHub Actions Continuous Integration (CI). There are four steps in making a release. -1. `Step 1 - Setting the version number`_ -2. `Step 2 - Preparing the release`_ +1. `Step 1 - Preparing the release`_ +2. `Step 2 - Checking the release`_ 3. `Step 3 - Creating the release`_ 4. `Step 4 - Finalising the Release`_ Each section has further details on what actions are required for a particular step. Each step must be done in order from step 1 through to step 4. -For all the steps in creating a release, you must be logged in to the Buildbot CI and be in the *admin* group. +For all the steps in creating a release, you must be a maintainer of the *cellml/libcellml* GitHub repository. .. note:: Merging in pull requests when a release is under way is not recommended and more importantly has not been tested. - To determine if a release is under way check the repository for the presence of a branch named *version_change* or *release_staging_*. + To determine if a release is under way check the repository for the presence of a branch named *version_change* or *release-staging-v*. -Step 1 - Setting the version number -=================================== +Step 1 - Preparing the release +============================== + +The *Prepare Release* GitHub workflow prepares a release using information submitted to the workflow. +Two workflows are required to prepare a release, the *Prepare Release* workflow and the *Generate and Commit Changelog* workflow. + +The *Prepare Release* workflow must be run first. +This workflow will create a staging branch in the repository, which contains all the changes from the source branch. +The source branch is typically the *main* branch (which is the default) but it can be any branch or tag in the repository. +Next the workflow will update the version number of the codebase to the version number that is set in the workflow interface, and then commit the changes to the staging branch. +The staging branch is named *release_staging_v* where is the version number that is set in the workflow interface. + +This workflow is manually triggered and requires a version number to be set for the release that is being prepared. +This is done using the interface provided through GitHub Actions. +Version numbers should be set following semantic versioning rules, see `Semantic versioning `_ for further information on semantic versioning. +Examples are: + +- 1.0.0 +- 1.0.0-alpha +- 1.0.0-alpha.1 +- 1.0.0-beta +- 1.0.0-beta.2 +- 1.0.0-rc.1 +- 1.0.0-rc.1+build.1 +- 1.0.0-rc.1+post.1 + +The *Generate and Commit Changelog* workflow must be run after the *Prepare Release* workflow. +This workflow will generate a changelog for the release and commit the changes to the staging branch. +It looks for a release staging branch and when it finds one it generates a changelog for the release. +The changelog is generated from the merged pull requests between the current release under preparation and the previous release. The version number for the project can be set using the *Set Version Builder* (:numref:`libcellml_release_process_set_version_builder`). The *Set Version Builder* sets the version that is entered into the interface, it does not increment the version. @@ -40,8 +68,9 @@ The version that you set in the interface will be applied as is to the codebase. libCellML uses semantic versioning as a versioning system, see `Semantic versioning `_ for further information. As such, each part of the version number carries a specific meaning and when setting a version number you need to make sure you are following semantic versioning rules. There are no checks to determine if semantic versioning is being followed. -The version number is split into two parts: the core version, made up of the major, minor, and patch version identifiers; and the developer version. -An official release is created by leaving the developer version input empty. +But the workflow will not run successfully if the version number is not in a format that can be parsed as a semantic version number. +The version number is split into two parts: the core part of the version, made up of the major, minor, and patch version identifiers, and these are whole numbers; and the developer part. +An official release is created by leaving the developer part empty. The main difference between an official release and a developer release is the assets built by the developer release process are not uploaded or published to public registries or attached to an associated GitHub release. .. figure:: ./images/release_process/set_version_builder_interface.png From 9accb4b8a2b4f06d4793d677715899ad380c5894 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 16:48:25 +1200 Subject: [PATCH 094/117] Connect up set version and add changelog workflows. --- .github/workflows/release-prepare.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 9fcd2d4fb..94da0f1ab 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -59,7 +59,22 @@ jobs: - name: Push branch run: | cd release-repo - git push origin ${{ env.BRANCH_PREFIX }}${{ inputs.version }} + git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + + - name: Generate and commit changelog + uses: ./.github/workflows/release-changelog.yml + with: + changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + secrets: + GH_TOKEN: ${{ github.token }} + + - name: Set version + uses: ./.github/workflows/set-version.yml + with: + target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + version: ${{ inputs.version }} + secrets: + GH_TOKEN: ${{ github.token }} - name: Open PR id: open_pr @@ -77,7 +92,7 @@ jobs: --head ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} \ --title "Proposed changes to create release v${{ inputs.version }} of libCellML" \ --body-file ../pr_body.txt \ - --draft + --draft - name: Delete remote branch if PR creation failed if: failure() && steps.open_pr.outcome == 'failure' From 1b0f25349189857c68ae64537a2bc2f31e5cc323 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 16:52:05 +1200 Subject: [PATCH 095/117] Remove unexpected value secrets. --- .github/workflows/release-prepare.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 94da0f1ab..e93100360 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -65,16 +65,12 @@ jobs: uses: ./.github/workflows/release-changelog.yml with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - secrets: - GH_TOKEN: ${{ github.token }} - name: Set version uses: ./.github/workflows/set-version.yml with: target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} version: ${{ inputs.version }} - secrets: - GH_TOKEN: ${{ github.token }} - name: Open PR id: open_pr From 04dc6d346fa2e7542b1f01c12992d92a761719d3 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 16:54:07 +1200 Subject: [PATCH 096/117] Correct paths to workflow files in release-prepare.yml. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index e93100360..fb9e2e734 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -62,12 +62,12 @@ jobs: git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - name: Generate and commit changelog - uses: ./.github/workflows/release-changelog.yml + uses: ./release-repo/.github/workflows/release-changelog.yml with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - name: Set version - uses: ./.github/workflows/set-version.yml + uses: ./release-repo/.github/workflows/set-version.yml with: target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} version: ${{ inputs.version }} From f4c0ff3f70961d468bc27e125f3c7fb77aba90ba Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 16:58:00 +1200 Subject: [PATCH 097/117] Add some debug output. --- .github/workflows/release-prepare.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index fb9e2e734..5ebeda4ba 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -58,6 +58,13 @@ jobs: - name: Push branch run: | + echo "_------------_" + ls -l release-repo + echo "_------------_" + ls -l release-repo/.github + echo "_------------_" + ls -l release-repo/.github/workflows + echo "_------------_" cd release-repo git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} From 6e2cbd2faaaad1f18ec045b0765818050e3a4ce8 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 17:03:44 +1200 Subject: [PATCH 098/117] Use qualified path to reusable workflows. --- .github/workflows/release-prepare.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 5ebeda4ba..037975d69 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -69,12 +69,12 @@ jobs: git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - name: Generate and commit changelog - uses: ./release-repo/.github/workflows/release-changelog.yml + uses: hsorby/libcellml/.github/workflows/release-changelog.yml@main with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - name: Set version - uses: ./release-repo/.github/workflows/set-version.yml + uses: hsorby/libcellml/release-repo/.github/workflows/set-version.yml@main with: target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} version: ${{ inputs.version }} From 7ca9df75aaef05ee2e932f4e0ed910784827de8f Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Mon, 4 May 2026 17:04:02 +1200 Subject: [PATCH 099/117] Remove debug output. --- .github/workflows/release-prepare.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 037975d69..5dd6ed060 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -58,13 +58,6 @@ jobs: - name: Push branch run: | - echo "_------------_" - ls -l release-repo - echo "_------------_" - ls -l release-repo/.github - echo "_------------_" - ls -l release-repo/.github/workflows - echo "_------------_" cd release-repo git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} From a5375186d3ef4e9080bf886a96dc19599d49b991 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 11:23:51 +1200 Subject: [PATCH 100/117] Try splitting out calls to other workflows into separate jobs. --- .github/workflows/release-prepare.yml | 28 ++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 5dd6ed060..4ae4b6913 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -19,9 +19,6 @@ jobs: prepare: runs-on: ubuntu-latest - env: - BRANCH_PREFIX: release-staging-v - steps: - uses: actions/checkout@v6 with: @@ -61,17 +58,23 @@ jobs: cd release-repo git push origin ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - - name: Generate and commit changelog - uses: hsorby/libcellml/.github/workflows/release-changelog.yml@main - with: - changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + generate_changelog: + needs: prepare + uses: ./.github/workflows/release-changelog.yml + with: + changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - - name: Set version - uses: hsorby/libcellml/release-repo/.github/workflows/set-version.yml@main - with: - target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - version: ${{ inputs.version }} + set_version: + needs: generate_changelog + uses: ./.github/workflows/set-version.yml + with: + target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + version: ${{ inputs.version }} + open_pr: + runs-on: ubuntu-latest + needs: set_version + steps: - name: Open PR id: open_pr env: @@ -82,7 +85,6 @@ jobs: echo "**Do not** merge this PR, it is only for review and testing." >> pr_body.txt echo "The release process will deal with it when the release is finalised." >> pr_body.txt - cd release-repo gh pr create \ --base release \ --head ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} \ From be9800b5f51540ed6a90b31e571fe44699d0fe4e Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 11:31:41 +1200 Subject: [PATCH 101/117] Add workflow_call triggers to called workflows. --- .github/workflows/release-changelog.yml | 7 +++++-- .github/workflows/release-prepare.yml | 2 ++ .github/workflows/set-version.yml | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index fdbeee941..9f8b3f22f 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -1,11 +1,14 @@ name: Generate and Commit Changelog on: - workflow_dispatch: + workflow_call: inputs: changelog_branch: description: "The target branch for adding the changelog to, a release staging branch" required: true + secrets: + GH_TOKEN: + required: true permissions: contents: write @@ -41,7 +44,7 @@ jobs: - name: Generate changelog env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} run: | pip install requests packaging python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4ae4b6913..4c87ab1b4 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -63,6 +63,8 @@ jobs: uses: ./.github/workflows/release-changelog.yml with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + secrets: + GH_TOKEN: ${{ github.token }} set_version: needs: generate_changelog diff --git a/.github/workflows/set-version.yml b/.github/workflows/set-version.yml index 296e73aae..f22feb56c 100644 --- a/.github/workflows/set-version.yml +++ b/.github/workflows/set-version.yml @@ -11,6 +11,16 @@ on: description: "Version to set (e.g. 0.7.1, 0.7.0-rc.1)" required: true + workflow_call: + inputs: + target_branch: + description: "Target branch (main or release staging branch)" + default: 'main' + required: true + version: + description: "Version to set (e.g. 0.7.1, 0.7.0-rc.1)" + required: true + permissions: contents: write pull-requests: write From 1bc4d59586a46ef3fc491aa6b47034d244e57cde Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 11:33:53 +1200 Subject: [PATCH 102/117] Add type to workflow call inputs. --- .github/workflows/release-changelog.yml | 2 ++ .github/workflows/set-version.yml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 9f8b3f22f..69f413786 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -6,9 +6,11 @@ on: changelog_branch: description: "The target branch for adding the changelog to, a release staging branch" required: true + type: string secrets: GH_TOKEN: required: true + type: string permissions: contents: write diff --git a/.github/workflows/set-version.yml b/.github/workflows/set-version.yml index f22feb56c..462956f7c 100644 --- a/.github/workflows/set-version.yml +++ b/.github/workflows/set-version.yml @@ -15,10 +15,11 @@ on: inputs: target_branch: description: "Target branch (main or release staging branch)" - default: 'main' + type: string required: true version: description: "Version to set (e.g. 0.7.1, 0.7.0-rc.1)" + type: string required: true permissions: From d55ccc011856e42b644ad3d016736ed6cb017727 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 11:38:30 +1200 Subject: [PATCH 103/117] Try and fix errors in release-changelog workflow. --- .github/workflows/release-changelog.yml | 6 ------ .github/workflows/release-prepare.yml | 2 -- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 69f413786..151b66bca 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -7,10 +7,6 @@ on: description: "The target branch for adding the changelog to, a release staging branch" required: true type: string - secrets: - GH_TOKEN: - required: true - type: string permissions: contents: write @@ -45,8 +41,6 @@ jobs: python-version: "3.12" - name: Generate changelog - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} run: | pip install requests packaging python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4c87ab1b4..4ae4b6913 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -63,8 +63,6 @@ jobs: uses: ./.github/workflows/release-changelog.yml with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} - secrets: - GH_TOKEN: ${{ github.token }} set_version: needs: generate_changelog From 0739187097ac019e8a0c5b44f039e458860d7d0b Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 11:41:15 +1200 Subject: [PATCH 104/117] Add github token back in through secrets. --- .github/workflows/release-changelog.yml | 4 ++++ .github/workflows/release-prepare.yml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 151b66bca..2a675e2ce 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -7,6 +7,10 @@ on: description: "The target branch for adding the changelog to, a release staging branch" required: true type: string + secrets: + GH_TOKEN: + description: "GitHub token with permissions to push to the repository" + required: true permissions: contents: write diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4ae4b6913..4c87ab1b4 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -63,6 +63,8 @@ jobs: uses: ./.github/workflows/release-changelog.yml with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + secrets: + GH_TOKEN: ${{ github.token }} set_version: needs: generate_changelog From a969c3b92a276dc3cc778b5b79fd0f20d83bc4ee Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 11:43:38 +1200 Subject: [PATCH 105/117] Add github tokenn secret into calling environment. --- .github/workflows/release-changelog.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 2a675e2ce..a32870157 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -45,6 +45,8 @@ jobs: python-version: "3.12" - name: Generate changelog + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} run: | pip install requests packaging python .github/scripts/generate_changelog.py -p cellml/libcellml PREV ${{ env.RELEASE_TAG }} From 6ceca2842c22289c81312b8d5d4abbdf3a46179c Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 14:40:06 +1200 Subject: [PATCH 106/117] Check GitHub token in release-changelog. --- .github/workflows/release-changelog.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index a32870157..d0c4a7893 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -35,6 +35,9 @@ jobs: tag="${branch#${{ vars.RELEASE_STAGING_BRANCH_STEM }}}" echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" + echo "GH_TOKEN set? ${GH_TOKEN:+yes}" + curl -s -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/rate_limit + - name: Checkout release staging branch run: | git checkout "${{ inputs.changelog_branch }}" From d180eaa5d9119fea75d7633136460079c8871081 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 14:44:11 +1200 Subject: [PATCH 107/117] Delete release branch if the workflow failed. --- .github/workflows/release-prepare.yml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4c87ab1b4..b0145f2f1 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -78,7 +78,6 @@ jobs: needs: set_version steps: - name: Open PR - id: open_pr env: GH_TOKEN: ${{ github.token }} run: | @@ -94,8 +93,24 @@ jobs: --body-file ../pr_body.txt \ --draft - - name: Delete remote branch if PR creation failed - if: failure() && steps.open_pr.outcome == 'failure' + delete_release_branch: + runs-on: ubuntu-latest + needs: + - prepare + - generate_changelog + - set_version + - open_pr + if: failure() + + steps: + - name: Delete remote release staging branch + env: + GH_TOKEN: ${{ github.token }} run: | - cd release-repo - git push origin --delete ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} + branch="${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }}" + echo "Cleaning up failed release branch: $branch" + + git clone https://github.com/${{ github.repository }}.git repo + cd repo + + git push origin --delete "$branch" || echo "Branch already deleted" From 99a87f4b921082a8bffd0b443d2a25dfef33efd6 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 14:57:07 +1200 Subject: [PATCH 108/117] Use secrets.GITHUB_TOKEN to pass through to sub workflows. --- .github/workflows/release-prepare.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index b0145f2f1..f38212adb 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -64,7 +64,7 @@ jobs: with: changelog_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} secrets: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} set_version: needs: generate_changelog @@ -72,6 +72,8 @@ jobs: with: target_branch: ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} version: ${{ inputs.version }} + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} open_pr: runs-on: ubuntu-latest @@ -79,7 +81,7 @@ jobs: steps: - name: Open PR env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | echo "Automated release PR for v${{ inputs.version }}." > pr_body.txt echo "" >> pr_body.txt @@ -113,4 +115,7 @@ jobs: git clone https://github.com/${{ github.repository }}.git repo cd repo + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git push origin --delete "$branch" || echo "Branch already deleted" From 7a44a9eeea6934df0ab796f1cab764cf248c486d Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 14:58:21 +1200 Subject: [PATCH 109/117] Use secrets.GITHUB_TOKEN to pass through to sub workflows. --- .github/workflows/release-prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index f38212adb..4a9e3a6fe 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -107,7 +107,7 @@ jobs: steps: - name: Delete remote release staging branch env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | branch="${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }}" echo "Cleaning up failed release branch: $branch" From 87e676cbc15b254dbefa01dfb0cc25bd4897fc4c Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 14:59:56 +1200 Subject: [PATCH 110/117] Make GH_TOKEN an input for workflow call in set-version workflow. --- .github/workflows/set-version.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/set-version.yml b/.github/workflows/set-version.yml index 462956f7c..4ba90fc44 100644 --- a/.github/workflows/set-version.yml +++ b/.github/workflows/set-version.yml @@ -21,6 +21,10 @@ on: description: "Version to set (e.g. 0.7.1, 0.7.0-rc.1)" type: string required: true + secrets: + GH_TOKEN: + description: "GitHub token with permissions to push to the repository" + required: true permissions: contents: write From 9330e60f391f6de4cd68e83005658b50ff1e3854 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 15:02:17 +1200 Subject: [PATCH 111/117] Correct indentation of secrets in workflow_call for set-version workflow. --- .github/workflows/set-version.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/set-version.yml b/.github/workflows/set-version.yml index 4ba90fc44..77da6ee62 100644 --- a/.github/workflows/set-version.yml +++ b/.github/workflows/set-version.yml @@ -21,10 +21,10 @@ on: description: "Version to set (e.g. 0.7.1, 0.7.0-rc.1)" type: string required: true - secrets: - GH_TOKEN: - description: "GitHub token with permissions to push to the repository" - required: true + secrets: + GH_TOKEN: + description: "GitHub token with permissions to push to the repository" + required: true permissions: contents: write @@ -43,6 +43,8 @@ jobs: - name: Set GitHub Actions bot identity run: | + echo "GH_TOKEN set? ${GH_TOKEN:+yes}" + git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" From fca8698657bfc130c58a0ed6fe6f44ab78ae1871 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 15:08:12 +1200 Subject: [PATCH 112/117] Fix reference to PR body text in release-prepare. --- .github/workflows/release-prepare.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 4a9e3a6fe..02a1e55ca 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -92,7 +92,7 @@ jobs: --base release \ --head ${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }} \ --title "Proposed changes to create release v${{ inputs.version }} of libCellML" \ - --body-file ../pr_body.txt \ + --body-file pr_body.txt \ --draft delete_release_branch: @@ -105,6 +105,8 @@ jobs: if: failure() steps: + - uses: actions/checkout@v6 + - name: Delete remote release staging branch env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -112,9 +114,6 @@ jobs: branch="${{ vars.RELEASE_STAGING_BRANCH_STEM }}${{ inputs.version }}" echo "Cleaning up failed release branch: $branch" - git clone https://github.com/${{ github.repository }}.git repo - cd repo - git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" From 1ada891ff8e3ead76cce27ab3fee8b4a07eef236 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 15:13:44 +1200 Subject: [PATCH 113/117] Set up repository for gh command in release-prepare workflow. --- .github/workflows/release-prepare.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 02a1e55ca..17021400a 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -79,6 +79,8 @@ jobs: runs-on: ubuntu-latest needs: set_version steps: + - uses: actions/checkout@v6 + - name: Open PR env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 7c96f4e81d916efbb35ec465a769a76795c75785 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 15:40:10 +1200 Subject: [PATCH 114/117] Tidy release process workflows a little. --- .github/workflows/release-changelog.yml | 5 +---- .github/workflows/release-prepare.yml | 2 +- .github/workflows/set-version.yml | 2 -- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index d0c4a7893..ab8216585 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -35,9 +35,6 @@ jobs: tag="${branch#${{ vars.RELEASE_STAGING_BRANCH_STEM }}}" echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" - echo "GH_TOKEN set? ${GH_TOKEN:+yes}" - curl -s -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/rate_limit - - name: Checkout release staging branch run: | git checkout "${{ inputs.changelog_branch }}" @@ -62,5 +59,5 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git add docs/changelogs/changelog_*.rst docs/changelogs/index.rst docs/index.rst - git commit -m "Add changelog for v${RELEASE_TAG}" || echo "No changes to commit" + git commit -m "Add changelog for release ${RELEASE_TAG}" || echo "No changes to commit" git push diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index 17021400a..5a7a14864 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -51,7 +51,7 @@ jobs: cd release-repo git add -A - git commit -m "Changes to update the codebase to release v${{ inputs.version }}." + git commit -m "Changes to update the release branch to ${{ inputs.version }}." - name: Push branch run: | diff --git a/.github/workflows/set-version.yml b/.github/workflows/set-version.yml index 77da6ee62..9cb7798e5 100644 --- a/.github/workflows/set-version.yml +++ b/.github/workflows/set-version.yml @@ -43,8 +43,6 @@ jobs: - name: Set GitHub Actions bot identity run: | - echo "GH_TOKEN set? ${GH_TOKEN:+yes}" - git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" From c2b71a7d34c1060464c26073307ed502f15dcff9 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 15:59:38 +1200 Subject: [PATCH 115/117] Update release process documentation. --- .../prepare_release_interface.png | Bin 0 -> 21405 bytes docs/release_process.rst | 39 +++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) create mode 100644 docs/images/release_process/prepare_release_interface.png diff --git a/docs/images/release_process/prepare_release_interface.png b/docs/images/release_process/prepare_release_interface.png new file mode 100644 index 0000000000000000000000000000000000000000..25b1f30a95a1785bdc2f6a407867eefdf14b75ef GIT binary patch literal 21405 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 YFhMe;T|ky1Lepn7(l^no)^UvZ7w;xoDgXcg literal 0 HcmV?d00001 diff --git a/docs/release_process.rst b/docs/release_process.rst index bf99f7d2b..f0387c0d1 100644 --- a/docs/release_process.rst +++ b/docs/release_process.rst @@ -27,16 +27,27 @@ Step 1 - Preparing the release ============================== The *Prepare Release* GitHub workflow prepares a release using information submitted to the workflow. -Two workflows are required to prepare a release, the *Prepare Release* workflow and the *Generate and Commit Changelog* workflow. -The *Prepare Release* workflow must be run first. +The *Prepare Release* workflow will collect all the changes that the release is made up of, add a changelog of merged pull requests, and update the version number throughout the codebase. +These changes are separated into separate commits to make reviewing the changes easier. + This workflow will create a staging branch in the repository, which contains all the changes from the source branch. The source branch is typically the *main* branch (which is the default) but it can be any branch or tag in the repository. -Next the workflow will update the version number of the codebase to the version number that is set in the workflow interface, and then commit the changes to the staging branch. The staging branch is named *release_staging_v* where is the version number that is set in the workflow interface. +The workflow can be triggered from the *Actions* tab of the `cellml/libcellml `__ repository. + This workflow is manually triggered and requires a version number to be set for the release that is being prepared. This is done using the interface provided through GitHub Actions. + +.. figure:: ./images/release_process/prepare_release_interface.png + :align: center + :alt: GitHub actions prepare release workflow interface. + :name: libcellml_release_process_prepare_release_interface + + *Prepare Release* interface on GitHub. + + Version numbers should be set following semantic versioning rules, see `Semantic versioning `_ for further information on semantic versioning. Examples are: @@ -49,24 +60,6 @@ Examples are: - 1.0.0-rc.1+build.1 - 1.0.0-rc.1+post.1 -The *Generate and Commit Changelog* workflow must be run after the *Prepare Release* workflow. -This workflow will generate a changelog for the release and commit the changes to the staging branch. -It looks for a release staging branch and when it finds one it generates a changelog for the release. -The changelog is generated from the merged pull requests between the current release under preparation and the previous release. - -The version number for the project can be set using the *Set Version Builder* (:numref:`libcellml_release_process_set_version_builder`). -The *Set Version Builder* sets the version that is entered into the interface, it does not increment the version. -The version that you set in the interface will be applied as is to the codebase. - -.. figure:: ./images/release_process/set_version_builder.png - :align: center - :alt: Buildbot set version builder. - :name: libcellml_release_process_set_version_builder - - *Set Version Builder* on Buildbot. - -libCellML uses semantic versioning as a versioning system, see `Semantic versioning `_ for further information. -As such, each part of the version number carries a specific meaning and when setting a version number you need to make sure you are following semantic versioning rules. There are no checks to determine if semantic versioning is being followed. But the workflow will not run successfully if the version number is not in a format that can be parsed as a semantic version number. The version number is split into two parts: the core part of the version, made up of the major, minor, and patch version identifiers, and these are whole numbers; and the developer part. @@ -94,8 +87,8 @@ When merging the pull request the *version_change* branch will be automatically When the version number has been set in the *main* branch the preparation of the release can start. -Step 2 - Preparing the release -============================== +Step 2 - Checking the release +============================= A release is prepared using the *Prepare Release Builder* (:numref:`libcellml_release_process_prepare_release`). The *Prepare Release Builder* will create a new branch named *release_staging_* (where is an actual semantic version number set in the first step) and generate a changelog. From 9d55bc0313a0cede14cdfb0f91c5153bdb1d870f Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 16:05:21 +1200 Subject: [PATCH 116/117] Update release process documentation for clarity and completeness. --- docs/release_process.rst | 90 +++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/docs/release_process.rst b/docs/release_process.rst index f0387c0d1..9fd92455b 100644 --- a/docs/release_process.rst +++ b/docs/release_process.rst @@ -4,52 +4,71 @@ Release process for *libCellML* =============================== -The target audience of this document are the developers of *libCellML*, who have write authority to the `cellml/libcellml `__ repository. -Releases are made using GitHub Actions Continuous Integration (CI). -There are four steps in making a release. +The purpose of this document is to describe the process for preparing, reviewing, creating, and finalising releases of *libCellML* using GitHub Actions Continuous Integration (CI). + +The target audience of this document are the developers of *libCellML* who have write authority to the `cellml/libcellml `_ repository. + +Releases are made using GitHub Actions CI and consist of four steps: 1. `Step 1 - Preparing the release`_ 2. `Step 2 - Checking the release`_ 3. `Step 3 - Creating the release`_ 4. `Step 4 - Finalising the Release`_ -Each section has further details on what actions are required for a particular step. -Each step must be done in order from step 1 through to step 4. +Each step must be performed in order. Later steps assume that all previous steps have completed successfully. + +Prerequisites +============= + +For all steps in the release process, you must: -For all the steps in creating a release, you must be a maintainer of the *cellml/libcellml* GitHub repository. +- Be a maintainer (have write access) of the *cellml/libcellml* GitHub repository. +- Be familiar with semantic versioning. +- Ensure that no other release is currently in progress. .. note:: - Merging in pull requests when a release is under way is not recommended and more importantly has not been tested. - To determine if a release is under way check the repository for the presence of a branch named *version_change* or *release-staging-v*. + Merging pull requests while a release is under way is not recommended and has not been tested. + Before starting a new release, check the repository for the presence of a branch named *release-staging-v*. + The presence of such a branch indicates that a release is already in progress. + +Branch model overview +===================== + +The *main* branch is the primary development branch where normal feature development occurs. +The *release* branch represents the base for all released versions of *libCellML*. +Release staging branches, named *release-staging-v*, are temporary branches used to assemble, review, and validate a candidate release before it is finalised. Step 1 - Preparing the release ============================== -The *Prepare Release* GitHub workflow prepares a release using information submitted to the workflow. +The *Prepare Release* GitHub Actions workflow prepares a release candidate using information supplied when the workflow is triggered. + +This workflow performs the following tasks: -The *Prepare Release* workflow will collect all the changes that the release is made up of, add a changelog of merged pull requests, and update the version number throughout the codebase. -These changes are separated into separate commits to make reviewing the changes easier. +- Collects all changes that will make up the release from a specified source branch. +- Generates a changelog summarising the merged pull requests included in the release. +- Updates version numbers throughout the codebase. -This workflow will create a staging branch in the repository, which contains all the changes from the source branch. -The source branch is typically the *main* branch (which is the default) but it can be any branch or tag in the repository. -The staging branch is named *release_staging_v* where is the version number that is set in the workflow interface. +These changes are separated into multiple commits (for example, content changes, changelog generation, and version updates) to make reviewing the release easier. + +The workflow creates a staging branch in the repository that contains all proposed release changes. +The source branch is typically the *main* branch (which is the default), but it may be any branch or tag in the repository. +The staging branch is named *release-staging-v*, where is the version number specified when triggering the workflow. The workflow can be triggered from the *Actions* tab of the `cellml/libcellml `__ repository. -This workflow is manually triggered and requires a version number to be set for the release that is being prepared. -This is done using the interface provided through GitHub Actions. +This workflow is manually triggered and requires a version number to be provided using the GitHub Actions user interface. .. figure:: ./images/release_process/prepare_release_interface.png :align: center - :alt: GitHub actions prepare release workflow interface. + :alt: GitHub Actions prepare release workflow interface. :name: libcellml_release_process_prepare_release_interface *Prepare Release* interface on GitHub. - -Version numbers should be set following semantic versioning rules, see `Semantic versioning `_ for further information on semantic versioning. -Examples are: +Version numbers must follow semantic versioning rules. See `Semantic Versioning `_ for further information. +Examples of valid version numbers include: - 1.0.0 - 1.0.0-alpha @@ -58,34 +77,19 @@ Examples are: - 1.0.0-beta.2 - 1.0.0-rc.1 - 1.0.0-rc.1+build.1 -- 1.0.0-rc.1+post.1 - -There are no checks to determine if semantic versioning is being followed. -But the workflow will not run successfully if the version number is not in a format that can be parsed as a semantic version number. -The version number is split into two parts: the core part of the version, made up of the major, minor, and patch version identifiers, and these are whole numbers; and the developer part. -An official release is created by leaving the developer part empty. -The main difference between an official release and a developer release is the assets built by the developer release process are not uploaded or published to public registries or attached to an associated GitHub release. +- 1.0.0-post.1 -.. figure:: ./images/release_process/set_version_builder_interface.png - :align: center - :alt: Buildbot set version builder interface. - :name: libcellml_release_process_set_version_builder_interface - - *Set Version Builder* interface. +There are no explicit checks to ensure that version numbers strictly follow semantic versioning conventions; however, the workflow will fail if the supplied version number cannot be parsed as a semantic version. -When the *Start Build* button is pressed (:numref:`libcellml_release_process_set_version_builder_interface`) Buildbot will create an internal pull request on the `cellml/libcellml `__ GitHub repository. -The pull request will be made from the *version_change* branch to the *main* branch. -The creation of the pull request will trigger a CI build, wait for the CI to finish its checks before merging the pull request. -If, for some reason, the CI checks fail changes may be required. -Changes can be made directly to the *vesion_change* branch but quite likely any such changes will need to be propagated to the CI for a permanent fix. -How changes are propagated to the CI is outside the scope of this document. -When merging the pull request the *version_change* branch will be automatically deleted. +The version number is split into two components: a core version (major, minor, and patch numbers) and an optional developer suffix. +An official release is created by omitting the developer suffix. +Developer releases include a suffix and differ from official releases in that the resulting assets are not published to public registries or attached to a GitHub release. -.. note:: +On successful completion, this workflow creates a pull request on the `cellml/libcellml `_ repository with *release* as the base branch and the newly created staging branch as the head branch. - The merging of a *version_change* pull request created by the CI system is exempt from the 'two reviews' required rule. +If the workflow fails, the pull request will not be created. Depending on how far the workflow progressed before failure, the staging branch may still exist and may need to be cleaned up manually or reused after resolving the issue. -When the version number has been set in the *main* branch the preparation of the release can start. +Once the pull request has been created, the checking of the release can begin (see `Step 2 - Checking the release`_). Step 2 - Checking the release ============================= From 581a695023f6e39ffce3152d506c0452f4630b6d Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Tue, 5 May 2026 17:14:28 +1200 Subject: [PATCH 117/117] Fix numeric version regex. --- .github/scripts/set_version.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/set_version.sh b/.github/scripts/set_version.sh index 886dedf27..ea7b75459 100755 --- a/.github/scripts/set_version.sh +++ b/.github/scripts/set_version.sh @@ -8,7 +8,7 @@ numeric_version=$(printf %02d "${version_array[@]}") version_regex='[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*' sed -i "s@ EXPECT_EQ(\"${version_regex}\", versionString);@ EXPECT_EQ(\"${version}\", versionString);@" tests/version/version.cpp -sed -i 's@ EXPECT_EQ(0x[[:digit:]]+U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp +sed -i 's@ EXPECT_EQ(0x[[:digit:]]*U, version);@ EXPECT_EQ(0x'${numeric_version}'U, version);@' tests/version/version.cpp sed -i 's/^set(_PROJECT_VERSION[^)]*)$/set(_PROJECT_VERSION '${version}')/' CMakeLists.txt sed -i 's/^set(PROJECT_DEVELOPER_VERSION[^)]*)$/set(PROJECT_DEVELOPER_VERSION '${developer_version}')/' CMakeLists.txt @@ -19,13 +19,13 @@ sed -i "s@LIBCELLML_VERSION = \"${version_regex}\"@LIBCELLML_VERSION = \"${versi files=$(grep -rl "/\* The content of this file was generated using \(the\|a modified\) C profile of libCellML ${version_regex}\. \*/" *) sed -i -E "s@/\* The content of this file was generated using (the|a modified) C profile of libCellML ${version_regex}\. \*/@/\* The content of this file was generated using \1 C profile of libCellML ${version}. \*/@" $files -files=$(grep -rl --exclude=simple-sed.sh "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML ${version_regex}\." *) +files=$(grep -rl "# The content of this file was generated using \(the\|a modified\) Python profile of libCellML ${version_regex}\." *) sed -i -E "s@# The content of this file was generated using (the|a modified) Python profile of libCellML ${version_regex}\.@# The content of this file was generated using \1 Python profile of libCellML ${version}.@" $files -files=$(grep -rl --exclude=simple-sed.sh "const char LIBCELLML_VERSION\[\] = \"${version_regex}\";" *) +files=$(grep -rl "const char LIBCELLML_VERSION\[\] = \"${version_regex}\";" *) sed -i "s@const char LIBCELLML_VERSION\[\] = \"${version_regex}\";@const char LIBCELLML_VERSION\[\] = \"${version}\";@" $files -files=$(grep -rl --exclude=simple-sed.sh " expect(libcellml\.versionString()).toBe('${version_regex}');" *) +files=$(grep -rl " expect(libcellml\.versionString()).toBe('${version_regex}');" *) sed -i "s@ expect(libcellml\.versionString()).toBe('${version_regex}');@ expect(libcellml\.versionString()).toBe('${version}');@" $files exit 0