All checks were successful
check / check (push) Successful in 3m5s
Gitea cancels an in-flight run when a newer commit lands on the same branch and records the cancellation as `failure` / "Has been cancelled". The workflow rewrote that to `skipped`, but Gitea's Combine() folds `skipped` into `success`, so the combined-status API returned green for a commit nothing had ever tested. Rewrite it to `failure` / "Superseded by a newer commit; never tested" instead: red-but-honest, and never `pending`, which would block the commit forever. Re-running the superseded commit would have been better still, but is not reachable on this Gitea (1.25.4): its API exposes no rerun endpoint, workflow dispatch takes a ref rather than a SHA, and every replay would be a full uncached build with no bound on how many pile up behind a burst of merges. The step also stops hardcoding its status context: the logic moves into script/ci-mark-superseded, which derives the context from the workflow name, job id and event, and fails loudly when no status on the commit being built carries that context, so renaming the workflow or the job cannot silently disable the rewrite. The derivation is not byte-exact with Gitea's own rule -- Gitea uses the job's display `name:` where the runner exports the job id -- so adding a `name:` to the job turns every push red rather than quietly doing nothing; the script header says so, because that loud failure is the point. That is item 2 of #147; item 1 there is untouched. Nothing about the walk may fail quietly, since the script exists to stop CI lying quietly. An ANCESTOR_LIMIT that is set but not a positive integer aborts instead of passing an unusable value to git and discarding the error. A shallow clone aborts on `git rev-parse --is-shallow-repository`: the graft makes the parent unresolvable, so a shallow checkout is indistinguishable from a root commit and the walk would exit 0 having marked nothing -- one dropped `fetch-depth: 0` away, which the workflow comment now records. A genuine root commit still exits 0, an unknown SHA has already been rejected by the context read's 404, and the walk carries no `|| true`, so a rev-list failure aborts. Per-ancestor status reads carry the same `--retry 3 --max-time 30` as the head-commit read and abort on failure rather than losing curl's exit status through a pipe. Tests drive the script against a fake Gitea covering the cancelled, laundered-skipped, genuinely-failed, passing and renamed cases, an unparseable ANCESTOR_LIMIT, an ancestor whose status read answers HTTP 500, and a depth-1 clone, so jq joins the builder image to run them.
97 lines
3.4 KiB
Docker
97 lines
3.4 KiB
Docker
# Lint stage
|
|
# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-07
|
|
# Using Debian-based image because mattn/go-sqlite3 (CGO) does not
|
|
# compile on Alpine musl (off64_t is a glibc type).
|
|
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends make && rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /src
|
|
|
|
# Copy go mod files first for better layer caching
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
# Copy source code. In CI the context also carries .ci-fingerprint, whose
|
|
# value changes with every commit that touches the build context (see
|
|
# .gitea/workflows/check.yml). That invalidates this layer, so the checks
|
|
# below cannot report success by replaying a cached pass. Do not add it to
|
|
# .dockerignore.
|
|
COPY . .
|
|
|
|
# Run formatting check and linter
|
|
RUN make fmt-check
|
|
RUN make lint
|
|
|
|
# Build stage
|
|
# golang:1.26.1-bookworm (Debian-based), 2026-03-17
|
|
# Using Debian-based image because gorm.io/driver/sqlite pulls in
|
|
# mattn/go-sqlite3 (CGO), which does not compile on Alpine musl.
|
|
FROM golang:1.26.1-bookworm@sha256:4465644228bc2857a954b092167e12aa59c006a3492282a6c820bf4755fd64a4 AS builder
|
|
|
|
# Depend on lint stage passing
|
|
COPY --from=lint /src/go.sum /dev/null
|
|
|
|
# jq is a runtime dependency of script/ci-mark-superseded, which the test
|
|
# suite executes.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends make curl ca-certificates jq && rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /build
|
|
|
|
# Copy go mod files first for better layer caching
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
# Copy source code, including the .ci-fingerprint cache barrier described in
|
|
# the lint stage above.
|
|
COPY . .
|
|
|
|
# Fetch the third-party browser assets the UI serves. They are not committed
|
|
# (REPO_POLICIES.md forbids minified bundles in version control) and
|
|
# .dockerignore keeps any host copy out of the build context, so this step is
|
|
# the only way they enter the image. Each download is checked against a
|
|
# hardcoded sha256 and the build fails on mismatch; make test re-checks the
|
|
# hashes against the bytes go:embed actually put in the binary.
|
|
RUN script/fetch-assets
|
|
|
|
# Run tests and build
|
|
RUN make test
|
|
RUN make build
|
|
|
|
# Rebuild with static linking for Alpine runtime.
|
|
# make build already verified compilation.
|
|
# The CGO binary from `make build` is dynamically linked against glibc,
|
|
# which doesn't exist on Alpine (musl). Rebuild with static linking so
|
|
# the binary runs on Alpine without glibc.
|
|
RUN CGO_ENABLED=1 go build -ldflags '-extldflags "-static"' -o bin/webhooker ./cmd/webhooker
|
|
|
|
# Runtime stage
|
|
# alpine:3.21, 2026-03-17
|
|
FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
|
|
|
|
RUN apk --no-cache add ca-certificates
|
|
|
|
# Create non-root user
|
|
RUN addgroup -g 1000 -S webhooker && \
|
|
adduser -u 1000 -S webhooker -G webhooker
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy binary from builder
|
|
COPY --from=builder /build/bin/webhooker /app/webhooker
|
|
|
|
# Create data directory for all SQLite databases (main app DB +
|
|
# per-webhook event DBs). DATA_DIR defaults to /var/lib/webhooker.
|
|
RUN mkdir -p /var/lib/webhooker
|
|
|
|
RUN chown -R webhooker:webhooker /app /var/lib/webhooker
|
|
|
|
USER webhooker
|
|
|
|
EXPOSE 8080
|
|
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/.well-known/healthcheck || exit 1
|
|
|
|
CMD ["/app/webhooker"]
|