Skip to content

Commit 11ae213

Browse files
FEATURE (versions): Add versioning
1 parent e0f125a commit 11ae213

File tree

4 files changed

+248
-1
lines changed

4 files changed

+248
-1
lines changed

.github/workflows/docker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080
cd frontend
8181
npm run lint
8282
83-
docker:
83+
build-and-push:
8484
runs-on: ubuntu-latest
8585
needs: [lint-backend, lint-frontend]
8686

.github/workflows/release.yml

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
name: Automated Release
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
if: ${{ !contains(github.event.head_commit.message, '[skip-release]') }}
12+
13+
steps:
14+
- name: Check out code
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
token: ${{ secrets.GITHUB_TOKEN }}
19+
20+
- name: Set up Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: "20"
24+
25+
- name: Install dependencies
26+
run: |
27+
npm install -g conventional-changelog-cli
28+
npm install -g semver
29+
30+
- name: Get current version
31+
id: current_version
32+
run: |
33+
# Get the latest tag, default to 0.0.0 if no tags exist
34+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
35+
echo "current_version=${LATEST_TAG#v}" >> $GITHUB_OUTPUT
36+
echo "Current version: ${LATEST_TAG#v}"
37+
38+
- name: Analyze commits and determine version bump
39+
id: version_bump
40+
run: |
41+
CURRENT_VERSION="${{ steps.current_version.outputs.current_version }}"
42+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
43+
44+
# Get commits since last tag
45+
if [ "$LATEST_TAG" = "v0.0.0" ]; then
46+
COMMITS=$(git log --pretty=format:"%s" --no-merges)
47+
else
48+
COMMITS=$(git log ${LATEST_TAG}..HEAD --pretty=format:"%s" --no-merges)
49+
fi
50+
51+
echo "Analyzing commits:"
52+
echo "$COMMITS"
53+
54+
# Initialize flags
55+
HAS_FEATURE=false
56+
HAS_FIX=false
57+
HAS_BREAKING=false
58+
59+
# Analyze each commit
60+
while IFS= read -r commit; do
61+
if [[ "$commit" =~ ^FEATURE ]]; then
62+
HAS_FEATURE=true
63+
echo "Found FEATURE commit: $commit"
64+
elif [[ "$commit" =~ ^FIX ]]; then
65+
HAS_FIX=true
66+
echo "Found FIX commit: $commit"
67+
elif [[ "$commit" =~ ^REFACTOR ]]; then
68+
HAS_FIX=true # Treat refactor as patch
69+
echo "Found REFACTOR commit: $commit"
70+
fi
71+
72+
# Check for breaking changes
73+
if [[ "$commit" =~ BREAKING[[:space:]]CHANGE ]] || [[ "$commit" =~ "!" ]]; then
74+
HAS_BREAKING=true
75+
echo "Found BREAKING CHANGE: $commit"
76+
fi
77+
done <<< "$COMMITS"
78+
79+
# Determine version bump
80+
if [ "$HAS_BREAKING" = true ]; then
81+
BUMP_TYPE="major"
82+
elif [ "$HAS_FEATURE" = true ]; then
83+
BUMP_TYPE="minor"
84+
elif [ "$HAS_FIX" = true ]; then
85+
BUMP_TYPE="patch"
86+
else
87+
BUMP_TYPE="none"
88+
fi
89+
90+
echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT
91+
92+
if [ "$BUMP_TYPE" != "none" ]; then
93+
NEW_VERSION=$(npx semver -i $BUMP_TYPE $CURRENT_VERSION)
94+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
95+
echo "should_release=true" >> $GITHUB_OUTPUT
96+
echo "New version will be: $NEW_VERSION"
97+
else
98+
echo "should_release=false" >> $GITHUB_OUTPUT
99+
echo "No version bump needed"
100+
fi
101+
102+
- name: Generate changelog
103+
id: changelog
104+
if: steps.version_bump.outputs.should_release == 'true'
105+
run: |
106+
CURRENT_VERSION="${{ steps.current_version.outputs.current_version }}"
107+
NEW_VERSION="${{ steps.version_bump.outputs.new_version }}"
108+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
109+
110+
# Get commits since last tag
111+
if [ "$LATEST_TAG" = "v0.0.0" ]; then
112+
COMMITS=$(git log --pretty=format:"%s|||%H|||%an|||%ad" --date=short --no-merges)
113+
else
114+
COMMITS=$(git log ${LATEST_TAG}..HEAD --pretty=format:"%s|||%H|||%an|||%ad" --date=short --no-merges)
115+
fi
116+
117+
# Create changelog
118+
CHANGELOG="# Changelog\n\n## [${NEW_VERSION}] - $(date +%Y-%m-%d)\n\n"
119+
120+
# Group commits by type and area
121+
FEATURES=""
122+
FIXES=""
123+
REFACTORS=""
124+
125+
while IFS= read -r line; do
126+
if [ -n "$line" ]; then
127+
COMMIT_MSG=$(echo "$line" | cut -d'|||' -f1)
128+
COMMIT_HASH=$(echo "$line" | cut -d'|||' -f2)
129+
COMMIT_AUTHOR=$(echo "$line" | cut -d'|||' -f3)
130+
COMMIT_DATE=$(echo "$line" | cut -d'|||' -f4)
131+
SHORT_HASH=${COMMIT_HASH:0:7}
132+
133+
if [[ "$COMMIT_MSG" =~ ^FEATURE[[:space:]]*\(([^)]+)\):[[:space:]]*(.*) ]]; then
134+
AREA="${BASH_REMATCH[1]}"
135+
DESC="${BASH_REMATCH[2]}"
136+
FEATURES="${FEATURES}- **${AREA}**: ${DESC} ([${SHORT_HASH}](https://github.com/${{ github.repository }}/commit/${COMMIT_HASH}))\n"
137+
elif [[ "$COMMIT_MSG" =~ ^FIX[[:space:]]*\(([^)]+)\):[[:space:]]*(.*) ]]; then
138+
AREA="${BASH_REMATCH[1]}"
139+
DESC="${BASH_REMATCH[2]}"
140+
FIXES="${FIXES}- **${AREA}**: ${DESC} ([${SHORT_HASH}](https://github.com/${{ github.repository }}/commit/${COMMIT_HASH}))\n"
141+
elif [[ "$COMMIT_MSG" =~ ^REFACTOR[[:space:]]*\(([^)]+)\):[[:space:]]*(.*) ]]; then
142+
AREA="${BASH_REMATCH[1]}"
143+
DESC="${BASH_REMATCH[2]}"
144+
REFACTORS="${REFACTORS}- **${AREA}**: ${DESC} ([${SHORT_HASH}](https://github.com/${{ github.repository }}/commit/${COMMIT_HASH}))\n"
145+
fi
146+
fi
147+
done <<< "$COMMITS"
148+
149+
# Build changelog sections
150+
if [ -n "$FEATURES" ]; then
151+
CHANGELOG="${CHANGELOG}### ✨ Features\n${FEATURES}\n"
152+
fi
153+
154+
if [ -n "$FIXES" ]; then
155+
CHANGELOG="${CHANGELOG}### 🐛 Bug Fixes\n${FIXES}\n"
156+
fi
157+
158+
if [ -n "$REFACTORS" ]; then
159+
CHANGELOG="${CHANGELOG}### 🔨 Refactoring\n${REFACTORS}\n"
160+
fi
161+
162+
# Save changelog to file
163+
echo -e "$CHANGELOG" > RELEASE_CHANGELOG.md
164+
165+
# Update main CHANGELOG.md
166+
if [ -f "CHANGELOG.md" ]; then
167+
# Create new changelog content
168+
echo -e "$CHANGELOG" > NEW_CHANGELOG.md
169+
# Get existing changelog content after the [Unreleased] section
170+
sed -n '/## \[Unreleased\]/,$p' CHANGELOG.md | tail -n +3 >> NEW_CHANGELOG.md
171+
# Replace the original file
172+
mv NEW_CHANGELOG.md CHANGELOG.md
173+
else
174+
echo -e "$CHANGELOG" > CHANGELOG.md
175+
fi
176+
177+
# Set output for GitHub release (escape newlines)
178+
{
179+
echo 'changelog<<EOF'
180+
echo -e "$CHANGELOG"
181+
echo EOF
182+
} >> $GITHUB_OUTPUT
183+
184+
- name: Create Git tag
185+
if: steps.version_bump.outputs.should_release == 'true'
186+
run: |
187+
NEW_VERSION="${{ steps.version_bump.outputs.new_version }}"
188+
git config user.name "github-actions[bot]"
189+
git config user.email "github-actions[bot]@users.noreply.github.com"
190+
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
191+
git push origin "v${NEW_VERSION}"
192+
193+
- name: Create GitHub Release
194+
if: steps.version_bump.outputs.should_release == 'true'
195+
uses: actions/create-release@v1
196+
env:
197+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
198+
with:
199+
tag_name: v${{ steps.version_bump.outputs.new_version }}
200+
release_name: Release v${{ steps.version_bump.outputs.new_version }}
201+
body: ${{ steps.changelog.outputs.changelog }}
202+
draft: false
203+
prerelease: false
204+
205+
- name: Update version files and changelog
206+
if: steps.version_bump.outputs.should_release == 'true'
207+
run: |
208+
NEW_VERSION="${{ steps.version_bump.outputs.new_version }}"
209+
cd frontend
210+
npm version $NEW_VERSION --no-git-tag-version
211+
cd ..
212+
git config user.name "github-actions[bot]"
213+
git config user.email "github-actions[bot]@users.noreply.github.com"
214+
git add frontend/package.json frontend/package-lock.json CHANGELOG.md
215+
git commit -m "chore: bump version to ${NEW_VERSION} [skip-release]" || true
216+
git push origin main || true

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
<!-- This file is automatically updated by the release workflow -->

