From 662db558bd6bc8eea2bd0a91b448d0ee98e5026c Mon Sep 17 00:00:00 2001 From: Torsten Ueberschar Date: Wed, 18 Mar 2026 22:20:02 +0100 Subject: [PATCH] das ist krank und ich kann es nicht validieren --- .github/actions/bump-version/action.yaml | 100 +++++++++++++++++------ README.md | 13 +-- 2 files changed, 84 insertions(+), 29 deletions(-) diff --git a/.github/actions/bump-version/action.yaml b/.github/actions/bump-version/action.yaml index 7616045..de770c8 100644 --- a/.github/actions/bump-version/action.yaml +++ b/.github/actions/bump-version/action.yaml @@ -6,7 +6,7 @@ inputs: required: false default: "24" release_line: - description: Release line for dev builds (e.g. 2.4) when it cannot be derived from the branch or PR target + description: Release line for dev builds (e.g. 2.4 or 2.4.1) when it cannot be derived from the branch or PR target required: false default: "" gitea_token: @@ -68,49 +68,103 @@ runs: node -p "require('./package.json').version" } - validate_release_line() { - local line="$1" - [[ "${line}" =~ ^[0-9]+\.[0-9]+$ ]] - } - - extract_release_line() { - local ref_name="$1" - if [[ "${ref_name}" =~ ^release/v?([0-9]+)\.([0-9]+)$ ]]; then - echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" + normalize_release_base() { + local value="$1" + if [[ "${value}" =~ ^v?([0-9]+)\.([0-9]+)$ ]]; then + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.0" + return 0 + fi + if [[ "${value}" =~ ^v?([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" return 0 fi return 1 } - resolve_release_line() { + extract_release_base() { + local ref_name="$1" + local normalized + if [[ "${ref_name}" =~ ^release/(.+)$ ]]; then + if normalized="$(normalize_release_base "${BASH_REMATCH[1]}")"; then + echo "${normalized}" + return 0 + fi + fi + return 1 + } + + infer_release_base_from_ancestor_release_branch() { + local best_release_base="" + local best_commit_time=0 + local remote_ref + local release_branch + local release_base + local commit_time + + # Best effort: not every repository has release branches. + git fetch --no-tags origin "+refs/heads/release/*:refs/remotes/origin/release/*" >/dev/null 2>&1 || true + + while IFS= read -r remote_ref; do + release_branch="${remote_ref#origin/}" + if ! release_base="$(extract_release_base "${release_branch}")"; then + continue + fi + + if ! git merge-base --is-ancestor "$(git rev-parse "${remote_ref}")" HEAD; then + continue + fi + + commit_time="$(git show -s --format=%ct "${remote_ref}")" + if [[ "${commit_time}" -gt "${best_commit_time}" ]]; then + best_commit_time="${commit_time}" + best_release_base="${release_base}" + fi + done < <(git for-each-ref --format='%(refname:short)' refs/remotes/origin/release/*) + + if [[ -n "${best_release_base}" ]]; then + echo "${best_release_base}" + return 0 + fi + + return 1 + } + + resolve_release_base() { local package_version + local normalized + local inferred_release_base if [[ -n "${INPUT_RELEASE_LINE}" ]]; then - if ! validate_release_line "${INPUT_RELEASE_LINE}"; then - echo "Invalid release_line input '${INPUT_RELEASE_LINE}'. Expected format: ." >&2 + if ! normalized="$(normalize_release_base "${INPUT_RELEASE_LINE}")"; then + echo "Invalid release_line input '${INPUT_RELEASE_LINE}'. Expected format: . or .." >&2 exit 1 fi - echo "${INPUT_RELEASE_LINE}" + echo "${normalized}" return 0 fi - if extract_release_line "${BRANCH}" >/dev/null; then - extract_release_line "${BRANCH}" + if extract_release_base "${BRANCH}" >/dev/null; then + extract_release_base "${BRANCH}" return 0 fi - if [[ -n "${BASE_BRANCH}" ]] && extract_release_line "${BASE_BRANCH}" >/dev/null; then - extract_release_line "${BASE_BRANCH}" + if [[ -n "${BASE_BRANCH}" ]] && extract_release_base "${BASE_BRANCH}" >/dev/null; then + extract_release_base "${BASE_BRANCH}" + return 0 + fi + + if inferred_release_base="$(infer_release_base_from_ancestor_release_branch)"; then + echo "${inferred_release_base}" return 0 fi package_version="$(read_package_version)" if [[ "${package_version}" =~ ^([0-9]+)\.([0-9]+)\.[0-9]+([-.].*)?$ ]]; then - echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.0" return 0 fi - echo "Unable to determine release line for branch '${BRANCH}'. Use release/., provide inputs.release_line, or ensure package.json contains a semantic version." >&2 + echo "Unable to determine release base for branch '${BRANCH}'. Use release/.[.], provide inputs.release_line, or ensure package.json contains a semantic version." >&2 exit 1 } @@ -164,13 +218,11 @@ runs: fi ;; release/*) - RELEASE_LINE="$(resolve_release_line)" - RELEASE_BASE="${RELEASE_LINE}.0" + RELEASE_BASE="$(resolve_release_base)" set_version "$(next_beta_version "${RELEASE_BASE}")" ;; *) - RELEASE_LINE="$(resolve_release_line)" - RELEASE_BASE="${RELEASE_LINE}.0" + RELEASE_BASE="$(resolve_release_base)" set_version "$(next_dev_version "${RELEASE_BASE}")" ;; esac diff --git a/README.md b/README.md index 6be26bf..96e207e 100644 --- a/README.md +++ b/README.md @@ -9,20 +9,21 @@ Creates or updates the version in `package.json`, commits it, creates a matching Inputs: - `node_version`: Node.js version to use. Default: `24` -- `release_line`: optional release line for dev builds. Format: `.`. Overrides branch, PR target, and `package.json`. +- `release_line`: optional release line for dev builds. Format: `.` or `..`. Overrides branch, PR target, inferred ancestor release branch, and `package.json`. - `gitea_token`: optional token used for checkout and push Branch and tag rules: - `main`: creates release tags from the latest commit message (`release/x.y.z`, `release/x.y`, `hotfix/x.y.z`) or falls back to a patch bump. -- `release/.`: creates the next beta tag for that release line, e.g. `v2.4.0-beta.3`. -- all other branches: create a dev tag for the resolved release line using a UTC timestamp, e.g. `v2.4.0-dev.20260314153045`. +- `release/.` or `release/..`: creates the next beta tag for that release base, e.g. `v2.4.0-beta.3` or `v2.4.1-beta.3`. +- all other branches: create a dev tag for the resolved release base using a UTC timestamp, e.g. `v2.4.0-dev.20260314153045`. Dev release line resolution order: - `inputs.release_line` -- current branch if it matches `release/.` -- PR target branch if it matches `release/.` +- current branch if it matches `release/.` or `release/..` +- PR target branch if it matches `release/.` or `release/..` +- newest ancestor branch from `origin/release/*` (for feature branches created from release branches outside PR context) - `major.minor` from `package.json` If no release line can be resolved for a dev build, the action fails intentionally. @@ -38,7 +39,9 @@ Commit message examples on `main`: Branch examples: - `release/2.4` -> next `v2.4.0-beta.N` +- `release/v2.4.1` -> next `v2.4.1-beta.N` - feature branch in a PR to `release/2.4` -> `v2.4.0-dev.` +- feature branch created from `release/v2.4.1` (without PR base) -> `v2.4.1-dev.` when the release branch tip is an ancestor - feature branch outside a PR with `package.json` version `2.4.0` -> `v2.4.0-dev.` - feature branch outside a PR with `release_line: 2.4` -> `v2.4.0-dev.`