diff --git a/README.md b/README.md index 6f29e4a..52c8183 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A forge-neutral composite action for preparing a versioned Frappe/ERPNext app before merging its release branch. It uses [`bump-my-version`](https://callowayproject.github.io/bump-my-version/) and ordinary Git commands, without `gh` or GitHub API calls. -The action compares `[tool.bumpversion].current_version` on the checked-out target branch with the configured base branch: +The action compares the app's `__version__` on the checked-out target branch with the configured base branch: - Equal: bump, commit, and optionally push the new version. - Target newer than base: do nothing, making PR synchronization events idempotent. @@ -13,22 +13,9 @@ The action compares `[tool.bumpversion].current_version` on the checked-out targ - A Linux runner with Bash, Git, and Python 3.11 or newer. - A clean checkout of the branch that should receive the version commit. - Persisted Git credentials with permission to push that branch. -- Stable `MAJOR.MINOR.PATCH` versions. -- A `bump-my-version` configuration that includes every version file. +- A version file containing a stable `MAJOR.MINOR.PATCH` assignment such as `__version__ = "0.0.1"`. -Example `pyproject.toml` for a Frappe app: - -```toml -[tool.bumpversion] -current_version = "0.0.1" -commit = false -tag = false - -[[tool.bumpversion.files]] -filename = "erp_operations/__init__.py" -search = '__version__ = "{current_version}"' -replace = '__version__ = "{new_version}"' -``` +No `bump-my-version` configuration is required in the consuming app. The action passes the current version, search/replace templates, and version file directly to the CLI. ## Gitea Actions usage @@ -61,7 +48,6 @@ jobs: # The absolute URL is required when Gitea's DEFAULT_ACTIONS_URL is "github". uses: https://gitea.cptckp.com/Nurinda/bump-erp-app-version@v1 with: - config-file: pyproject.toml version-file: erp_operations/__init__.py base-branch: main target-branch: develop @@ -79,8 +65,7 @@ Gitea may suppress workflows caused by pushes authenticated with its built-in `G | Input | Default | Description | |---|---|---| -| `config-file` | `pyproject.toml` | `bump-my-version` TOML configuration | -| `version-file` | empty | App `__init__.py` used to bootstrap a base branch that predates the configuration | +| `version-file` | required | App `__init__.py` containing the authoritative `__version__` | | `base-branch` | `main` | Branch providing the baseline version | | `target-branch` | `develop` | Checked-out branch receiving the commit | | `bump` | `patch` | `major`, `minor`, or `patch` | @@ -104,7 +89,7 @@ Gitea may suppress workflows caused by pushes authenticated with its built-in `G The action does not force-push, create tags, publish releases, or call forge APIs. Concurrent release jobs are resolved safely by the remote rejecting a non-fast-forward push. Configure workflow-level concurrency if release PRs can overlap. -For the first versioned release, the base branch might not contain `[tool.bumpversion]` yet. Set `version-file` to the app's `__init__.py`; the action will read its base-branch `__version__` as the bootstrap baseline. Once the configuration reaches the base branch, it becomes the authoritative baseline. +The configured `version-file` is the only version source. The action reads it from both branches and commits only the version change produced by `bump-my-version`. ## Releasing the action diff --git a/action.yml b/action.yml index 1cd380f..192e874 100644 --- a/action.yml +++ b/action.yml @@ -1,14 +1,9 @@ name: Bump ERP app version description: Bump a Frappe/ERPNext app version on a release branch using bump-my-version inputs: - config-file: - description: Path to the bump-my-version TOML configuration - required: false - default: pyproject.toml version-file: - description: App __init__.py used to bootstrap when the base branch has no bump-my-version config - required: false - default: "" + description: App __init__.py containing a stable MAJOR.MINOR.PATCH __version__ assignment + required: true base-branch: description: Branch whose version is used as the release baseline required: false @@ -65,7 +60,6 @@ runs: id: bump shell: bash env: - INPUT_CONFIG_FILE: ${{ inputs.config-file }} INPUT_VERSION_FILE: ${{ inputs.version-file }} INPUT_BASE_BRANCH: ${{ inputs.base-branch }} INPUT_TARGET_BRANCH: ${{ inputs.target-branch }} diff --git a/scripts/bump.sh b/scripts/bump.sh index 1c3d495..e6fbff0 100755 --- a/scripts/bump.sh +++ b/scripts/bump.sh @@ -1,8 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -: "${INPUT_CONFIG_FILE:?config-file is required}" -: "${INPUT_VERSION_FILE:=}" +: "${INPUT_VERSION_FILE:?version-file is required}" : "${INPUT_BASE_BRANCH:?base-branch is required}" : "${INPUT_TARGET_BRANCH:?target-branch is required}" : "${INPUT_BUMP:?bump is required}" @@ -24,8 +23,8 @@ case "${INPUT_BUMP}" in *) echo "bump must be major, minor, or patch" >&2; exit 1 ;; esac -if [[ ! -f "${INPUT_CONFIG_FILE}" ]]; then - echo "Configuration file not found: ${INPUT_CONFIG_FILE}" >&2 +if [[ ! -f "${INPUT_VERSION_FILE}" ]]; then + echo "Version file not found: ${INPUT_VERSION_FILE}" >&2 exit 1 fi @@ -45,12 +44,8 @@ git fetch "${INPUT_REMOTE}" \ script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) base_ref="refs/remotes/${INPUT_REMOTE}/${INPUT_BASE_BRANCH}" -current_version=$(python3 "${script_dir}/read_version.py" "${INPUT_CONFIG_FILE}") -base_version_args=("${INPUT_CONFIG_FILE}" --ref "${base_ref}") -if [[ -n "${INPUT_VERSION_FILE}" ]]; then - base_version_args+=(--fallback-version-file "${INPUT_VERSION_FILE}") -fi -base_version=$(python3 "${script_dir}/read_version.py" "${base_version_args[@]}") +current_version=$(python3 "${script_dir}/read_version.py" "${INPUT_VERSION_FILE}") +base_version=$(python3 "${script_dir}/read_version.py" "${INPUT_VERSION_FILE}" --ref "${base_ref}") comparison=$(python3 - "${current_version}" "${base_version}" <<'PY' import re @@ -96,13 +91,17 @@ git config user.email "${INPUT_GIT_USER_EMAIL}" message=${INPUT_COMMIT_MESSAGE//\{version\}/\{new_version\}} "${venv_dir}/bin/bump-my-version" bump \ - --config-file "${INPUT_CONFIG_FILE}" \ + --current-version "${current_version}" \ + --search '__version__ = "{current_version}"' \ + --replace '__version__ = "{new_version}"' \ + --no-configured-files \ --commit \ --no-tag \ --message "${message}" \ - "${INPUT_BUMP}" + "${INPUT_BUMP}" \ + "${INPUT_VERSION_FILE}" -new_version=$(python3 "${script_dir}/read_version.py" "${INPUT_CONFIG_FILE}") +new_version=$(python3 "${script_dir}/read_version.py" "${INPUT_VERSION_FILE}") commit=$(git rev-parse HEAD) if [[ "${INPUT_PUSH}" == "true" ]]; then diff --git a/scripts/read_version.py b/scripts/read_version.py index ce0383d..7ee6f81 100755 --- a/scripts/read_version.py +++ b/scripts/read_version.py @@ -1,73 +1,40 @@ #!/usr/bin/env python3 -"""Read bump-my-version's current_version from a local file or Git ref.""" +"""Read a Frappe app's stable __version__ from a local file or Git ref.""" from __future__ import annotations import argparse import re import subprocess -import sys -import tomllib def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() - parser.add_argument("config_file") + parser.add_argument("version_file") parser.add_argument("--ref") - parser.add_argument("--fallback-version-file") return parser.parse_args() -def read_bootstrap_version(args: argparse.Namespace, config_error: KeyError) -> str: - if not args.ref or not args.fallback_version_file: - raise SystemExit( - f"Could not read tool.bumpversion.current_version from {args.config_file}: {config_error}" - ) from config_error - - try: - version_content = subprocess.check_output( - ["git", "show", f"{args.ref}:{args.fallback_version_file}"], - text=True, - ) - except (OSError, subprocess.CalledProcessError) as error: - raise SystemExit( - f"Could not load bootstrap version file {args.fallback_version_file}: {error}" - ) from error - - match = re.search(r'^__version__ = "(\d+\.\d+\.\d+)"$', version_content, re.MULTILINE) - if not match: - raise SystemExit(f"Could not read __version__ from bootstrap file {args.fallback_version_file}") - - version = match.group(1) - print(f"Using bootstrap version {version} from {args.fallback_version_file}", file=sys.stderr) - return version - - def main() -> None: args = parse_args() try: if args.ref: content = subprocess.check_output( - ["git", "show", f"{args.ref}:{args.config_file}"], + ["git", "show", f"{args.ref}:{args.version_file}"], + text=True, ) else: - with open(args.config_file, "rb") as config_file: - content = config_file.read() + with open(args.version_file, encoding="utf-8") as version_file: + content = version_file.read() except (OSError, subprocess.CalledProcessError) as error: - raise SystemExit(f"Could not load {args.config_file}: {error}") from error + raise SystemExit(f"Could not load {args.version_file}: {error}") from error - try: - version = tomllib.loads(content.decode())["tool"]["bumpversion"]["current_version"] - except KeyError as error: - version = read_bootstrap_version(args, error) - except (TypeError, UnicodeDecodeError, tomllib.TOMLDecodeError) as error: + match = re.search(r'^__version__ = "(\d+\.\d+\.\d+)"$', content, re.MULTILINE) + if not match: raise SystemExit( - f"Could not read tool.bumpversion.current_version from {args.config_file}: {error}" - ) from error - - if not isinstance(version, str): - raise SystemExit("tool.bumpversion.current_version must be a string") - print(version) + f"Could not read a stable MAJOR.MINOR.PATCH __version__ from {args.version_file}" + ) + print(match.group(1)) if __name__ == "__main__": diff --git a/tests/integration.sh b/tests/integration.sh index 85399b9..c4b933d 100755 --- a/tests/integration.sh +++ b/tests/integration.sh @@ -22,23 +22,8 @@ git commit -qm initial git remote add origin "${test_root}/remote.git" git push -qu origin main git switch -qc develop -cat >> pyproject.toml <<'TOML' - -[tool.bumpversion] -current_version = "0.0.1" -commit = false -tag = false - -[[tool.bumpversion.files]] -filename = "erp_operations/__init__.py" -search = '__version__ = "{current_version}"' -replace = '__version__ = "{new_version}"' -TOML -git add pyproject.toml -git commit -qm 'configure versioning' git push -qu origin develop -export INPUT_CONFIG_FILE=pyproject.toml export INPUT_VERSION_FILE=erp_operations/__init__.py export INPUT_BASE_BRANCH=main export INPUT_TARGET_BRANCH=develop @@ -53,7 +38,7 @@ export RUNNER_TEMP="${test_root}" export GITHUB_OUTPUT="${test_root}/output" "${repo_root}/scripts/bump.sh" -grep -q '^current_version = "0.0.2"$' pyproject.toml +! grep -q '^\[tool.bumpversion\]' pyproject.toml grep -q '^__version__ = "0.0.2"$' erp_operations/__init__.py grep -q '^bumped=true$' "${GITHUB_OUTPUT}" grep -q '^previous-version=0.0.1$' "${GITHUB_OUTPUT}"