contribute/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,27 @@ Before any commit, make sure:
4141
3. All tests are passing
4242
4. Project is building successfully
4343

44+
### Automated Versioning
45+
46+
This project uses automated versioning based on commit messages:
47+
48+
- **FEATURE (area)**: Creates a **minor** version bump (e.g., 1.0.0 → 1.1.0)
49+
- **FIX (area)**: Creates a **patch** version bump (e.g., 1.0.0 → 1.0.1)
50+
- **REFACTOR (area)**: Creates a **patch** version bump (e.g., 1.0.0 → 1.0.1)
51+
- **BREAKING CHANGE**: Creates a **major** version bump (e.g., 1.0.0 → 2.0.0)
52+
53+
The system automatically:
54+
55+
- Analyzes commits since the last release
56+
- Determines the appropriate version bump
57+
- Generates a changelog grouped by area (frontend/backend/etc.)
58+
- Creates GitHub releases with detailed release notes
59+
- Updates package.json version numbers
60+
61+
To skip automated release (for documentation updates, etc.), add `[skip-release]` to your commit message.
62+
63+
### Docs
64+
4465
If you need to add some explanation, do it in appropriate place in the code. Or in the /docs folder if it is something general. For charts, use Mermaid.
4566

4667
### Priorities

0 commit comments

Comments
 (0)