From 1af61410c31b236d1c6744a4fa98811206e50a11 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 6 Jul 2026 20:01:16 -0400 Subject: [PATCH 1/5] Add default release patch+1 to release workflow --- .github/steps/resolve-version/action.yml | 46 ++++++++++++++++++++++++ .github/workflows/release.yml | 19 ++++++---- 2 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 .github/steps/resolve-version/action.yml diff --git a/.github/steps/resolve-version/action.yml b/.github/steps/resolve-version/action.yml new file mode 100644 index 000000000..eedb535bf --- /dev/null +++ b/.github/steps/resolve-version/action.yml @@ -0,0 +1,46 @@ +name: 'Resolve Version' +description: 'Resolves the release version: patch+1 auto bump when empty, provided version used as-is' + +inputs: + version: + description: 'Requested version (x.y.z). Leave empty for auto patch bump' + required: false + default: '' + +outputs: + version: + description: 'Resolved release version' + value: ${{ steps.resolve.outputs.version }} + +runs: + using: "composite" + steps: + - name: Resolve version + id: resolve + shell: bash + env: + INPUT_VERSION: ${{ inputs.version }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + latest=$(gh release view --json tagName --jq '.tagName' 2>/dev/null || echo "") + latest=${latest:-0.0.0} + if ! [[ "$latest" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + latest="0.0.0" + fi + IFS='.' read -r cur_major cur_minor cur_patch <<< "$latest" + + if [ -z "$INPUT_VERSION" ]; then + version="${cur_major}.${cur_minor}.$((cur_patch + 1))" + echo "No version provided, auto patch bump: ${latest} -> ${version}" + else + if ! [[ "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid version format: ${INPUT_VERSION} (expected x.y.z)" + exit 1 + fi + version="$INPUT_VERSION" + echo "Using provided version: ${version}" + fi + + echo "version=${version}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7f653e511..1a090257b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,9 +16,10 @@ on: workflow_dispatch: inputs: version: - description: "Version to Release (e.g., v1.2.3)" - required: true + description: "Version to Release (x.y.z). Leave empty for auto patch bump." + required: false type: string + default: '' env: REGISTRY: "ghcr.io" @@ -203,13 +204,17 @@ jobs: name: "Create Release" needs: [build_client, create_universal_macos] runs-on: ubuntu-latest - if: | - github.event_name == 'workflow_dispatch' && - inputs.version != '' + if: github.event_name == 'workflow_dispatch' steps: - name: Checkout uses: actions/checkout@v4 + - name: Resolve version + id: resolve + uses: ./.github/steps/resolve-version + with: + version: ${{ inputs.version }} + - name: Download all artifacts uses: actions/download-artifact@v4 with: @@ -254,8 +259,8 @@ jobs: - name: Create Release uses: softprops/action-gh-release@v2 with: - tag_name: ${{ inputs.version }} - name: "🦩 ${{ inputs.version }}" + tag_name: ${{ steps.resolve.outputs.version }} + name: "🦩 ${{ steps.resolve.outputs.version }}" draft: false prerelease: false token: ${{ secrets.GITHUB_TOKEN }} From 160a551b7cad0cbbef9c3853d925eaf3be6e6320 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Tue, 7 Jul 2026 08:53:49 -0400 Subject: [PATCH 2/5] ci(release): rename resolve step to version for clarity --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1a090257b..59b8b829f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -210,7 +210,7 @@ jobs: uses: actions/checkout@v4 - name: Resolve version - id: resolve + id: version uses: ./.github/steps/resolve-version with: version: ${{ inputs.version }} @@ -259,8 +259,8 @@ jobs: - name: Create Release uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.resolve.outputs.version }} - name: "🦩 ${{ steps.resolve.outputs.version }}" + tag_name: ${{ steps.version.outputs.version }} + name: "🦩 ${{ steps.version.outputs.version }}" draft: false prerelease: false token: ${{ secrets.GITHUB_TOKEN }} From 67cc7778b079fe4b24d8c1e38f9a8e03ce3da0b5 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Tue, 7 Jul 2026 14:27:09 -0400 Subject: [PATCH 3/5] ci(release): rename resolve-version action to version --- .github/steps/{resolve-version => version}/action.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename .github/steps/{resolve-version => version}/action.yml (95%) diff --git a/.github/steps/resolve-version/action.yml b/.github/steps/version/action.yml similarity index 95% rename from .github/steps/resolve-version/action.yml rename to .github/steps/version/action.yml index eedb535bf..c6f8f1fb8 100644 --- a/.github/steps/resolve-version/action.yml +++ b/.github/steps/version/action.yml @@ -10,13 +10,13 @@ inputs: outputs: version: description: 'Resolved release version' - value: ${{ steps.resolve.outputs.version }} + value: ${{ steps.version.outputs.version }} runs: using: "composite" steps: - name: Resolve version - id: resolve + id: version shell: bash env: INPUT_VERSION: ${{ inputs.version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 59b8b829f..5ccdf4484 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -211,7 +211,7 @@ jobs: - name: Resolve version id: version - uses: ./.github/steps/resolve-version + uses: ./.github/steps/version with: version: ${{ inputs.version }} From 14d6f8433e7a7c659249e84cf2cb9d8a0239f2f5 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Wed, 8 Jul 2026 19:17:09 -0400 Subject: [PATCH 4/5] ci(release): resolve latest via releases/latest API, validate bump --- .github/steps/version/action.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/steps/version/action.yml b/.github/steps/version/action.yml index c6f8f1fb8..cf470fbde 100644 --- a/.github/steps/version/action.yml +++ b/.github/steps/version/action.yml @@ -24,11 +24,8 @@ runs: run: | set -euo pipefail - latest=$(gh release view --json tagName --jq '.tagName' 2>/dev/null || echo "") - latest=${latest:-0.0.0} - if ! [[ "$latest" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - latest="0.0.0" - fi + latest=$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name) + [[ "$latest" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Latest release tag '${latest}' is not x.y.z"; exit 1; } IFS='.' read -r cur_major cur_minor cur_patch <<< "$latest" if [ -z "$INPUT_VERSION" ]; then @@ -39,6 +36,13 @@ runs: echo "Invalid version format: ${INPUT_VERSION} (expected x.y.z)" exit 1 fi + IFS='.' read -r new_major new_minor new_patch <<< "$INPUT_VERSION" + if ! ([ "$new_major" -eq $((cur_major + 1)) ] && [ "$new_minor" -eq 0 ] && [ "$new_patch" -eq 0 ]) && + ! ([ "$new_major" -eq "$cur_major" ] && [ "$new_minor" -eq $((cur_minor + 1)) ] && [ "$new_patch" -eq 0 ]) && + ! ([ "$new_major" -eq "$cur_major" ] && [ "$new_minor" -eq "$cur_minor" ] && [ "$new_patch" -eq $((cur_patch + 1)) ]); then + echo "Invalid version bump: ${latest} -> ${INPUT_VERSION} (expected $((cur_major + 1)).0.0, ${cur_major}.$((cur_minor + 1)).0, or ${cur_major}.${cur_minor}.$((cur_patch + 1)))" + exit 1 + fi version="$INPUT_VERSION" echo "Using provided version: ${version}" fi From 8516360085206ac1c366e79ea851866223580932 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Thu, 9 Jul 2026 08:41:12 -0400 Subject: [PATCH 5/5] ci(release): move version resolution to reusable workflow --- .github/steps/version/action.yml | 50 ------------------------- .github/workflows/release.yml | 18 ++++----- .github/workflows/version.yml | 63 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 59 deletions(-) delete mode 100644 .github/steps/version/action.yml create mode 100644 .github/workflows/version.yml diff --git a/.github/steps/version/action.yml b/.github/steps/version/action.yml deleted file mode 100644 index cf470fbde..000000000 --- a/.github/steps/version/action.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: 'Resolve Version' -description: 'Resolves the release version: patch+1 auto bump when empty, provided version used as-is' - -inputs: - version: - description: 'Requested version (x.y.z). Leave empty for auto patch bump' - required: false - default: '' - -outputs: - version: - description: 'Resolved release version' - value: ${{ steps.version.outputs.version }} - -runs: - using: "composite" - steps: - - name: Resolve version - id: version - shell: bash - env: - INPUT_VERSION: ${{ inputs.version }} - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - - latest=$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name) - [[ "$latest" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Latest release tag '${latest}' is not x.y.z"; exit 1; } - IFS='.' read -r cur_major cur_minor cur_patch <<< "$latest" - - if [ -z "$INPUT_VERSION" ]; then - version="${cur_major}.${cur_minor}.$((cur_patch + 1))" - echo "No version provided, auto patch bump: ${latest} -> ${version}" - else - if ! [[ "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Invalid version format: ${INPUT_VERSION} (expected x.y.z)" - exit 1 - fi - IFS='.' read -r new_major new_minor new_patch <<< "$INPUT_VERSION" - if ! ([ "$new_major" -eq $((cur_major + 1)) ] && [ "$new_minor" -eq 0 ] && [ "$new_patch" -eq 0 ]) && - ! ([ "$new_major" -eq "$cur_major" ] && [ "$new_minor" -eq $((cur_minor + 1)) ] && [ "$new_patch" -eq 0 ]) && - ! ([ "$new_major" -eq "$cur_major" ] && [ "$new_minor" -eq "$cur_minor" ] && [ "$new_patch" -eq $((cur_patch + 1)) ]); then - echo "Invalid version bump: ${latest} -> ${INPUT_VERSION} (expected $((cur_major + 1)).0.0, ${cur_major}.$((cur_minor + 1)).0, or ${cur_major}.${cur_minor}.$((cur_patch + 1)))" - exit 1 - fi - version="$INPUT_VERSION" - echo "Using provided version: ${version}" - fi - - echo "version=${version}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ccdf4484..8bf6dad27 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,12 @@ env: # ============================================================================= jobs: + version: + name: Resolve Version + uses: ./.github/workflows/version.yml + with: + version: ${{ inputs.version }} + build_client: name: "Build C Client (${{ matrix.os }})" runs-on: ${{ matrix.os }} @@ -202,19 +208,13 @@ jobs: release: name: "Create Release" - needs: [build_client, create_universal_macos] + needs: [version, build_client, create_universal_macos] runs-on: ubuntu-latest if: github.event_name == 'workflow_dispatch' steps: - name: Checkout uses: actions/checkout@v4 - - name: Resolve version - id: version - uses: ./.github/steps/version - with: - version: ${{ inputs.version }} - - name: Download all artifacts uses: actions/download-artifact@v4 with: @@ -259,8 +259,8 @@ jobs: - name: Create Release uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.version.outputs.version }} - name: "🦩 ${{ steps.version.outputs.version }}" + tag_name: ${{ needs.version.outputs.version }} + name: "🦩 ${{ needs.version.outputs.version }}" draft: false prerelease: false token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml new file mode 100644 index 000000000..e45591fa6 --- /dev/null +++ b/.github/workflows/version.yml @@ -0,0 +1,63 @@ +name: Resolve Version + +on: + workflow_call: + inputs: + version: + description: "Version to release (x.y.z). Leave empty for auto patch bump." + required: false + type: string + default: '' + outputs: + version: + description: "Resolved release version" + value: ${{ jobs.version.outputs.version }} + +jobs: + version: + name: Resolve version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version_push.outputs.version || steps.version_dispatch.outputs.version }} + steps: + - name: Resolve version (push) + id: version_push + if: github.event_name != 'workflow_dispatch' + run: | + echo "push event: version resolves to latest" + echo "version=latest" >> "$GITHUB_OUTPUT" + + - name: Resolve version (dispatch) + id: version_dispatch + if: github.event_name == 'workflow_dispatch' + env: + INPUT_VERSION: ${{ inputs.version }} + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + set -euo pipefail + + latest=$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name) + [[ "$latest" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Latest release tag '${latest}' is not x.y.z"; exit 1; } + IFS='.' read -r cur_major cur_minor cur_patch <<< "$latest" + + if [ -z "$INPUT_VERSION" ]; then + version="${cur_major}.${cur_minor}.$((cur_patch + 1))" + echo "No version provided, auto patch bump: ${latest} -> ${version}" + else + if ! [[ "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid version format: ${INPUT_VERSION} (expected x.y.z)" + exit 1 + fi + IFS='.' read -r new_major new_minor new_patch <<< "$INPUT_VERSION" + if ! ([ "$new_major" -eq $((cur_major + 1)) ] && [ "$new_minor" -eq 0 ] && [ "$new_patch" -eq 0 ]) && + ! ([ "$new_major" -eq "$cur_major" ] && [ "$new_minor" -eq $((cur_minor + 1)) ] && [ "$new_patch" -eq 0 ]) && + ! ([ "$new_major" -eq "$cur_major" ] && [ "$new_minor" -eq "$cur_minor" ] && [ "$new_patch" -eq $((cur_patch + 1)) ]); then + echo "Invalid version bump: ${latest} -> ${INPUT_VERSION} (expected $((cur_major + 1)).0.0, ${cur_major}.$((cur_minor + 1)).0, or ${cur_major}.${cur_minor}.$((cur_patch + 1)))" + exit 1 + fi + version="$INPUT_VERSION" + echo "Using provided version: ${version}" + fi + + echo "version=${version}" >> "$GITHUB_OUTPUT"