Skip to content

Commit 2a24eb3

Browse files
committed
feat: add release workflow and changelog generation
1 parent b8536cf commit 2a24eb3

4 files changed

Lines changed: 213 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*"
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Setup Go
16+
uses: actions/setup-go@v5
17+
with:
18+
go-version-file: go.mod
19+
20+
- name: Generate Changelog
21+
run: make changelog-current
22+
23+
- name: Build Kustomize Manifests
24+
run: |
25+
# Extract version from tag
26+
VERSION=${GITHUB_REF#refs/tags/}
27+
# Build installer with version tag
28+
make build-installer IMG=ghcr.io/${{ github.repository }}:${VERSION}
29+
30+
- name: Release
31+
uses: softprops/action-gh-release@v2
32+
with:
33+
body_path: CHANGELOG-current.md
34+
files: |
35+
dist/install.yaml

Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ KUSTOMIZE ?= $(LOCALBIN)/kustomize
194194
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
195195
ENVTEST ?= $(LOCALBIN)/setup-envtest
196196
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
197+
GIT_CLIFF ?= $(LOCALBIN)/git-cliff
197198

198199
## Tool Versions
199200
KUSTOMIZE_VERSION ?= v5.6.0
@@ -203,6 +204,7 @@ ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller
203204
#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
204205
ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}')
205206
GOLANGCI_LINT_VERSION ?= v2.3.0
207+
GIT_CLIFF_VERSION ?= 2.8.0
206208

207209
.PHONY: kustomize
208210
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
@@ -232,6 +234,30 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
232234
$(GOLANGCI_LINT): $(LOCALBIN)
233235
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
234236

237+
.PHONY: git-cliff
238+
git-cliff: $(GIT_CLIFF) ## Download git-cliff locally if necessary.
239+
$(GIT_CLIFF): $(LOCALBIN)
240+
@[ -f "$(GIT_CLIFF)-$(GIT_CLIFF_VERSION)" ] || { \
241+
echo "Downloading git-cliff $(GIT_CLIFF_VERSION)..."; \
242+
case "$(shell uname -s)-$(shell uname -m)" in \
243+
"Darwin-arm64") \
244+
curl -L "https://github.com/orhun/git-cliff/releases/download/v$(GIT_CLIFF_VERSION)/git-cliff-$(GIT_CLIFF_VERSION)-aarch64-apple-darwin.tar.gz" | \
245+
tar xz -C $(LOCALBIN) --strip-components=1 git-cliff-$(GIT_CLIFF_VERSION)/git-cliff ;; \
246+
"Darwin-x86_64") \
247+
curl -L "https://github.com/orhun/git-cliff/releases/download/v$(GIT_CLIFF_VERSION)/git-cliff-$(GIT_CLIFF_VERSION)-x86_64-apple-darwin.tar.gz" | \
248+
tar xz -C $(LOCALBIN) --strip-components=1 git-cliff-$(GIT_CLIFF_VERSION)/git-cliff ;; \
249+
"Linux-x86_64") \
250+
curl -L "https://github.com/orhun/git-cliff/releases/download/v$(GIT_CLIFF_VERSION)/git-cliff-$(GIT_CLIFF_VERSION)-x86_64-unknown-linux-gnu.tar.gz" | \
251+
tar xz -C $(LOCALBIN) --strip-components=1 git-cliff-$(GIT_CLIFF_VERSION)/git-cliff ;; \
252+
"Linux-aarch64") \
253+
curl -L "https://github.com/orhun/git-cliff/releases/download/v$(GIT_CLIFF_VERSION)/git-cliff-$(GIT_CLIFF_VERSION)-aarch64-unknown-linux-gnu.tar.gz" | \
254+
tar xz -C $(LOCALBIN) --strip-components=1 git-cliff-$(GIT_CLIFF_VERSION)/git-cliff ;; \
255+
*) echo "Unsupported platform: $(shell uname -s)-$(shell uname -m)" && exit 1 ;; \
256+
esac; \
257+
mv $(GIT_CLIFF) $(GIT_CLIFF)-$(GIT_CLIFF_VERSION); \
258+
} ;\
259+
ln -sf $(GIT_CLIFF)-$(GIT_CLIFF_VERSION) $(GIT_CLIFF)
260+
235261
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
236262
# $1 - target path with name of binary
237263
# $2 - package url which can be installed
@@ -247,3 +273,12 @@ mv $(1) $(1)-$(3) ;\
247273
} ;\
248274
ln -sf $$(realpath $(1)-$(3)) $(1)
249275
endef
276+
277+
# Release notes generation
278+
.PHONY: changelog
279+
changelog: git-cliff ## Generate changelog
280+
$(GIT_CLIFF) --tag $(VERSION) --output CHANGELOG.md
281+
282+
.PHONY: changelog-current
283+
changelog-current: git-cliff ## Generate changelog for the current release
284+
$(GIT_CLIFF) --latest --strip all --output CHANGELOG-current.md

