3 Commits

Author SHA1 Message Date
lerko 9b5cc37ad4 build(docker): pin base images by digest
CI / test (pull_request) Successful in 2m20s
CI / lint (pull_request) Successful in 41s
CI / vulncheck (pull_request) Successful in 41s
Prevents silently pulling a compromised or broken upstream image.
Digests must be updated manually when bumping Alpine/Go versions.
2026-06-01 21:38:31 -04:00
lerko 3a169b2bcd ci(docker): add Grype CVE scanning after image push
Scans published image for Alpine and dependency CVEs.
Fails on critical severity, reports all others in table output.
2026-06-01 21:32:20 -04:00
lerko 50eb43971c refactor(ci): split release pipeline, add nfpm/homebrew/git-cliff
Split monolithic release.yml into independent workflows:
- release-binaries.yml: tag-triggered, GoReleaser + git-cliff notes
- release-docker.yml: tag-triggered + manual dispatch, SHA tags

Add DEB/RPM packaging via nfpm in GoReleaser. Add Homebrew cask
config (skip_upload until macOS builds exist). Replace GoReleaser
built-in changelog with git-cliff for structured release notes.
2026-06-01 21:14:54 -04:00
6 changed files with 168 additions and 62 deletions
+53
View File
@@ -0,0 +1,53 @@
name: Release Binaries
on:
push:
tags:
- "[0-9]*"
jobs:
release:
runs-on: ubuntu-latest
defaults:
run:
shell: sh
steps:
- name: Install build tools
run: apk add --no-cache git gcc musl-dev
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: "1.26"
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: release-go-${{ hashFiles('go.sum') }}
restore-keys: release-go-
- name: Install git-cliff
run: |
apk add --no-cache curl
curl -sSL https://github.com/orhun/git-cliff/releases/latest/download/git-cliff-x86_64-unknown-linux-musl.tar.gz | tar xz
mv git-cliff-*/git-cliff /usr/local/bin/
git-cliff --version
- name: Generate release notes
run: git-cliff --current --strip header -o /tmp/release-notes.md
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser
version: "~> v2"
args: release --clean --release-notes=/tmp/release-notes.md
env:
GORELEASER_FORCE_TOKEN: gitea
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITEA_API_URL: http://gitea:3000/api/v1
@@ -1,53 +1,36 @@
name: Release
name: Release Docker
on:
push:
tags:
- "[0-9]*"
workflow_dispatch:
inputs:
tag:
description: "Image tag (e.g. 2026.06.1). Defaults to latest commit SHA."
required: false
jobs:
release:
runs-on: ubuntu-latest
defaults:
run:
shell: sh
steps:
- name: Install build tools
run: apk add --no-cache git gcc musl-dev
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: "1.26"
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: release-go-${{ hashFiles('go.sum') }}
restore-keys: release-go-
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser
version: "~> v2"
args: release --clean
env:
GORELEASER_FORCE_TOKEN: gitea
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITEA_API_URL: http://gitea:3000/api/v1
docker:
runs-on: docker-builder
needs: [release]
steps:
- uses: actions/checkout@v4
- name: Resolve image tag
id: meta
run: |
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ github.event.inputs.tag }}"
if [ -z "$TAG" ]; then
TAG="${{ github.sha }}"
fi
else
TAG="${{ github.ref_name }}"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -69,13 +52,19 @@ jobs:
sbom: true
provenance: mode=max
tags: |
lerkolabs/uptop:${{ github.ref_name }}
lerkolabs/uptop:${{ steps.meta.outputs.tag }}
lerkolabs/uptop:latest
lerkolabs/uptop:sha-${{ steps.meta.outputs.short_sha }}
build-args: |
VERSION=${{ github.ref_name }}
VERSION=${{ steps.meta.outputs.tag }}
COMMIT=${{ github.sha }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
- name: Scan image for CVEs
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
grype lerkolabs/uptop:${{ steps.meta.outputs.tag }} --fail-on critical --output table
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@v4
with:
+2 -14
View File
@@ -1,10 +1,3 @@
# Created by https://www.toptal.com/developers/gitignore/api/go
# Edit at https://www.toptal.com/developers/gitignore?templates=go
### Go ###
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
@@ -24,16 +17,11 @@
# Go workspace file
go.work
# End of https://www.toptal.com/developers/gitignore/api/go
/uptop
/dist
uptop.db*
.ssh
authorized_keys
tmp
*.local.json
*.local.md
*.local.md
+37 -6
View File
@@ -33,10 +33,41 @@ archives:
checksum:
name_template: checksums.txt
nfpms:
- package_name: uptop
file_name_template: "{{ .ConventionalFileName }}"
vendor: LerkoLabs
homepage: https://gitea.lerkolabs.com/lerkolabs/uptop
maintainer: Tyler Koenig <tyler@lerkolabs.com>
description: Self-hosted uptime monitoring with a TUI over SSH
license: MIT
section: net
priority: optional
formats:
- deb
- rpm
bindir: /usr/bin
contents:
- src: ./LICENSE
dst: /usr/share/doc/uptop/LICENSE
type: doc
homebrew_casks:
- name: uptop
homepage: https://gitea.lerkolabs.com/lerkolabs/uptop
description: Self-hosted uptime monitoring with a TUI over SSH
directory: Casks
skip_upload: true
commit_msg_template: "update uptop to {{ .Tag }}"
url:
template: "https://gitea.lerkolabs.com/lerkolabs/uptop/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
repository:
owner: lerkolabs
name: homebrew-tap
git:
url: "ssh://git@gitea.lerkolabs.com:2222/lerkolabs/homebrew-tap.git"
private_key: "{{ if index .Env \"TAP_SSH_KEY\" }}{{ .Env.TAP_SSH_KEY }}{{ end }}"
ssh_command: "ssh -o StrictHostKeyChecking=accept-new"
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^chore:"
- "^style:"
disable: true
+2 -2
View File
@@ -1,5 +1,5 @@
# --- Stage 1: Builder ---
FROM golang:1.26-alpine3.23 AS builder
FROM golang:1.26-alpine3.23@sha256:91eda9776261207ea25fd06b5b7fed8d397dd2c0a283e77f2ab6e91bfa71079d AS builder
RUN apk add --no-cache gcc musl-dev
WORKDIR /app
COPY go.mod go.sum ./
@@ -15,7 +15,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \
go build -trimpath -ldflags="-s -w -X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${BUILD_DATE}" -o uptop ./cmd/uptop/main.go
# --- Stage 2: Runner ---
FROM alpine:3.23
FROM alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11
WORKDIR /app
RUN apk add --no-cache ca-certificates && apk upgrade --no-cache
RUN addgroup -g 1000 -S uptop && adduser -u 1000 -S uptop -G uptop
+45
View File
@@ -0,0 +1,45 @@
[changelog]
header = """
# Changelog\n
"""
body = """
{% if version %}\
## [{{ version }}] — {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim }}
{% for commit in commits %}
- {{ commit.message | split(pat="\n") | first | trim }}\
{% endfor %}
{% endfor %}\n
"""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
protect_breaking_commits = false
filter_commits = false
tag_pattern = "[0-9]*"
topo_order = false
sort_commits = "oldest"
commit_parsers = [
{ message = "^feat", group = "Added" },
{ message = "^fix", group = "Fixed" },
{ message = "^perf", group = "Changed" },
{ message = "^refactor", group = "Changed" },
{ message = "^security", group = "Security" },
{ body = ".*security", group = "Security" },
{ body = "BREAKING", group = "Breaking" },
{ footer = "BREAKING.CHANGE", group = "Breaking" },
{ message = "^docs", skip = true },
{ message = "^style", skip = true },
{ message = "^chore", skip = true },
{ message = "^ci", skip = true },
{ message = "^test", skip = true },
{ message = "^build", skip = true },
]