4 Commits

3 changed files with 113 additions and 21 deletions

39
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
permissions:
contents: write
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
name: Release ${{ steps.version.outputs.version }}
body: |
## git-release ${{ steps.version.outputs.version }}
Minimal tool to create and publish Git release tags following the vX.Y.Z format.
See the [README](https://github.com/${{ github.repository }}/blob/${{ steps.version.outputs.version }}/README.md) for more details.
files: |
git-release
LICENSE
README.md
draft: false
prerelease: false

View File

@@ -2,18 +2,52 @@
Minimal tool to create and publish Git release tags following the vX.Y.Z format.
## Usage
Installation
**Single project (default):**
- From the repository root: `./git-release` or `git release`
- Creates tags in the format `vX.Y.Z`
**Multi-project:**
- From the repository root: `./git-release <projectname>` or `git release <projectname>`
- Creates tags in the format `<projectname>-vX.Y.Z`
- Useful for monorepos with multiple independent release cycles
The script creates an annotated tag and pushes it to the configured remote.
## Example
```
$ git release
📦 Current version: v0.0.0
Select release type:
1) patch
2) minor
3) major
4) cancel
#? 2
🚀 Creating new release: v0.1.0
Confirm? (y/N): y
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 20 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 560 bytes | 560.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:ObNitram/git-release.git
* [new tag] v0.1.0 -> v0.1.0
✅ Release v0.1.0 created and pushed successfully!
```
## Installation
- Make the script executable: `chmod +x git-release`
- (Optional) Copy to PATH: `cp git-release $HOME/.local/bin/git-release`
- (Optional) Create a hard link in your PATH :
`ln "$(pwd)/git-release" "$HOME/.local/bin/git-release"`
Usage
- From the repository root: `./git-release`
- The script creates an annotated tag and pushes it to the configured remote.
- If the script is in your PATH, you can run it directly with `git release`
Dependencies
## Dependencies
- git
License
## License
- See the repository `LICENSE` file.

View File

@@ -2,17 +2,32 @@
# git-release
# https://github.com/obnitram/git-release
# -------------------------------------------------
# Only accepts tags of the form vX.Y.Z (no prerelease/build).
# Usage: git-release [<projectname>]
# Accepts tags of the form vX.Y.Z or <projectname>-vX.Y.Z (no prerelease/build).
# Interactively bumps patch/minor/major, creates an annotated tag,
# and pushes it to origin.
#
# Requirements:
# - The last tag MUST match ^v[0-9]+\.[0-9]+\.[0-9]+$ (or none => starts from v0.0.0)
# - The last tag MUST match ^v[0-9]+\.[0-9]+\.[0-9]+$ or ^<projectname>-v[0-9]+\.[0-9]+\.[0-9]+$ (or none => starts from v0.0.0)
# - Working tree must be clean.
set -euo pipefail
trap 'echo "❌ ${0##*/} failed at line $LINENO." >&2; exit 1' ERR
# --- Parse optional project name argument ---
PROJECT_NAME="${1:-}"
if [[ -n "$PROJECT_NAME" ]]; then
TAG_PREFIX="${PROJECT_NAME}-v"
TAG_PATTERN="${PROJECT_NAME}-v[0-9]*.[0-9]*.[0-9]*"
TAG_REGEX="^${PROJECT_NAME}-v([0-9]+)\.([0-9]+)\.([0-9]+)$"
DEFAULT_TAG="${PROJECT_NAME}-v0.0.0"
else
TAG_PREFIX="v"
TAG_PATTERN="v[0-9]*.[0-9]*.[0-9]*"
TAG_REGEX="^v([0-9]+)\.([0-9]+)\.([0-9]+)$"
DEFAULT_TAG="v0.0.0"
fi
# --- Safety: ensure we are inside a Git repository ---
git rev-parse --is-inside-work-tree >/dev/null 2>&1
@@ -23,14 +38,18 @@ if ! git diff-index --quiet HEAD --; then
fi
# --- Get strictly matching tags and pick the highest (lexicographic by version) ---
# Using Git's glob to only list vX.Y.Z, then version-sort.
LAST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' | sort -V | tail -n1)
CURRENT_TAG=${LAST_TAG:-v0.0.0}
# Using Git's glob to only list matching pattern, then version-sort.
LAST_TAG=$(git tag --list "$TAG_PATTERN" | sort -V | tail -n1)
CURRENT_TAG=${LAST_TAG:-$DEFAULT_TAG}
# --- Validate strict format (vX.Y.Z only) ---
if [[ ! "$CURRENT_TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
echo "❌ Latest tag '$CURRENT_TAG' is not strictly 'vX.Y.Z'."
echo " Please create a proper base tag, e.g.: git tag -a v0.0.0 -m 'init'"
# --- Validate strict format ---
if [[ ! "$CURRENT_TAG" =~ $TAG_REGEX ]]; then
echo "❌ Latest tag '$CURRENT_TAG' does not match expected format."
if [[ -n "$PROJECT_NAME" ]]; then
echo " Please create a proper base tag, e.g.: git tag -a ${PROJECT_NAME}-v0.0.0 -m 'init'"
else
echo " Please create a proper base tag, e.g.: git tag -a v0.0.0 -m 'init'"
fi
exit 1
fi
@@ -51,12 +70,12 @@ select TYPE in "patch" "minor" "major" "cancel"; do
esac
done
# --- Build next tag (still strict vX.Y.Z) ---
NEXT_TAG="v${MAJOR}.${MINOR}.${PATCH}"
# --- Build next tag ---
NEXT_TAG="${TAG_PREFIX}${MAJOR}.${MINOR}.${PATCH}"
# --- Double-check strictness before proceeding ---
if [[ ! "$NEXT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ Computed tag '$NEXT_TAG' is not strictly 'vX.Y.Z'. Aborting."
if [[ ! "$NEXT_TAG" =~ $TAG_REGEX ]]; then
echo "❌ Computed tag '$NEXT_TAG' does not match expected format. Aborting."
exit 1
fi