diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4b1be560..527692b69 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,3 +35,11 @@ Please, note that we follow [Effective Go](https://golang.org/doc/effective_go.h 2. Preserve as much history as possible. If you need to re-name file use `git mv` - it will preserve git history. + +## Commit format + +We use a slight variation of [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). + +We enforce the usage of `!` for breaking changes, or `_` for non-breaking. The rationale is that +if we don't enforce one or the other, often devs will forget to add `!` to breaking changes. +Forcing to add one or the other, will also hopefully force devs to make a decision with each commit. diff --git a/Makefile b/Makefile index eb2b6311b..6b98caa91 100644 --- a/Makefile +++ b/Makefile @@ -439,6 +439,12 @@ migration: migration-check: bash _assets/scripts/migration_check.sh +commit-check: + bash _assets/scripts/commit_check.sh + +tag-version: + bash _assets/scripts/tag_version.sh $(TARGET_COMMIT) + migration-wallet: DEFAULT_WALLET_MIGRATION_PATH := walletdatabase/migrations/sql migration-wallet: touch $(DEFAULT_WALLET_MIGRATION_PATH)/$$(date +%s)_$(D).up.sql diff --git a/RELEASING.md b/RELEASING.md index 40d6e17ad..5958a4512 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -17,24 +17,34 @@ We use `0` as the MAJOR version and bump only MINOR when there are breaking chan After successful build, open it (https://ci.status.im/job/status-go/job/manual/$BUILD_ID/) in a browser. Artifacts will have a random ID, for example `status-go-android-181221-143603-5708af.aar`, means that `181221-143603-5708af` is a version you can use in [status-mobile](https://github.com/status-im/status-mobile). -## Releasing a new patch (no breaking changes or a hot-fix release) -TODO: create a script that can do that instead of manual work. +## Release branch -1. Checkout a release branch you want to release from (release branches have names like `release/0.X`), -1. Cherry-pick a commit you want to include OR merge `develop` branch, -1. Bump `Y` (`0.X.Y`) in the current version (`VERSION` file), -1. Commit and push the change to `release/0.X` branch, -1. Go to [Jenkins job](https://ci.status.im/job/status-go/job/manual/), select "RELEASE" and use `release/0.X` branch name. +The release branch takes the form of `release/v0.y.x`, where `x` is hardcoded. +For example a valid release branch name is `release/v0.177.x` or `release/v0.188.x`. +Currently commits on this branch are not tagged and the branch name is used as a ref. -## Releasing a new version (breaking changes) +### Hotfixes -TODO: create a script that can do that instead of manual work. +If an hotfix is necessary on the release branch (that happens after the app is released, and we need to push out a patched version), we historically tagged it using the format `release/v0.177.x+hotfix.1`. -1. Merge your PR to `develop` branch, -1. Pull `develop` branch locally, -1. Bump `X`, reset `Z` to `0` and commit to `develop` with a message "Bump version to 0.X.Y", -1. Checkout a new branch `release/0.X`, -1. Remove `-beta.Z` suffix from the current version (`VERSION` file), -1. Commit and push the change, -1. Go to [Jenkins job](https://ci.status.im/job/status-go/job/manual/), select "RELEASE" and use `release/0.X` branch. + +The process over release branches is still in work since we still had few coordinated release between desktop and mobile, and we are still in the exploration phase. + + +## Tagging versions + +To tag a version, you should run the command: + +`make tag-version` to create a tag for `HEAD` + +or + +`make tag-version TARGET_COMMIT={hash}` to create a tag for a specific hash + +You will have to then check the tag is correct, and push the tag: + +`git push origin {tag_created}` + + +That can then be used as a stable tag. diff --git a/_assets/ci/Jenkinsfile.tests b/_assets/ci/Jenkinsfile.tests index 0f6a434d2..e3e1ed1bc 100644 --- a/_assets/ci/Jenkinsfile.tests +++ b/_assets/ci/Jenkinsfile.tests @@ -81,6 +81,16 @@ pipeline { } } } + stage('Commit') { + when { // https://github.com/status-im/status-go/issues/4993#issuecomment-2022685544 + expression { !isTestNightlyJob() } + } + steps { script { + nix.shell('make commit-check', pure: false) + } } + } + + stage('Lint') { steps { script { nix.shell('make lint', pure: true) diff --git a/_assets/hooks/pre-merge-commit b/_assets/hooks/pre-merge-commit deleted file mode 120000 index 0a7a928c5..000000000 --- a/_assets/hooks/pre-merge-commit +++ /dev/null @@ -1 +0,0 @@ -../scripts/migration_check.sh \ No newline at end of file diff --git a/_assets/hooks/pre-merge-commit b/_assets/hooks/pre-merge-commit new file mode 100755 index 000000000..179011c6a --- /dev/null +++ b/_assets/hooks/pre-merge-commit @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +CHECK_MIGRATIONS="_assets/scripts/migration_check.sh" +CHECK_COMMITS="_assets/scripts/commit_check.sh" + +"${CHECK_MIGRATIONS}" "$@" +"${CHECK_COMMITS}" "$@" diff --git a/_assets/scripts/commit_check.sh b/_assets/scripts/commit_check.sh new file mode 100755 index 000000000..e2bbc0f96 --- /dev/null +++ b/_assets/scripts/commit_check.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euo pipefail + +parse_commits() { + start_commit=${1:-origin/develop} + end_commit=${2:-HEAD} + is_breaking_change=false + + echo "checking commits between: $start_commit $end_commit" >&2 + # Run the loop in the current shell using process substitution + while IFS= read -r message || [ -n "$message" ]; do + # Check if commit message follows conventional commits format + if [[ $message =~ ^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.*\))?(\_|!):.*$ ]]; then + # Check for breaking changes + if [[ ${BASH_REMATCH[3]} == *'!'* ]]; then + is_breaking_change=true + break + fi + else + echo "Commit message \"$message\" is not well-formed. Aborting merge. We use https://www.conventionalcommits.org/en/v1.0.0/ but with _ for non-breaking changes" + # Uncomment the line below if you want to exit on an invalid commit message + exit 1 + fi + done < <(git log --format=%B "$start_commit".."$end_commit" | sed '/^\s*$/d') + + echo "$is_breaking_change" +} + +parse_commits diff --git a/_assets/scripts/tag_version.sh b/_assets/scripts/tag_version.sh new file mode 100644 index 000000000..b3025b2a3 --- /dev/null +++ b/_assets/scripts/tag_version.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source _assets/scripts/commit_check.sh + +get_latest_tag() { + # Get the latest tag on develop + latest_tag=$(git describe --tags --abbrev=0 develop) + echo "$latest_tag" +} + +bump_version() { + local tag=$1 + local is_breaking_change=$2 + IFS='.' read -r major minor patch <<< "$tag" + + # Bump the version based on the type of change + if [[ "$is_breaking_change" = true ]]; then + ((minor++)) + else + ((patch++)) + fi + + new_version="$major.$minor.$patch" + echo "$new_version" +} + +calculate_new_version() { + # Get the latest tag + latest_tag=$(get_latest_tag) + + echo "calculating new tag from $latest_tag and $1" >&2 + + # Parse commits to determine if there are breaking changes + is_breaking_change=$(parse_commits "$latest_tag" "$1") + + # Bump version accordingly + echo "$(bump_version "$latest_tag" "$is_breaking_change")" + } + + +main() { + target_commit=${1:-HEAD} + new_version=$(calculate_new_version "$target_commit") + echo "calculated new version: $new_version" >&2 + + git tag -a "$new_version" "$target_commit" -m "release $target_commit" +} + +main "$1"