diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md
new file mode 100755
index 0000000..389e978
--- /dev/null
+++ b/.chglog/CHANGELOG.tpl.md
@@ -0,0 +1,49 @@
+{{ if .Versions -}}
+
+## [Unreleased]
+
+{{ if .Unreleased.CommitGroups -}}
+{{ range .Unreleased.CommitGroups -}}
+### {{ .Title }}
+{{ range .Commits -}}
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
+{{ end }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
+
+{{ range .Versions }}
+
+## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
+{{ range .CommitGroups -}}
+### {{ .Title }}
+{{ range .Commits -}}
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
+{{ end }}
+{{ end -}}
+
+{{- if .RevertCommits -}}
+### Reverts
+{{ range .RevertCommits -}}
+- {{ .Revert.Header }}
+{{ end }}
+{{ end -}}
+
+{{- if .NoteGroups -}}
+{{ range .NoteGroups -}}
+### {{ .Title }}
+{{ range .Notes }}
+{{ .Body }}
+{{ end }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
+
+{{- if .Versions }}
+[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
+{{ range .Versions -}}
+{{ if .Tag.Previous -}}
+[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
\ No newline at end of file
diff --git a/.chglog/config.yml b/.chglog/config.yml
new file mode 100755
index 0000000..a498033
--- /dev/null
+++ b/.chglog/config.yml
@@ -0,0 +1,43 @@
+style: github
+template: CHANGELOG.tpl.md
+info:
+ title: CHANGELOG
+ repository_url: https://github.com/grafana/grafana-build-tools
+options:
+ tag_filter_pattern: '^v'
+ sort: "date"
+
+ header:
+ # The format for the subject line: `: `
+ # The list of recognized types is specified below.
+ pattern: "^(\\w*)\\:\\s(.*)$"
+ pattern_maps:
+ - Type
+ - Subject
+
+ commits:
+ filters:
+ Type: # types to include in changelog, anything else is not added
+ - feat
+ - fix
+ - perf
+
+ commit_groups:
+ group_by: Type
+ title_maps: # How to present types in the changelog
+ feat: Features
+ fix: Bug Fixes
+ perf: Performance Improvements
+
+ issues:
+ prefix:
+ - '#'
+
+ refs:
+ actions:
+ - Closes
+ - Fixes
+
+ notes:
+ keywords:
+ - BREAKING CHANGE
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
diff --git a/Dockerfile b/Dockerfile
index 89dc84b..9177d40 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -50,6 +50,9 @@ FROM registry.hub.docker.com/library/golang:1.21.3 as go-build
# Add shellcheck
COPY --from=registry.hub.docker.com/koalaman/shellcheck:v0.9.0 /bin/shellcheck /build
+ # Add git-chglog
+ RUN env GOBIN=/build go install github.com/git-chglog/git-chglog/cmd/git-chglog@v0.15.4
+
FROM registry.hub.docker.com/library/debian:stable-slim as build
COPY --from=go-build /usr/local/go /usr/local/go
diff --git a/Makefile b/Makefile
index 479d964..3c020a8 100644
--- a/Makefile
+++ b/Makefile
@@ -5,10 +5,11 @@ S := @
ROOTDIR := $(abspath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
IMAGE_REPO := ghcr.io/grafana
-IMAGE_NAME := build-tools
+IMAGE_NAME := grafana-build-tools
IMAGE_TAG ?= $(shell $(ROOTDIR)/scripts/image-tag)
IMAGE := $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_TAG)
LATEST_IMAGE := $(IMAGE_REPO)/$(IMAGE_NAME):latest
+LOCAL_IMAGE := $(IMAGE_REPO)/$(IMAGE_NAME):local
LOCAL_ARCH := $(strip $(shell $(ROOTDIR)/scripts/local-machine))
@@ -36,6 +37,12 @@ endef
$(foreach BUILD_ARCH,$(PUSH_ARCHES),$(eval $(call build-target,$(BUILD_ARCH))))
+.PHONY: build-local
+build-local:
+ docker build . -f Dockerfile -t "$(LOCAL_IMAGE)"
+
+BUILD_TARGETS += build-local
+
.PHONY: build
build: $(BUILD_TARGETS)
build: ## build the image
diff --git a/lib/image-test b/lib/image-test
index f143f27..5a484ea 100755
--- a/lib/image-test
+++ b/lib/image-test
@@ -54,6 +54,9 @@ golangci-lint version
echo '=== shellcheck'
shellcheck --version
+echo '=== git-chglog'
+git-chglog --help
+
echo '=== gcc'
gcc --version
diff --git a/scripts/release b/scripts/release
new file mode 100755
index 0000000..a2be58d
--- /dev/null
+++ b/scripts/release
@@ -0,0 +1,140 @@
+#!/bin/sh
+
+set -e
+set -u
+
+gbt_image=ghcr.io/grafana/grafana-build-tools:local
+
+for req in git docker ; do
+ if ! command -v "${req}" > /dev/null 2>&1 ; then
+ echo "E: '${req}' is needed to run this script. Abort."
+ exit 1
+ fi
+done
+
+next_version=${1:-}
+
+cd "$(git rev-parse --show-toplevel)" || exit 2
+
+if test ! -e CHANGELOG.md ; then
+ echo "E: Expecting a CHANGELOG.md file in $PWD, none found. Abort."
+ exit 3
+fi
+
+current_version() {
+ git describe --tags HEAD | cut -d- -f1 | tr -d v
+}
+
+next_version_patch() {
+ parts=$(current_version)
+ major=$(echo "${parts}" | cut -d. -f1)
+ minor=$(echo "${parts}" | cut -d. -f2)
+ patch=$(echo "${parts}" | cut -d. -f3)
+ echo "${major}.${minor}.$((patch+1))"
+}
+
+next_version_minor() {
+ parts=$(current_version)
+ major=$(echo "${parts}" | cut -d. -f1)
+ minor=$(echo "${parts}" | cut -d. -f2)
+ echo "${major}.$((minor+1)).0"
+}
+
+next_version_major() {
+ parts=$(current_version)
+ major=$(echo "${parts}" | cut -d. -f1)
+ echo "$((major+1)).0.0"
+}
+
+# Does the most recent commit look like a release?
+head_is_release=false
+git log --format=%s --max-count=1 | grep -q -E "^Release v[0-9]+\.[0-9]+\.[0-9]+( \(#[0-9]+\))?\$" && head_is_release=true
+
+if test "${head_is_release}" = false -a -z "${next_version}" ; then
+ cv=$(current_version)
+ next_patch=$(next_version_patch)
+ next_minor=$(next_version_minor)
+ next_major=$(next_version_major)
+ cat <<-EOT
+ I: Current version: v${cv}
+ I: Next fix: v${next_patch}
+ I: Next feature: v${next_minor}
+ I: Next breaking change: v${next_major}
+
+ I: Changes since current version:
+
+ EOT
+
+ git --no-pager log --pretty=tformat:' %C(auto)%h %d %s' "v${cv}...HEAD"
+
+ cat <<-EOT
+
+ E: Next version argument required. Abort.
+ EOT
+ exit 4
+fi
+
+commit_msg=$(mktemp)
+
+cleanup() {
+ rm -f "${commit_msg}"
+}
+
+trap cleanup EXIT
+
+if ${head_is_release} ; then
+ # We have a release, tag it.
+
+ # First extract the version from the most recent commit. We know we
+ # have this because we tested for it above.
+ next_version=$(git log --format=%s --max-count=1 | sed -e 's,^Release \(\S\+\).*,\1,')
+
+ if test -z "${next_version}" ; then
+ echo 'E: Unexpected condition looking for the next version. Abort.'
+ exit 127
+ fi
+
+ # Then extract the commit message from the most recent commit.
+ git show --pretty=format:"%B" --no-patch > "${commit_msg}"
+
+ # Then create a tag with the next version.
+ git tag --annotate --file="${commit_msg}" "${next_version}"
+
+ cat <<-EOT
+ An annotated tag has been created for version ${next_version}.
+
+ You must push this tag to the remote repository in order to trigger the
+ release process.
+ EOT
+else
+ # Need to create release notes.
+ cur_version=$(git describe --tags | cut -d- -f1)
+
+ if ! docker image inspect "${gbt_image}" > /dev/null 2>&1 ; then
+ make build-local
+ fi
+
+ docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" --workdir /work "${gbt_image}" \
+ git chglog --next-tag "${next_version}" > CHANGELOG.md
+
+ git add CHANGELOG.md
+
+ git switch --create "release-${next_version}"
+
+ cat > "${commit_msg}" <<-EOT
+ Release ${next_version}
+
+ $(git log --oneline --reverse "${cur_version}".. | cut -d' ' -f2- | sed -e 's,^,* ,')
+ EOT
+
+ git commit --signoff --file="${commit_msg}"
+
+ cat <<-EOT
+
+ A commit has been created to update the CHANGELOG.md file and prepare a
+ release for version ${next_version}.
+
+ Please open a PR and once that is merged, run this script again with the
+ same version number as argument.
+ EOT
+fi