Skip to content

Commit 881bcd6

Browse files
committed
Add automated nightly releases.
- Automatic GitHub action release to Docker and GitHub releases tagged `nightly`. - Add to docs. - Change migration behaviour to always run nightly migrations since last recorded version (and not record nightly version).
1 parent 577036a commit 881bcd6

File tree

7 files changed

+363
-25
lines changed

7 files changed

+363
-25
lines changed

.github/workflows/nightly.yml

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: nightly
2+
on:
3+
schedule:
4+
- cron: "0 2 * * *"
5+
workflow_dispatch:
6+
7+
permissions:
8+
contents: write
9+
packages: write
10+
11+
jobs:
12+
check:
13+
runs-on: ubuntu-latest
14+
outputs:
15+
skip: ${{ steps.check_changes.outputs.skip }}
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v6
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Check for changes since last nightly release
23+
id: check_changes
24+
env:
25+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26+
run: |
27+
LAST_NIGHTLY_SHA=$(gh release view nightly --json targetCommitish -q '.targetCommitish' 2>/dev/null || echo "")
28+
CURRENT_SHA=$(git rev-parse HEAD)
29+
echo "Last nightly SHA: $LAST_NIGHTLY_SHA"
30+
echo "Current SHA: $CURRENT_SHA"
31+
if [ -n "$LAST_NIGHTLY_SHA" ] && [ "$LAST_NIGHTLY_SHA" = "$CURRENT_SHA" ]; then
32+
echo "No changes since last nightly build, skipping ..."
33+
echo "skip=true" >> $GITHUB_OUTPUT
34+
else
35+
echo "Changes detected, proceeding with build ..."
36+
echo "skip=false" >> $GITHUB_OUTPUT
37+
fi
38+
39+
nightly:
40+
needs: check
41+
if: needs.check.outputs.skip != 'true'
42+
runs-on: ubuntu-latest
43+
steps:
44+
- name: Checkout
45+
uses: actions/checkout@v6
46+
with:
47+
fetch-depth: 0
48+
49+
- name: Set up QEMU
50+
uses: docker/setup-qemu-action@v3
51+
52+
- name: Set up Go
53+
uses: actions/setup-go@v5
54+
with:
55+
go-version: "1.25.6"
56+
57+
- name: Login to Docker Hub
58+
uses: docker/login-action@v3
59+
with:
60+
username: ${{ secrets.DOCKERHUB_USERNAME }}
61+
password: ${{ secrets.DOCKERHUB_TOKEN }}
62+
63+
- name: Login to GitHub Container Registry
64+
uses: docker/login-action@v3
65+
with:
66+
registry: ghcr.io
67+
username: ${{ github.actor }}
68+
password: ${{ secrets.GITHUB_TOKEN }}
69+
70+
- name: Delete existing nightly release
71+
env:
72+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73+
run: gh release delete nightly --yes --cleanup-tag 2>/dev/null || true
74+
75+
- name: Set nightly date
76+
id: tag
77+
run: |
78+
NIGHTLY_DATE=$(date -u +%Y-%m-%d)
79+
echo "date=$NIGHTLY_DATE" >> $GITHUB_OUTPUT
80+
81+
- name: Prepare dependencies
82+
run: make dist
83+
env:
84+
LISTMONK_VERSION: nightly-${{ steps.tag.outputs.date }}
85+
86+
- name: Run GoReleaser
87+
uses: goreleaser/goreleaser-action@v6
88+
with:
89+
version: latest
90+
args: release --snapshot --parallelism 1 --clean --config .goreleaser-nightly.yml
91+
env:
92+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
93+
LISTMONK_VERSION: nightly-${{ steps.tag.outputs.date }}
94+
95+
- name: Push Docker images
96+
run: |
97+
# Push all architecture-specific images
98+
docker push listmonk/listmonk:nightly-amd64
99+
docker push listmonk/listmonk:nightly-arm64v8
100+
docker push listmonk/listmonk:nightly-armv6
101+
docker push listmonk/listmonk:nightly-armv7
102+
docker push ghcr.io/knadh/listmonk:nightly-amd64
103+
docker push ghcr.io/knadh/listmonk:nightly-arm64v8
104+
docker push ghcr.io/knadh/listmonk:nightly-armv6
105+
docker push ghcr.io/knadh/listmonk:nightly-armv7
106+
107+
- name: Create and push Docker manifests
108+
run: |
109+
# Docker Hub manifest
110+
docker manifest create listmonk/listmonk:nightly \
111+
listmonk/listmonk:nightly-amd64 \
112+
listmonk/listmonk:nightly-arm64v8 \
113+
listmonk/listmonk:nightly-armv6 \
114+
listmonk/listmonk:nightly-armv7
115+
docker manifest push listmonk/listmonk:nightly
116+
117+
# GHCR manifest
118+
docker manifest create ghcr.io/knadh/listmonk:nightly \
119+
ghcr.io/knadh/listmonk:nightly-amd64 \
120+
ghcr.io/knadh/listmonk:nightly-arm64v8 \
121+
ghcr.io/knadh/listmonk:nightly-armv6 \
122+
ghcr.io/knadh/listmonk:nightly-armv7
123+
docker manifest push ghcr.io/knadh/listmonk:nightly
124+
125+
- name: Create GitHub Release
126+
env:
127+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
run: |
129+
gh release create nightly \
130+
--title "Nightly release" \
131+
--notes "
132+
> **Warning**: This is an automated nightly build from the master branch.
133+
> It may contain bugs and breaking changes. Use at your own risk.
134+
> Available on Docker Hub and GitHub Container Registry as `listmonk/listmonk:nightly`.
135+
> For stable releases, please use a versioned release. [Learn more](https://listmonk.app/docs/installation/#nightly)
136+
137+
Built from commit: $(git rev-parse --short HEAD)" \
138+
--prerelease \
139+
--target $(git rev-parse HEAD) \
140+
dist/*.tar.gz

.github/workflows/release.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,26 @@ jobs:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Checkout
15-
uses: actions/checkout@v3
15+
uses: actions/checkout@v6
1616
with:
1717
fetch-depth: 0
1818

1919
- name: Set up QEMU
20-
uses: docker/setup-qemu-action@v2
20+
uses: docker/setup-qemu-action@v3
2121

2222
- name: Set up Go
2323
uses: actions/setup-go@v5
2424
with:
25-
go-version: "1.24.1"
25+
go-version: "1.25.6"
2626

2727
- name: Login to Docker Registry
28-
uses: docker/login-action@v2
28+
uses: docker/login-action@v3
2929
with:
3030
username: ${{ secrets.DOCKERHUB_USERNAME }}
3131
password: ${{ secrets.DOCKERHUB_TOKEN }}
3232

3333
- name: Login to GitHub Docker Registry
34-
uses: docker/login-action@v2
34+
uses: docker/login-action@v3
3535
with:
3636
registry: ghcr.io
3737
username: ${{ github.actor }}
@@ -46,7 +46,7 @@ jobs:
4646
docker version
4747
4848
- name: Run GoReleaser
49-
uses: goreleaser/goreleaser-action@v5
49+
uses: goreleaser/goreleaser-action@v6
5050
with:
5151
version: latest
5252
args: release --parallelism 1 --clean

.goreleaser-nightly.yml

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
version: 2
2+
3+
snapshot:
4+
version_template: "{{ .Env.LISTMONK_VERSION }}"
5+
6+
# GoReleaser config for nightly builds
7+
8+
env:
9+
- GO111MODULE=on
10+
- CGO_ENABLED=0
11+
- GITHUB_ORG=knadh
12+
- DOCKER_ORG=listmonk
13+
14+
before:
15+
hooks:
16+
- make build-frontend
17+
18+
builds:
19+
- binary: listmonk
20+
main: ./cmd
21+
goos:
22+
- linux
23+
- windows
24+
- darwin
25+
- freebsd
26+
- openbsd
27+
- netbsd
28+
goarch:
29+
- amd64
30+
- arm64
31+
- arm
32+
goarm:
33+
- 6
34+
- 7
35+
ldflags:
36+
- -s -w -X "main.buildString=nightly ({{ .ShortCommit }} {{ .Date }}, {{ .Os }}/{{ .Arch }})" -X "main.versionString={{ .Env.LISTMONK_VERSION }}"
37+
38+
hooks:
39+
# stuff executables with static assets.
40+
post: make pack-bin BIN={{ .Path }}
41+
42+
archives:
43+
- format: tar.gz
44+
name_template: "listmonk_nightly_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
45+
files:
46+
- README.md
47+
- LICENSE
48+
49+
dockers:
50+
- use: buildx
51+
goos: linux
52+
goarch: amd64
53+
ids:
54+
- listmonk
55+
image_templates:
56+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-amd64"
57+
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-amd64"
58+
build_flag_templates:
59+
- --platform=linux/amd64
60+
- --label=org.opencontainers.image.title={{ .ProjectName }}
61+
- --label=org.opencontainers.image.description={{ .ProjectName }}
62+
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
63+
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
64+
- --label=org.opencontainers.image.version=nightly
65+
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
66+
- --label=org.opencontainers.image.revision={{ .FullCommit }}
67+
- --label=org.opencontainers.image.licenses=AGPL-3.0
68+
dockerfile: Dockerfile
69+
extra_files:
70+
- config.toml.sample
71+
- docker-entrypoint.sh
72+
- use: buildx
73+
goos: linux
74+
goarch: arm64
75+
ids:
76+
- listmonk
77+
image_templates:
78+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-arm64v8"
79+
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-arm64v8"
80+
build_flag_templates:
81+
- --platform=linux/arm64/v8
82+
- --label=org.opencontainers.image.title={{ .ProjectName }}
83+
- --label=org.opencontainers.image.description={{ .ProjectName }}
84+
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
85+
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
86+
- --label=org.opencontainers.image.version=nightly
87+
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
88+
- --label=org.opencontainers.image.revision={{ .FullCommit }}
89+
- --label=org.opencontainers.image.licenses=AGPL-3.0
90+
dockerfile: Dockerfile
91+
extra_files:
92+
- config.toml.sample
93+
- docker-entrypoint.sh
94+
- use: buildx
95+
goos: linux
96+
goarch: arm
97+
goarm: 6
98+
ids:
99+
- listmonk
100+
image_templates:
101+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-armv6"
102+
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-armv6"
103+
build_flag_templates:
104+
- --platform=linux/arm/v6
105+
- --label=org.opencontainers.image.title={{ .ProjectName }}
106+
- --label=org.opencontainers.image.description={{ .ProjectName }}
107+
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
108+
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
109+
- --label=org.opencontainers.image.version=nightly
110+
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
111+
- --label=org.opencontainers.image.revision={{ .FullCommit }}
112+
- --label=org.opencontainers.image.licenses=AGPL-3.0
113+
dockerfile: Dockerfile
114+
extra_files:
115+
- config.toml.sample
116+
- docker-entrypoint.sh
117+
- use: buildx
118+
goos: linux
119+
goarch: arm
120+
goarm: 7
121+
ids:
122+
- listmonk
123+
image_templates:
124+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-armv7"
125+
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-armv7"
126+
build_flag_templates:
127+
- --platform=linux/arm/v7
128+
- --label=org.opencontainers.image.title={{ .ProjectName }}
129+
- --label=org.opencontainers.image.description={{ .ProjectName }}
130+
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
131+
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
132+
- --label=org.opencontainers.image.version=nightly
133+
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
134+
- --label=org.opencontainers.image.revision={{ .FullCommit }}
135+
- --label=org.opencontainers.image.licenses=AGPL-3.0
136+
dockerfile: Dockerfile
137+
extra_files:
138+
- config.toml.sample
139+
- docker-entrypoint.sh
140+
141+
docker_manifests:
142+
- name_template: "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly"
143+
image_templates:
144+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-amd64"
145+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-arm64v8"
146+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-armv6"
147+
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:nightly-armv7"
148+
- name_template: ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly
149+
image_templates:
150+
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-amd64
151+
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-arm64v8
152+
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-armv6
153+
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:nightly-armv7
154+
155+
changelog:
156+
disable: true
157+
158+
release:
159+
prerelease: true
160+
name_template: "Nightly Build"
161+
header: |
162+
## Nightly Build
163+
164+
> **Warning**: This is an automated nightly build from the master branch.
165+
> It may contain bugs and breaking changes. Use at your own risk.
166+
> For stable releases, please use a versioned release.
167+
168+
Built from commit: {{ .ShortCommit }}

cmd/main.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ func init() {
149149
os.Exit(0)
150150
}
151151

152+
// Is this a nightly build?
153+
isNightly := strings.Contains(versionString, "nightly")
154+
152155
// Check if the DB schema is installed.
153156
if ok, err := checkSchema(db); err != nil {
154157
log.Fatalf("error checking schema in DB: %v", err)
@@ -157,12 +160,23 @@ func init() {
157160
}
158161

159162
if ko.Bool("upgrade") {
160-
upgrade(db, fs, !ko.Bool("yes"))
163+
// Even on explicit upgrade runs, for nightly builds, do not record the last
164+
// migration version in the DB.
165+
lo.Printf("running upgrade...")
166+
upgrade(db, fs, !ko.Bool("yes"), !isNightly)
161167
os.Exit(0)
162168
}
163169

164-
// Before the queries are prepared, see if there are pending upgrades.
165-
checkUpgrade(db)
170+
// For nightly builds, always auto-run pending migrations without
171+
// recording the last version in the DB. Migrations are idempotent, and between
172+
// nightly releases, they may change multiple times.
173+
if isNightly {
174+
lo.Printf("auto-running all migrations for nightly %s since last major version", versionString)
175+
upgrade(db, fs, false, false)
176+
} else {
177+
// Before the queries are prepared, see if there are pending upgrades.
178+
checkUpgrade(db)
179+
}
166180

167181
// Read the SQL queries from the queries file.
168182
qMap := readQueries(queryFilePath, fs)

0 commit comments

Comments
 (0)