cliff.toml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# git-cliff configuration
2+
# https://git-cliff.org/docs/configuration
3+
4+
[changelog]
5+
# changelog header
6+
header = """
7+
# Changelog
8+
9+
All notable changes to this project will be documented in this file.
10+
11+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
12+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
13+
"""
14+
# template for the changelog body
15+
body = """
16+
{% if version %}\
17+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
18+
{% else %}\
19+
## [Unreleased]
20+
{% endif %}\
21+
{% for group, commits in commits | group_by(attribute="group") %}
22+
### {{ group | upper_first }}
23+
{% for commit in commits %}
24+
- {{ commit.message | upper_first }}\
25+
{% if commit.breaking %}\
26+
- BREAKING CHANGE: {{ commit.breaking_description | default(value="") | upper_first }}\
27+
{% endif %}
28+
{% endfor %}
29+
{% endfor %}
30+
"""
31+
# remove the leading and trailing whitespaces from the template
32+
trim = true
33+
# changelog footer
34+
footer = """
35+
<!-- generated by git-cliff -->
36+
"""
37+
38+
[git]
39+
# parse the commits based on https://www.conventionalcommits.org
40+
conventional_commits = true
41+
# filter out the commits that are not conventional
42+
filter_unconventional = true
43+
# process each line of a commit as an individual commit
44+
split_commits = false
45+
# regex for preprocessing the commit messages
46+
commit_parsers = [
47+
{ message = "^feat", group = "Added" },
48+
{ message = "^fix", group = "Fixed" },
49+
{ message = "^docs", group = "Documentation" },
50+
{ message = "^style", group = "Style" },
51+
{ message = "^refactor", group = "Refactored" },
52+
{ message = "^perf", group = "Performance" },
53+
{ message = "^test", group = "Tests" },
54+
{ message = "^build", group = "Build" },
55+
{ message = "^ci", group = "CI" },
56+
{ message = "^chore", group = "Chore" },
57+
{ message = "^revert", group = "Reverted" },
58+
]
59+
# protect breaking changes from being skipped due to matching a skipping commit_parser
60+
protect_breaking_commits = false
61+
# filter out the commits that are not matched by commit parsers
62+
filter_commits = false
63+
# glob pattern for matching git tags
64+
tag_pattern = "v[0-9]*"
65+
# regex for skipping tags
66+
skip_tags = ""
67+
# regex for ignoring tags
68+
ignore_tags = ""
69+
# sort the tags topologically
70+
topo_order = false
71+
# sort the commits inside sections by oldest/newest order
72+
sort_commits = "oldest"
73+
74+
[preprocessor]
75+
# regex for preprocessing the commit messages
76+
preprocessors = [
77+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/kuberik/environment-controller/issues/${2}))" },
78+
]
79+
80+
[postprocessor]
81+
# regex for postprocessing the commit messages
82+
postprocessors = [
83+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/kuberik/environment-controller/issues/${2}))" },
84+
]

hack/release.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
# Get the current branch
6+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
7+
8+
# Get the latest tag
9+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
10+
11+
# Split version into components
12+
IFS='.' read -r -a VERSION_PARTS <<< "${LATEST_TAG#v}"
13+
MAJOR="${VERSION_PARTS[0]}"
14+
MINOR="${VERSION_PARTS[1]}"
15+
PATCH="${VERSION_PARTS[2]}"
16+
17+
# Determine version increment based on branch
18+
if [[ "$BRANCH" == "main" ]]; then
19+
# On main branch, increment minor version
20+
NEW_VERSION="v${MAJOR}.$((MINOR + 1)).0"
21+
RELEASE_BRANCH="release-${MAJOR}.$((MINOR + 1))"
22+
elif [[ "$BRANCH" =~ ^release- ]]; then
23+
# On release branch, increment patch version
24+
NEW_VERSION="v${MAJOR}.${MINOR}.$((PATCH + 1)}"
25+
else
26+
echo "Error: Must be on main or release-* branch to create a release"
27+
exit 1
28+
fi
29+
30+
echo "Current version: ${LATEST_TAG}"
31+
echo "New version: ${NEW_VERSION}"
32+
33+
(
34+
cd config/manager
35+
kustomize edit set image controller=ghcr.io/kuberik/environment-controller:${NEW_VERSION}
36+
)
37+
38+
# Generate changelog
39+
echo "Generating changelog..."
40+
make changelog VERSION="${NEW_VERSION}"
41+
42+
# Create git tag
43+
git add CHANGELOG.md config/manager/kustomization.yaml
44+
git commit -m "chore: release version ${NEW_VERSION}"
45+
git tag -a "${NEW_VERSION}" -m "Release ${NEW_VERSION}"
46+
47+
# For minor releases on main, create a release branch
48+
if [[ "$BRANCH" == "main" ]]; then
49+
git checkout -b "${RELEASE_BRANCH}"
50+
echo "Created release branch: ${RELEASE_BRANCH}"
51+
fi
52+
53+
echo "Created release ${NEW_VERSION}"
54+
echo "To push the changes, run:"
55+
if [[ "$BRANCH" == "main" ]]; then
56+
echo "git push origin main"
57+
echo "git push origin ${RELEASE_BRANCH}"
58+
fi
59+
echo "git push origin ${NEW_VERSION}"

0 commit comments

Comments
 (0)