Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Nightly

# Scheduled nightly health build (spec 07 toolchain, mirrors CI). This lane exists
# to catch drift that the per-PR CI can miss — a new Go point release, a transitive
# dependency that breaks a cross-compile target, or non-deterministic generation —
# BEFORE it bites a real release. It runs the full `make ci` equivalent plus the
# 4-target CGO-free cross-build and the determinism assertion.
#
# It NEVER cuts a real semver release and NEVER pushes a git tag: a stray non-semver
# tag (e.g. `nightly`) confuses `git describe` in goreleaser and breaks the
# release-dryrun lane on every PR (MEMORY: no-non-semver-tags). The optional
# pre-release path (default OFF) publishes goreleaser *snapshot* artifacts as
# workflow artifacts only — no tag is ever created.

on:
schedule:
- cron: "0 7 * * *" # 07:00 UTC daily
workflow_dispatch: {}

permissions:
contents: read

concurrency:
group: nightly-${{ github.ref }}
cancel-in-progress: true

env:
# Single enforced Go toolchain floor (DECISIONS, ARCHITECTURE §7.8) — same as CI.
GO_VERSION: "1.25"

jobs:
# The full nightly gate. Guarded so scheduled runs no-op on forks (a fork inherits
# the schedule but not the org repo name); workflow_dispatch on a fork is likewise
# skipped, keeping this lane a first-party-only signal.
nightly:
if: github.repository == 'open-source-cloud/devstack'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true # nightly's whole point: pick up new Go point releases
cache: true # GOMODCACHE + GOCACHE, keyed by go.sum

# `make ci` (fmt-check + vet + CGO=0 build + CGO=1 test-race) plus the 4-target
# cross-build and the byte-identical generation assertion, all via one target.
- name: nightly gate (fmt-check + vet + cross-build + test-race + determinism)
run: make nightly

# Optional rolling pre-release. Default OFF: enable by setting the repo variable
# gh variable set NIGHTLY_PRERELEASE --body true
# Produces goreleaser SNAPSHOT artifacts (no real version, no git tag) and uploads
# them as workflow artifacts for manual smoke-testing of a nightly build. We do NOT
# create a GitHub Release or a `nightly` git tag — that would trip release-dryrun.
prerelease:
needs: nightly
if: github.repository == 'open-source-cloud/devstack' && vars.NIGHTLY_PRERELEASE == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for goreleaser's snapshot versioning
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
cache: true
- uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
args: release --snapshot --clean
- name: upload nightly snapshot artifacts
uses: actions/upload-artifact@v4
with:
name: nightly-snapshot
path: |
dist/*.tar.gz
dist/*.deb
dist/*.rpm
dist/checksums.txt
retention-days: 7
if-no-files-found: error
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ BINDIR ?= $(or $(XDG_BIN_HOME),$(PREFIX)/bin)

# The release binary MUST be CGO-free (static), but `go test -race` REQUIRES cgo.
# So CGO is set per-target, never globally.
.PHONY: build run test test-race integration e2e test-one vet fmt fmt-check lint tidy vuln clean snapshot ci determinism install uninstall smoke help
.PHONY: build run test test-race integration e2e test-one vet fmt fmt-check lint tidy vuln clean snapshot cross ci nightly determinism install uninstall smoke help

build: ## Build the static binary into ./dist
CGO_ENABLED=0 go build -trimpath -ldflags '$(LDFLAGS)' -o dist/$(BINARY) ./cmd/devstack
Expand Down Expand Up @@ -63,8 +63,16 @@ tidy: ## go mod tidy
snapshot: ## Local goreleaser snapshot build (no publish)
go run github.com/goreleaser/goreleaser/v2@latest release --snapshot --clean

cross: ## Cross-compile the 4 CGO-free release targets (build-only, output discarded)
@set -eu; for t in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64; do \
echo "-> $$t"; \
GOOS=$${t%/*} GOARCH=$${t#*/} CGO_ENABLED=0 go build -trimpath -ldflags '$(LDFLAGS)' -o /dev/null ./cmd/devstack; \
done

ci: fmt-check vet build test-race ## What CI runs

nightly: fmt-check vet cross test-race determinism ## Full nightly gate (make ci + 4-target cross-build + determinism)

determinism: build ## Assert generation is byte-identical across runs/paths (M1, spec 02)
@set -eu; \
bin="$$PWD/dist/$(BINARY)"; \
Expand Down
Loading