Skip to content

Commit fb776c5

Browse files
committed
✨ Add GitHub Action for automated release note generation
Introduce action.yml to make Git-Iris available as a reusable GitHub Action that generates AI-powered release notes in CI/CD pipelines. The action supports multiple LLM providers, cross-platform binary downloads, and an optional build-from-source mode for dogfooding unreleased changes. CICD workflow updates: - Add Linux ARM64 build target to release matrix - Refactor release note generation to use the new action with build-from-source for self-testing - Add job to auto-update major version tags (v1, v2) so users can pin to stable major versions Documentation includes action usage examples, input/output reference tables, and supported platform information.
1 parent 45df4e0 commit fb776c5

File tree

4 files changed

+283
-29
lines changed

4 files changed

+283
-29
lines changed

.github/workflows/cicd.yml

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ jobs:
100100
target: x86_64-unknown-linux-gnu
101101
binary_name: git-iris
102102

103+
- build: linux-arm64
104+
os: ubuntu-24.04-arm
105+
target: aarch64-unknown-linux-gnu
106+
binary_name: git-iris
107+
103108
- build: windows-gnu
104109
os: windows-latest
105110
target: x86_64-pc-windows-gnu
@@ -349,34 +354,31 @@ jobs:
349354
ls -la ./release-assets
350355
echo "::endgroup::"
351356
352-
- name: 📝 Generate release notes with git-iris
353-
env:
354-
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
357+
- name: 🏷️ Get previous tag
358+
id: prev_tag
355359
run: |
356-
echo "::group::Generating release notes"
357-
if [ -n "$OPENAI_API_KEY" ]; then
358-
# Build git-iris locally to generate release notes
359-
cargo build --release
360-
361-
# Get previous tag
362-
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
363-
364-
if [ -n "$PREVIOUS_TAG" ]; then
365-
# Generate release notes using git-iris
366-
./target/release/git-iris release-notes --from "$PREVIOUS_TAG" --to "$GITHUB_REF_NAME" --print > RELEASE_NOTES.md
367-
echo "✨ Generated release notes with git-iris"
368-
else
369-
echo "No previous tag found, using default release notes"
370-
echo "# Release $GITHUB_REF_NAME" > RELEASE_NOTES.md
371-
echo "See commit history for changes." >> RELEASE_NOTES.md
372-
fi
373-
else
374-
echo "No OpenAI API key available, using default release notes"
375-
echo "# Release $GITHUB_REF_NAME" > RELEASE_NOTES.md
376-
echo "See commit history for changes." >> RELEASE_NOTES.md
377-
fi
378-
cat RELEASE_NOTES.md
379-
echo "::endgroup::"
360+
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
361+
echo "tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
362+
echo "Previous tag: $PREVIOUS_TAG"
363+
364+
- name: 📝 Generate release notes with Git-Iris
365+
id: release_notes
366+
if: steps.prev_tag.outputs.tag != ''
367+
uses: ./
368+
with:
369+
from: ${{ steps.prev_tag.outputs.tag }}
370+
to: ${{ github.ref_name }}
371+
provider: openai
372+
api-key: ${{ secrets.OPENAI_API_KEY }}
373+
output-file: RELEASE_NOTES.md
374+
build-from-source: "true"
375+
376+
- name: 📝 Fallback release notes
377+
if: steps.prev_tag.outputs.tag == '' || steps.release_notes.outcome == 'failure'
378+
run: |
379+
echo "# Release ${{ github.ref_name }}" > RELEASE_NOTES.md
380+
echo "" >> RELEASE_NOTES.md
381+
echo "See [commit history](https://github.com/hyperb1iss/git-iris/commits/${{ github.ref_name }}) for changes." >> RELEASE_NOTES.md
380382
381383
- name: 🚀 Create GitHub Release
382384
uses: softprops/action-gh-release@v2
@@ -387,3 +389,37 @@ jobs:
387389
files: |
388390
./release-assets/*
389391
body_path: RELEASE_NOTES.md
392+
393+
# Auto-update major version tag (v1, v2, etc.) for GitHub Action users
394+
update-major-tag:
395+
name: 🏷️ Update Major Version Tag
396+
if: startsWith(github.ref, 'refs/tags/v')
397+
needs: create-release
398+
runs-on: ubuntu-latest
399+
permissions:
400+
contents: write
401+
steps:
402+
- name: 📥 Checkout code
403+
uses: actions/checkout@v4
404+
with:
405+
fetch-depth: 0
406+
407+
- name: 🏷️ Update major version tag
408+
run: |
409+
# Extract major version from tag (v1.2.3 -> v1)
410+
FULL_TAG="${GITHUB_REF_NAME}"
411+
MAJOR_TAG=$(echo "$FULL_TAG" | sed -E 's/^(v[0-9]+).*/\1/')
412+
413+
echo "Full tag: $FULL_TAG"
414+
echo "Major tag: $MAJOR_TAG"
415+
416+
# Configure git
417+
git config user.name "github-actions[bot]"
418+
git config user.email "github-actions[bot]@users.noreply.github.com"
419+
420+
# Force update the major version tag
421+
git tag -fa "$MAJOR_TAG" -m "Update $MAJOR_TAG to $FULL_TAG"
422+
git push origin "$MAJOR_TAG" --force
423+
424+
echo "✨ Updated $MAJOR_TAG to point to $FULL_TAG"
425+
echo "Users can now use: uses: hyperb1iss/git-iris@$MAJOR_TAG"

