Skip to content

Commit

Permalink
Merge pull request #840 from mrapp-ke/manage-release-branches
Browse files Browse the repository at this point in the history
Manage release branches via CI
  • Loading branch information
mrapp-ke authored Apr 23, 2024
2 parents 1fcc2ea + 2138d1e commit e9ac13a
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 28 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/merge_bugfix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Merge bugfix into feature branch
on:
push:
branches:
- 'bugfix'
jobs:
merge_bugfix:
name: Merge bugfix into feature branch
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[Bot]')"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: feature
- name: Reset bugfix branch
run: |
export FEATURE_VERSION="$(cat VERSION)"
git fetch origin bugfix:bugfix
git reset --hard bugfix
echo $FEATURE_VERSION > VERSION
- name: Generate token
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.TOKEN_APP_ID }}
private-key: ${{ secrets.TOKEN_APP_SECRET }}
- name: Submit pull request
id: pull-request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ steps.app-token.outputs.token }}
commit-message: "[Bot] Merge bugfix into feature branch."
branch: merge-bugfix
title: "Merge bugfix into feature branch"
labels: bot
body: "Merge branch \"bugfix\" into \"feature\"."
- name: Enable auto-merge
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --merge --auto "${{ steps.pull-request.outputs.pull-request-number }}"
41 changes: 41 additions & 0 deletions .github/workflows/merge_feature.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Merge feature into main branch
on:
push:
branches:
- 'feature'
jobs:
merge_feature:
name: Merge feature into main branch
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[Bot]')"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
- name: Reset feature branch
run: |
export MAIN_VERSION="$(cat VERSION)"
git fetch origin feature:feature
git reset --hard feature
echo $MAIN_VERSION > VERSION
- name: Generate token
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.TOKEN_APP_ID }}
private-key: ${{ secrets.TOKEN_APP_SECRET }}
- name: Submit pull request
id: pull-request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ steps.app-token.outputs.token }}
commit-message: "[Bot] Merge feature into main branch."
branch: merge-feature
title: "Merge feature into main branch"
labels: bot
body: "Merge branch \"feature\" into \"main\"."
- name: Enable auto-merge
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --merge --auto "${{ steps.pull-request.outputs.pull-request-number }}"
52 changes: 47 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,61 @@ on:
release:
types: [ published ]
jobs:
reset_development_version:
name: Reset development version
merge_release:
name: Merge into upstream branches
runs-on: ubuntu-latest
if: ${{ github.event.release.target_commitish == 'main' }}
steps:
- name: Generate token
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.TOKEN_APP_ID }}
private-key: ${{ secrets.TOKEN_APP_SECRET }}
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Setup git
uses: fregante/setup-git-user@v2
- name: Reset development version
- name: Checkout release branch
run: |
git checkout ${{ github.event.release.target_commitish }}
- name: Update major version
if: ${{ github.event.release.target_commitish == 'main' }}
run: |
./build reset_development_version
git add VERSION.dev && git commit -m "[Bot] Update development version to $(cat VERSION.dev)." && git push
git add VERSION.dev && git commit -m "[Bot] Update development version to $(cat VERSION.dev)." && git push origin main
./build increment_major_version
git add VERSION
git commit -m "[Bot] Update version to $(cat VERSION)."
git push origin main
- name: Merge into feature branch
if: ${{ github.event.release.target_commitish == 'main' }}
run: |
git checkout -b feature origin/feature
git merge origin/main --strategy-option theirs -m "[Bot] Merge branch \"main\" into \"feature\"."
./build increment_minor_version
git add VERSION
git commit -m "[Bot] Update version to $(cat VERSION)."
git push origin feature
- name: Merge into bugfix branch
if: ${{ github.event.release.target_commitish == 'main' }} || ${{ github.event.release.target_commitish == 'feature' }}
run: |
git checkout -b bugfix origin/bugfix
git merge origin/feature --strategy-option theirs -m "[Bot] Merge branch \"main\" into \"bugfix\"."
./build increment_patch_version
git add VERSION
git commit -m "[Bot] Update version to $(cat VERSION)."
git push origin bugfix
- name: Update patch version
if: ${{ github.event.release.target_commitish == 'bugfix' }}
run: |
git checkout -b bugfix origin/bugfix
./build increment_patch_version
git add VERSION
git commit -m "[Bot] Update version to $(cat VERSION)."
git push origin bugfix
publish_packages:
name: Publish wheel packages
uses: ./.github/workflows/template_publish.yml
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release_development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
jobs:
update_development_version:
name: Update development version
if: "!contains(github.event.head_commit.message, '[Bot]')"
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_file_changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
uses: xalvarez/prevent-file-change-action@v1
with:
githubToken: ${{ steps.app-token.outputs.token }}
pattern: ^VERSION.dev$
pattern: ^VERSION(.dev)?$
allowNewFiles: true
- name: Save Git repository to cache
uses: actions/cache/save@v4
Expand Down
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ This release comes with several API changes. For an updated overview of the avai
- **The value to be used for sparse elements of a feature matrix** can now be specified via the C++ or Python API.
- **Nominal and ordinal feature values are now represented as integers** to avoid issues due to limited floating point precision.
- **Safe comparisons of floating point values** are now used to avoid issues due to limited floating point precision.
- **Fundamental data structures for vectors and matrices have been reworked** to ease reusing existing functionality and avoiding redundant code.

