Skip to content

chore: version 1.8.0 #11

chore: version 1.8.0

chore: version 1.8.0 #11

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: read
defaults:
run:
shell: bash
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
build-and-package:
runs-on: ubuntu-24.04
container:
image: ubuntu:24.04@sha256:023f8a753c22258c9fe2d0005a7d28258038da7d620e9f93e9ad78aa266f9f11 # ubuntu:24.04 linux/amd64
env:
NFPM_VERSION: v2.46.3
GO_VERSION: 1.25.0 # nFPM v2.46.3 go directive
GOTOOLCHAIN: local
RELEASE_GLIBC_VERSION: 2.39
MIN_PIPEWIRE_VERSION: 0.3.65
outputs:
version: ${{ steps.release.outputs.version }}
package_release: ${{ steps.release.outputs.package_release }}
display_version: ${{ steps.release.outputs.display_version }}
tag: ${{ steps.release.outputs.tag }}
commit: ${{ steps.release.outputs.commit }}
steps:
- name: Install workflow prerequisites
run: |
set -euo pipefail
apt-get update
apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
git \
libclang-dev \
libpipewire-0.3-dev \
libwayland-dev \
libx11-dev \
libx11-xcb-dev \
libxext-dev \
libxfixes-dev \
libxrandr-dev \
libxcursor-dev \
libxi-dev \
libxinerama-dev \
libxkbcommon-dev \
libfontconfig1-dev \
libfreetype6-dev \
libvulkan-dev \
pkg-config \
xz-utils \
zstd
rm -rf /var/lib/apt/lists/*
- name: Validate native dependency versions
run: |
set -euo pipefail
glibc_version="$(ldd --version | awk 'NR==1 {print $NF}')"
pipewire_version="$(pkg-config --modversion libpipewire-0.3)"
echo "glibc version: ${glibc_version}"
echo "PipeWire/SPA development headers: ${pipewire_version}"
if [ "${glibc_version}" != "${RELEASE_GLIBC_VERSION}" ]; then
echo "Release pipeline expected glibc ${RELEASE_GLIBC_VERSION}; got ${glibc_version}" >&2
exit 1
fi
if ! dpkg --compare-versions "${pipewire_version}" ge "${MIN_PIPEWIRE_VERSION}"; then
echo "Release pipeline requires PipeWire/SPA development headers >= ${MIN_PIPEWIRE_VERSION}" >&2
exit 1
fi
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false
- name: Mark workspace as safe for Git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Validate release tag and prepare notes
id: release
env:
TAG_NAME: ${{ github.ref_name }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
cargo_value() {
local key="$1"
awk -F'"' -v key="$key" '
/^[[:space:]]*\[package\][[:space:]]*$/ { in_pkg = 1; next }
/^[[:space:]]*\[/ { in_pkg = 0 }
in_pkg && $0 ~ "^[[:space:]]*" key "[[:space:]]*=" { print $2; exit }
'
}
version="$(cargo_value version < Cargo.toml)"
if [ -z "$version" ]; then
echo "Could not parse package.version from Cargo.toml" >&2
exit 1
fi
tag="${TAG_NAME}"
tag_body="${tag#v}"
package_release="1"
display_version="${version}"
if [ "$tag_body" = "$version" ]; then
:
elif [[ "$tag_body" == "${version}-"* ]]; then
package_release="${tag_body#"${version}-"}"
if [[ ! "$package_release" =~ ^[1-9][0-9]*$ ]]; then
echo "Release tag ${tag} has invalid package release suffix; expected v${version}-<positive integer>." >&2
exit 1
fi
display_version="${version}-${package_release}"
else
echo "Release tag ${tag} does not match Cargo.toml package.version (${version}); expected v${version} or v${version}-<package release>." >&2
exit 1
fi
head_commit="$(git rev-parse HEAD)"
tag_commit="$(git rev-parse "${tag}^{commit}")"
if [ "$tag_commit" != "$head_commit" ]; then
echo "Release tag ${tag} points at ${tag_commit}, but checkout is ${head_commit}." >&2
exit 1
fi
default_branch="${DEFAULT_BRANCH:-main}"
default_branch_ref="origin/${default_branch}"
if ! git rev-parse --verify "${default_branch_ref}^{commit}" >/dev/null; then
echo "Default branch ref ${default_branch_ref} is unavailable from checkout." >&2
exit 1
fi
if ! git merge-base --is-ancestor "$head_commit" "$default_branch_ref"; then
echo "Release commit ${head_commit} is not contained in ${default_branch_ref}." >&2
exit 1
fi
rust_toolchain="$(cargo_value rust-version < Cargo.toml)"
rust_toolchain="${rust_toolchain:-stable}"
previous_tag="$(git tag --merged "$head_commit" --sort=-v:refname --list 'v[0-9]*.[0-9]*.[0-9]*' \
| grep -Fvx "$tag" \
| head -n 1 || true)"
changelog_range="${head_commit}"
if [ -n "$previous_tag" ] && git rev-parse "${previous_tag}^{commit}" >/dev/null 2>&1; then
changelog_range="${previous_tag}..${head_commit}"
fi
changelog="$(git log --no-merges --pretty=format:'%h %s' "$changelog_range" 2>/dev/null || true)"
if [ -z "$changelog" ]; then
changelog="(no changelog entries)"
fi
notes_file="release-notes.md"
{
echo "## Changelog"
echo
echo '```'
printf '%s\n' "$changelog"
echo '```'
} > "$notes_file"
{
echo "version=${version}"
echo "package_release=${package_release}"
echo "display_version=${display_version}"
echo "rust_toolchain=${rust_toolchain}"
echo "tag=${tag}"
echo "commit=${head_commit}"
echo "notes_file=${notes_file}"
} >> "$GITHUB_OUTPUT"
- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: ${{ steps.release.outputs.rust_toolchain }}
components: rustfmt, clippy
- name: Check formatting
run: cargo fmt --all -- --check
- name: Cache Cargo builds
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
- name: Lint
run: cargo clippy --workspace --locked --all-targets -- -D warnings
- name: Test
run: cargo test --workspace --locked --all-targets
- name: Set up Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GO_VERSION }}
cache: false
- name: Build and package release assets
run: |
set -euo pipefail
go install "github.com/goreleaser/nfpm/v2/cmd/nfpm@${NFPM_VERSION}"
export PATH="${HOME}/go/bin:${PATH}"
cargo build --locked --release
make -C packaging all \
RELEASE="${{ steps.release.outputs.package_release }}" \
MAX_GLIBC_VERSION="${RELEASE_GLIBC_VERSION}"
version="${{ steps.release.outputs.version }}"
package_release="${{ steps.release.outputs.package_release }}"
dist_dir="${PWD}/dist"
shopt -s nullglob
assets=(
"${dist_dir}"/openmeters-"${version}"-*.tar.xz
"${dist_dir}"/openmeters_"${version}"-"${package_release}"_*.deb
"${dist_dir}"/openmeters-"${version}"-"${package_release}".*.rpm
"${dist_dir}"/SHA256SUMS
)
shopt -u nullglob
if [ "${#assets[@]}" -ne 4 ]; then
echo "Expected 4 release artifacts in ${dist_dir}, found ${#assets[@]}." >&2
for asset in "${assets[@]}"; do
echo " ${asset}" >&2
done
exit 1
fi
- name: Upload release assets
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-assets
path: |
dist/openmeters-${{ steps.release.outputs.version }}-*.tar.xz
dist/openmeters_${{ steps.release.outputs.version }}-${{ steps.release.outputs.package_release }}_*.deb
dist/openmeters-${{ steps.release.outputs.version }}-${{ steps.release.outputs.package_release }}.*.rpm
dist/SHA256SUMS
if-no-files-found: error
retention-days: 7
- name: Upload release notes
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-notes
path: ${{ steps.release.outputs.notes_file }}
if-no-files-found: error
retention-days: 7
publish:
needs: build-and-package
runs-on: ubuntu-24.04
permissions:
contents: write
env:
VERSION: ${{ needs.build-and-package.outputs.version }}
PACKAGE_RELEASE: ${{ needs.build-and-package.outputs.package_release }}
DISPLAY_VERSION: ${{ needs.build-and-package.outputs.display_version }}
TAG_NAME: ${{ needs.build-and-package.outputs.tag }}
COMMIT: ${{ needs.build-and-package.outputs.commit }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
GH_REPO: ${{ github.repository }}
steps:
- name: Validate remote release target
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
tag_ref="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG_NAME}" --jq '.object.type + " " + .object.sha')"
read -r object_type object_sha <<< "$tag_ref"
case "$object_type" in
commit)
tag_commit="$object_sha"
;;
tag)
tag_target="$(gh api "repos/${GITHUB_REPOSITORY}/git/tags/${object_sha}" --jq '.object.type + " " + .object.sha')"
read -r target_type tag_commit <<< "$tag_target"
if [ "$target_type" != "commit" ]; then
echo "Release tag ${TAG_NAME} ultimately points at a ${target_type}, not a commit." >&2
exit 1
fi
;;
*)
echo "Release tag ${TAG_NAME} points at unsupported git object type ${object_type}." >&2
exit 1
;;
esac
if [ "$tag_commit" != "$COMMIT" ]; then
echo "Remote release tag ${TAG_NAME} points at ${tag_commit}, but validated build commit is ${COMMIT}." >&2
exit 1
fi
default_branch="${DEFAULT_BRANCH:-main}"
compare_status="$(gh api "repos/${GITHUB_REPOSITORY}/compare/${COMMIT}...${default_branch}" --jq '.status')"
case "$compare_status" in
identical|ahead)
;;
*)
echo "Default branch ${default_branch} does not contain release commit ${COMMIT}; compare status: ${compare_status}." >&2
exit 1
;;
esac
- name: Download release assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-assets
path: dist
- name: Download release notes
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-notes
path: release-notes
- name: Publish GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
notes_file="${PWD}/release-notes/release-notes.md"
if [ ! -s "$notes_file" ]; then
echo "Release notes file ${notes_file} is missing or empty." >&2
exit 1
fi
dist_dir="${PWD}/dist"
(cd "$dist_dir" && sha256sum --check SHA256SUMS)
shopt -s nullglob
assets=(
"${dist_dir}"/openmeters-"${VERSION}"-*.tar.xz
"${dist_dir}"/openmeters_"${VERSION}"-"${PACKAGE_RELEASE}"_*.deb
"${dist_dir}"/openmeters-"${VERSION}"-"${PACKAGE_RELEASE}".*.rpm
"${dist_dir}"/SHA256SUMS
)
shopt -u nullglob
if [ "${#assets[@]}" -ne 4 ]; then
echo "Expected 4 downloaded release artifacts in ${dist_dir}, found ${#assets[@]}." >&2
for asset in "${assets[@]}"; do
echo " ${asset}" >&2
done
exit 1
fi
if is_draft="$(gh release view "$TAG_NAME" --json isDraft --jq .isDraft 2>/dev/null)"; then
if [ "$is_draft" = "true" ]; then
echo "Deleting existing draft release ${TAG_NAME} before recreating it."
gh release delete "$TAG_NAME" --yes
else
echo "Release ${TAG_NAME} already exists and is not a draft; refusing to overwrite it." >&2
exit 1
fi
fi
gh release create "$TAG_NAME" \
--draft \
--target "$COMMIT" \
--title "openmeters ${DISPLAY_VERSION}" \
--notes-file "$notes_file" \
--verify-tag
gh release upload "$TAG_NAME" "${assets[@]}"
gh release edit "$TAG_NAME" --draft=false