.github/workflows/docs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ on:
44
push:
55
branches: [main]
66
paths:
7-
- 'docs/**'
8-
- '.github/workflows/docs.yml'
7+
- "docs/**"
8+
- ".github/workflows/docs.yml"
99
workflow_dispatch:
1010

1111
permissions:

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[![License](https://img.shields.io/badge/License-Apache%202.0-5E81AC?style=for-the-badge&logo=apache&logoColor=white&color=3B6EA8)](https://opensource.org/licenses/Apache-2.0)
88
[![GitHub Release](https://img.shields.io/github/release/hyperb1iss/git-iris.svg?style=for-the-badge&logo=github&logoColor=white&color=9D6DB3)][releases]
99
[![Crates.io](https://img.shields.io/crates/v/git-iris.svg?style=for-the-badge&logo=rust&logoColor=white&color=D35D47)][crates]
10+
[![GitHub Action](https://img.shields.io/badge/GitHub_Action-Release_Notes-5E81AC?style=for-the-badge&logo=github-actions&logoColor=white)](https://github.com/marketplace/actions/git-iris-release-notes)
1011
[![Rust](https://img.shields.io/badge/rust-stable-EBCB8B?style=for-the-badge&logo=rust&logoColor=white&color=EFBB4D)](https://www.rust-lang.org/)
1112
[![ko-fi](https://img.shields.io/badge/Ko--fi-Support%20Me-A3BE8C?style=for-the-badge&logo=ko-fi&logoColor=white&color=82B062)](https://ko-fi.com/hyperb1iss)
1213

@@ -172,6 +173,56 @@ To build and test the Docker image locally:
172173

173174
For detailed instructions, examples, and CI/CD integration, see our [Docker Usage Guide](docker/README.md).
174175

176+
## 🤖 GitHub Action
177+
178+
Git-Iris is available as a GitHub Action for automated release note generation in your workflows:
179+
180+
```yaml
181+
- name: Generate release notes
182+
uses: hyperb1iss/git-iris@v1
183+
id: release_notes
184+
with:
185+
from: v1.0.0
186+
to: v1.1.0
187+
provider: openai
188+
api-key: ${{ secrets.OPENAI_API_KEY }}
189+
output-file: RELEASE_NOTES.md
190+
191+
- name: Create Release
192+
uses: softprops/action-gh-release@v2
193+
with:
194+
body: ${{ steps.release_notes.outputs.release-notes }}
195+
```
196+
197+
### Action Inputs
198+
199+
| Input | Description | Required | Default |
200+
|-------|-------------|----------|---------|
201+
| `from` | Starting Git reference (tag, commit, branch) | Yes | - |
202+
| `to` | Ending Git reference | No | `HEAD` |
203+
| `provider` | LLM provider (`openai`, `anthropic`, `google`) | No | `openai` |
204+
| `model` | Model to use (provider-specific) | No | Provider default |
205+
| `api-key` | API key for the LLM provider | Yes | - |
206+
| `output-file` | File path to write release notes | No | - |
207+
| `version-name` | Explicit version name for release notes | No | - |
208+
| `custom-instructions` | Custom instructions for generation | No | - |
209+
| `version` | Git-Iris version to use | No | `latest` |
210+
211+
### Action Outputs
212+
213+
| Output | Description |
214+
|--------|-------------|
215+
| `release-notes` | Generated release notes content |
216+
| `release-notes-file` | Path to output file (if specified) |
217+
218+
### Supported Platforms
219+
220+
The action downloads prebuilt binaries for fast execution:
221+
- Linux x64 (`ubuntu-latest`)
222+
- Linux ARM64 (`ubuntu-24.04-arm`)
223+
- macOS ARM64 (`macos-latest`, runs on x64 via Rosetta)
224+
- Windows x64 (`windows-latest`)
225+
175226
## ⚙️ Configuration
176227

177228
Git-Iris offers both global configuration and project-specific configuration options.

action.yml

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
name: "Git-Iris Release Notes"
2+
description: "Generate AI-powered release notes using Git-Iris"
3+
author: "hyperb1iss"
4+
branding:
5+
icon: "file-text"
6+
color: "purple"
7+
8+
inputs:
9+
from:
10+
description: "Starting Git reference (tag, commit, or branch)"
11+
required: true
12+
to:
13+
description: "Ending Git reference (defaults to HEAD)"
14+
required: false
15+
default: "HEAD"
16+
provider:
17+
description: "LLM provider (openai, anthropic, google)"
18+
required: false
19+
default: "openai"
20+
model:
21+
description: "Model to use (provider-specific)"
22+
required: false
23+
api-key:
24+
description: "API key for the LLM provider"
25+
required: true
26+
output-file:
27+
description: "File path to write release notes (optional)"
28+
required: false
29+
version-name:
30+
description: "Explicit version name to use in release notes"
31+
required: false
32+
custom-instructions:
33+
description: "Custom instructions for release note generation"
34+
required: false
35+
version:
36+
description: "Git-Iris version to use (defaults to latest)"
37+
required: false
38+
default: "latest"
39+
build-from-source:
40+
description: "Build from source instead of downloading binary (for dogfooding unreleased changes)"
41+
required: false
42+
default: "false"
43+
44+
outputs:
45+
release-notes:
46+
description: "Generated release notes content"
47+
value: ${{ steps.generate.outputs.release_notes }}
48+
release-notes-file:
49+
description: "Path to the release notes file (if output-file was specified)"
50+
value: ${{ steps.generate.outputs.release_notes_file }}
51+
52+
runs:
53+
using: "composite"
54+
steps:
55+
- name: Determine platform
56+
id: platform
57+
shell: bash
58+
run: |
59+
case "${{ runner.os }}-${{ runner.arch }}" in
60+
Linux-X64)
61+
echo "artifact=git-iris-linux-amd64" >> $GITHUB_OUTPUT
62+
echo "binary=git-iris" >> $GITHUB_OUTPUT
63+
;;
64+
Linux-ARM64)
65+
echo "artifact=git-iris-linux-arm64" >> $GITHUB_OUTPUT
66+
echo "binary=git-iris" >> $GITHUB_OUTPUT
67+
;;
68+
macOS-ARM64|macOS-X64)
69+
# ARM64 binary works on both via Rosetta 2
70+
echo "artifact=git-iris-macos-arm64" >> $GITHUB_OUTPUT
71+
echo "binary=git-iris" >> $GITHUB_OUTPUT
72+
;;
73+
Windows-X64)
74+
echo "artifact=git-iris-windows-gnu" >> $GITHUB_OUTPUT
75+
echo "binary=git-iris.exe" >> $GITHUB_OUTPUT
76+
;;
77+
*)
78+
echo "::error::Unsupported platform: ${{ runner.os }}-${{ runner.arch }}"
79+
exit 1
80+
;;
81+
esac
82+
83+
- name: Download git-iris binary
84+
if: inputs.build-from-source != 'true'
85+
shell: bash
86+
env:
87+
GH_TOKEN: ${{ github.token }}
88+
run: |
89+
echo "::group::Downloading git-iris"
90+
91+
# Determine version to download
92+
if [ "${{ inputs.version }}" = "latest" ]; then
93+
VERSION=$(gh release view --repo hyperb1iss/git-iris --json tagName -q '.tagName')
94+
else
95+
VERSION="${{ inputs.version }}"
96+
fi
97+
98+
echo "Downloading git-iris $VERSION for ${{ steps.platform.outputs.artifact }}"
99+
100+
# Download the artifact
101+
gh release download "$VERSION" \
102+
--repo hyperb1iss/git-iris \
103+
--pattern "${{ steps.platform.outputs.artifact }}" \
104+
--dir /tmp/git-iris-download
105+
106+
# The artifact is downloaded as a directory, binary is inside
107+
chmod +x "/tmp/git-iris-download/${{ steps.platform.outputs.binary }}"
108+
sudo mv "/tmp/git-iris-download/${{ steps.platform.outputs.binary }}" /usr/local/bin/git-iris
109+
110+
echo "git-iris installed successfully"
111+
git-iris --version
112+
echo "::endgroup::"
113+
114+
- name: Build git-iris from source
115+
if: inputs.build-from-source == 'true'
116+
shell: bash
117+
run: |
118+
echo "::group::Building git-iris from source"
119+
cargo build --release --locked
120+
echo "GIT_IRIS_BIN=./target/release/git-iris" >> $GITHUB_ENV
121+
echo "::endgroup::"
122+
123+
- name: Generate release notes
124+
id: generate
125+
shell: bash
126+
env:
127+
OPENAI_API_KEY: ${{ inputs.provider == 'openai' && inputs.api-key || '' }}
128+
ANTHROPIC_API_KEY: ${{ inputs.provider == 'anthropic' && inputs.api-key || '' }}
129+
GOOGLE_API_KEY: ${{ inputs.provider == 'google' && inputs.api-key || '' }}
130+
IRIS_PROVIDER: ${{ inputs.provider }}
131+
IRIS_MODEL: ${{ inputs.model }}
132+
IRIS_INSTRUCTIONS: ${{ inputs.custom-instructions }}
133+
run: |
134+
echo "::group::Generating release notes"
135+
136+
# Use built binary or installed command
137+
GIT_IRIS="${GIT_IRIS_BIN:-git-iris}"
138+
139+
# Build command
140+
CMD="$GIT_IRIS release-notes --from '${{ inputs.from }}' --to '${{ inputs.to }}' --raw"
141+
142+
# Add version name if specified
143+
if [ -n "${{ inputs.version-name }}" ]; then
144+
CMD="$CMD --version-name '${{ inputs.version-name }}'"
145+
fi
146+
147+
# Execute and capture output
148+
RELEASE_NOTES=$(eval $CMD)
149+
150+
# Handle multiline output for GitHub Actions
151+
echo "release_notes<<EOF" >> $GITHUB_OUTPUT
152+
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
153+
echo "EOF" >> $GITHUB_OUTPUT
154+
155+
# Write to file if specified
156+
if [ -n "${{ inputs.output-file }}" ]; then
157+
echo "$RELEASE_NOTES" > "${{ inputs.output-file }}"
158+
echo "release_notes_file=${{ inputs.output-file }}" >> $GITHUB_OUTPUT
159+
echo "Release notes written to ${{ inputs.output-file }}"
160+
fi
161+
162+
echo "::endgroup::"
163+
164+
# Print preview
165+
echo "::group::Release Notes Preview"
166+
echo "$RELEASE_NOTES"
167+
echo "::endgroup::"

0 commit comments

Comments
 (0)