### Additions to the Command Line API

- **Information about the program can now be printed** via the argument `-v` or `--version`.
- **Information abused to implementout the program can now be printed** via the argument `-v` or `--version`.
- **Data characteristics do now include the number of ordinal attributes** when printed on the console or written to a file via the command line argument `--print-data-characteristics` or `--store-data-characteristics`.

### Bugfixes
Expand All @@ -45,11 +46,10 @@ This release comes with several API changes. For an updated overview of the avai
- Added support for unit testing the project's C++ code. Compilation of the tests can be disabled via a build option.
- The Python code is now checked for common issues by applying `pylint` via continuous integration.
- The Makefile has been replaced with wrapper scripts triggering a [SCons](https://scons.org/) build.
- Development versions of wheel packages are now frequently built via continuous integration, uploaded as artifacts, and published on [Test-PyPI](https://test.pypi.org/).
- Development versions of wheel packages are now regularly built via continuous integration, uploaded as artifacts, and published on [Test-PyPI](https://test.pypi.org/).
- Continuous integration is now used to maintain separate branches for major, feature, and bugfix releases and keep them up-to-date.
- The runtime of continuous integration jobs has been optimized by running individual steps only if necessary, caching files across subsequent runs, and making use of parallelization.
- When built via continuous integration, libraries and the documentation are now uploaded as artifacts.
- When tests are run via continuous integration, a summary of the test results is now added to merge requests and Github workflows.
- The fundamental data structures used to implement vectors and matrices have been reworked to ease reusing existing functionality and avoiding redundant code.
- Markdown files are now used for writing the documentation.
- A consistent style is now enforced for Markdown files by applying the tool `mdformat` via continuous integration.
- C++ 17 or newer is now required for compiling the project.
Expand Down
17 changes: 16 additions & 1 deletion doc/developer_guide/coding_standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ A track record of past runs can be found on Github in the [Actions](https://gith

The workflow definitions of individual CI jobs can be found in the directory [.github/workflows/](https://github.com/mrapp-ke/MLRL-Boomer/tree/8ed4f36af5e449c5960a4676bc0a6a22de195979/.github/workflows). Currently, the following jobs are used in the project:

- `release.yml` is used for publishing pre-built packages on [PyPI](https://pypi.org/) (see {ref}`installation`). For this purpose, the project is built from source for each of the target platforms and architectures, using virtualization in some cases. The job is run automatically when a new release was published on [Github](https://github.com/mrapp-ke/MLRL-Boomer/releases).
- `release.yml` is used for publishing pre-built packages on [PyPI](https://pypi.org/) (see {ref}`installation`). For this purpose, the project is built from source for each of the target platforms and architectures, using virtualization in some cases. The job is run automatically when a new release was published on [Github](https://github.com/mrapp-ke/MLRL-Boomer/releases). It does also increment the project's major version number and merge the release branch into its upstream branches (see {ref}`release-process`).
- `release_development.yml` publishes development versions of packages on [Test-PyPI](https://test.pypi.org/) whenever changes to the project's source code have been pushed to the main branch. The packages built by each of these runs are also saved as [artifacts](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts) and can be downloaded as zip archives.
- `test_release.yml` ensures that the packages to be released for different architectures and Python versions can be built. The job is run for pull requests that modify relevant parts of the source code.
- `test_build.yml` builds the project for each of the supported target platforms, i.e., Linux, Windows, and MacOS (see {ref}`compilation`). In the Linux environment, this job does also execute all available unit and integration tests (see {ref}`testing`). It is run for pull requests whenever relevant parts of the project's source code have been modified.
- `test_doc.yml` generates the latest documentation (see {ref}`documentation`) whenever relevant parts of the source code are affected by a pull request.
- `test_format.yml` ensures that all source files in the project adhere to our coding style guidelines (see {ref}`code-style`). This job is run automatically for pull requests whenever they include any changes affecting the relevant source files.
- `test_file_changes.yml` prevents pull requests from modifying certain files that must not be modified manually, but are intended to only be updated via Github Actions.
- `merge_feature.yml` and `merge_bugfix.yml` are used to merge changes that have been pushed to the feature or bugfix branch into downstream branches via pull requests (see {ref}`release-process`).

(testing)=

Expand Down Expand Up @@ -142,6 +143,20 @@ Feature releases with the major version `0` are not obliged to maintain API comp

Increments of the major version indicate big leaps in the software's development. They are reserved for new versions of the software that introduce new functionality, fundamentally change how the software works, or come with compatibility-breaking changes. In general, major releases are not guaranteed to be compatible with past releases in any way. In particular, they may introduce compatibility-breaking API changes, affecting the command line API or programmatic APIs in the project's Python or C++ code. Moreover, models that have been trained using an older version are not guaranteed to work after updating to a new major release and must potentially be trained from scratch.

(release-process)=

## Release Process

To enable releasing new major, feature, or bugfix releases at any time, we maintain a branch for each type of release:

- `main` contains all changes that will be included in the next major release (including changes on the feature and bugfix branch).
- `feature` comes with the changes that will be part of an upcoming feature release (including changes on the bugfix branch).
- `bugfix` is restricted to minor changes that will be published as a bugfix release.

We do not allow directly pushing to the above branches. Instead, all changes must be submitted via pull requests and require certain checks to pass. Once modifications to one of the branches have been merged, {ref}`ci` jobs are used to automatically update downstream branches via pull requests. If all checks run for such pull requests are successful, they are merged automatically. If there are any merge conflicts, they must be resolved manually. Following this procedure, changes to the feature brach are merged into the main branch (see `merge_feature.yml`). Changes to the bugfix branch are first merged into the feature branch and then into the main branch (see `merge_bugfix.yml`).

Whenever a new release has been published, the release branch is merged into the upstream branches (see `release.yml`), i.e., major releases result in the feature and bugfix branches being updated, whereas minor releases result in the bugfix branch to be updated. The version of the release branch and the affected branches are updated accordingly. The version of a branch is specified in the file `VERSION` in the project's root directory. Similarly, the file `VERSION.dev` is used to keep track of the version number used for development releases (see `release_development.yml`).

(dependencies)=

## Dependencies
Expand Down
32 changes: 18 additions & 14 deletions scons/sconstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from modules import BUILD_MODULE, CPP_MODULE, DOC_MODULE, PYTHON_MODULE
from packaging import build_python_wheel, install_python_wheels
from testing import tests_cpp, tests_python
from versioning import increment_development_version, reset_development_version
from versioning import increment_development_version, increment_major_version, increment_minor_version, \
increment_patch_version, reset_development_version

from SCons.Script import COMMAND_LINE_TARGETS
from SCons.Script.SConscript import SConsEnvironment
Expand All @@ -34,6 +35,9 @@ def __print_if_clean(environment, message: str):
# Define target names...
TARGET_NAME_INCREMENT_DEVELOPMENT_VERSION = 'increment_development_version'
TARGET_NAME_RESET_DEVELOPMENT_VERSION = 'reset_development_version'
TARGET_NAME_INCREMENT_PATCH_VERSION = 'increment_patch_version'
TARGET_NAME_INCREMENT_MINOR_VERSION = 'increment_minor_version'
TARGET_NAME_INCREMENT_MAJOR_VERSION = 'increment_major_version'
TARGET_NAME_TEST_FORMAT = 'test_format'
TARGET_NAME_TEST_FORMAT_PYTHON = TARGET_NAME_TEST_FORMAT + '_python'
TARGET_NAME_TEST_FORMAT_CPP = TARGET_NAME_TEST_FORMAT + '_cpp'
Expand Down Expand Up @@ -61,13 +65,14 @@ def __print_if_clean(environment, message: str):
TARGET_NAME_DOC = 'doc'

VALID_TARGETS = {
TARGET_NAME_INCREMENT_DEVELOPMENT_VERSION, TARGET_NAME_RESET_DEVELOPMENT_VERSION, TARGET_NAME_TEST_FORMAT,
TARGET_NAME_TEST_FORMAT_PYTHON, TARGET_NAME_TEST_FORMAT_CPP, TARGET_NAME_TEST_FORMAT_MD, TARGET_NAME_FORMAT,
TARGET_NAME_FORMAT_PYTHON, TARGET_NAME_FORMAT_CPP, TARGET_NAME_FORMAT_MD, TARGET_NAME_CHECK_DEPENDENCIES,
TARGET_NAME_VENV, TARGET_NAME_COMPILE, TARGET_NAME_COMPILE_CPP, TARGET_NAME_COMPILE_CYTHON, TARGET_NAME_INSTALL,
TARGET_NAME_INSTALL_CPP, TARGET_NAME_INSTALL_CYTHON, TARGET_NAME_BUILD_WHEELS, TARGET_NAME_INSTALL_WHEELS,
TARGET_NAME_TESTS, TARGET_NAME_TESTS_CPP, TARGET_NAME_TESTS_PYTHON, TARGET_NAME_APIDOC, TARGET_NAME_APIDOC_CPP,
TARGET_NAME_APIDOC_PYTHON, TARGET_NAME_DOC
TARGET_NAME_INCREMENT_DEVELOPMENT_VERSION, TARGET_NAME_RESET_DEVELOPMENT_VERSION,
TARGET_NAME_INCREMENT_PATCH_VERSION, TARGET_NAME_INCREMENT_MINOR_VERSION, TARGET_NAME_INCREMENT_MAJOR_VERSION,
TARGET_NAME_TEST_FORMAT, TARGET_NAME_TEST_FORMAT_PYTHON, TARGET_NAME_TEST_FORMAT_CPP, TARGET_NAME_TEST_FORMAT_MD,
TARGET_NAME_FORMAT, TARGET_NAME_FORMAT_PYTHON, TARGET_NAME_FORMAT_CPP, TARGET_NAME_FORMAT_MD,
TARGET_NAME_CHECK_DEPENDENCIES, TARGET_NAME_VENV, TARGET_NAME_COMPILE, TARGET_NAME_COMPILE_CPP,
TARGET_NAME_COMPILE_CYTHON, TARGET_NAME_INSTALL, TARGET_NAME_INSTALL_CPP, TARGET_NAME_INSTALL_CYTHON,
TARGET_NAME_BUILD_WHEELS, TARGET_NAME_INSTALL_WHEELS, TARGET_NAME_TESTS, TARGET_NAME_TESTS_CPP,
TARGET_NAME_TESTS_PYTHON, TARGET_NAME_APIDOC, TARGET_NAME_APIDOC_CPP, TARGET_NAME_APIDOC_PYTHON, TARGET_NAME_DOC
}

DEFAULT_TARGET = TARGET_NAME_INSTALL_WHEELS
Expand All @@ -85,12 +90,11 @@ def __print_if_clean(environment, message: str):
env.SConsignFile(name=path.relpath(path.join(BUILD_MODULE.build_dir, '.sconsign'), BUILD_MODULE.root_dir))

# Defines targets for updating the project's version...
target_increment_development_version = __create_phony_target(env,
TARGET_NAME_INCREMENT_DEVELOPMENT_VERSION,
action=increment_development_version)
target_reset_development_version = __create_phony_target(env,
TARGET_NAME_RESET_DEVELOPMENT_VERSION,
action=reset_development_version)
__create_phony_target(env, TARGET_NAME_INCREMENT_DEVELOPMENT_VERSION, action=increment_development_version)
__create_phony_target(env, TARGET_NAME_RESET_DEVELOPMENT_VERSION, action=reset_development_version)
__create_phony_target(env, TARGET_NAME_INCREMENT_PATCH_VERSION, action=increment_patch_version)
__create_phony_target(env, TARGET_NAME_INCREMENT_MINOR_VERSION, action=increment_minor_version)
__create_phony_target(env, TARGET_NAME_INCREMENT_MAJOR_VERSION, action=increment_major_version)

# Define targets for checking code style definitions...
target_test_format_python = __create_phony_target(env, TARGET_NAME_TEST_FORMAT_PYTHON, action=check_python_code_style)
Expand Down
Loading

0 comments on commit e9ac13a

Please sign in to comment.