ci: gate and build main too (:main image); :latest stays release-only
Previously main pushes were deliberately skipped — CI only ran on dev and v* tags. This conflicted with the intended policy (CI on dev AND main). Now main is a first-class gated, built line: dev->:dev, main->:main, v* tag->:latest + :<version>, every build also tagged with the commit sha. Per-ref concurrency already supersedes rapid pushes, so dev and main run independently without stacking identical work. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-21
@@ -1,12 +1,19 @@
|
||||
# CI runs first; build only proceeds if all checks pass.
|
||||
#
|
||||
# Push to dev: typecheck + lint + test + build :dev + :<sha>
|
||||
# Tag v* (release): typecheck + lint + test + build :latest + :<sha> + :<version>
|
||||
# Push to dev: typecheck + lint + test + build :dev + :<sha>
|
||||
# Push to main: typecheck + lint + test + build :main + :<sha>
|
||||
# Tag v* (release): typecheck + lint + test + build :latest + :<version> + :<sha>
|
||||
#
|
||||
# main pushes are NOT gated here: a merge to main only happens after
|
||||
# dev has already passed CI, and the release tag is the sole trigger
|
||||
# for a production image. Re-running CI on the merge commit just burns
|
||||
# runner time without changing the outcome.
|
||||
# Both dev and main are gated AND built: dev is the working line, main the
|
||||
# protected/integration line, each its own deployable image (:dev / :main).
|
||||
# Running CI again on the main merge commit is intentional — main is
|
||||
# validated and built in its own right. The v* release tag is the ONLY
|
||||
# trigger that publishes :latest (the production pointer) plus the
|
||||
# immutable :<version> tag.
|
||||
#
|
||||
# Successive pushes to the SAME ref supersede each other (see concurrency
|
||||
# below), so rapid pushes don't stack identical work; dev and main runs are
|
||||
# independent refs and never cancel one another.
|
||||
#
|
||||
# To cut a release:
|
||||
# Create a release via the Forgejo UI on main with a v* tag name.
|
||||
@@ -16,11 +23,8 @@
|
||||
# gating on branch push is already enough.
|
||||
#
|
||||
# NOTE on the `if:` guards below: Forgejo Actions does not consistently
|
||||
# honor `on.push.branches` as a filter — merge commits landing on main
|
||||
# still trigger the workflow, producing redundant runs on the same SHA
|
||||
# that was already gated on dev. Every job therefore repeats the ref
|
||||
# check so main pushes trigger the workflow but every job skips
|
||||
# immediately (no runner time, no duplicate work).
|
||||
# honor `on.push.branches` as a filter, so every job repeats the ref check
|
||||
# explicitly — permitting dev, main, and v* tags, rejecting anything else.
|
||||
#
|
||||
# Required secrets (repo → Settings → Secrets → Actions):
|
||||
# REGISTRY_USER — your Forgejo username
|
||||
@@ -29,7 +33,7 @@ name: CI & Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
branches: [dev, main]
|
||||
tags: ["v*"]
|
||||
paths:
|
||||
- "src/**"
|
||||
@@ -67,8 +71,8 @@ env:
|
||||
jobs:
|
||||
typecheck:
|
||||
name: TypeScript typecheck
|
||||
# Skip on main merge-commit pushes — see workflow header comment.
|
||||
if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v')
|
||||
# Gate dev, main, and v* tags; reject any other ref (see header note).
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: python-ci
|
||||
container:
|
||||
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
||||
@@ -92,7 +96,7 @@ jobs:
|
||||
|
||||
lint:
|
||||
name: Python lint
|
||||
if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v')
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: python-ci
|
||||
container:
|
||||
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
||||
@@ -106,7 +110,7 @@ jobs:
|
||||
|
||||
test:
|
||||
name: Python tests
|
||||
if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v')
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: python-ci
|
||||
container:
|
||||
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
||||
@@ -138,11 +142,9 @@ jobs:
|
||||
build:
|
||||
name: Build & push image
|
||||
needs: [typecheck, lint, test]
|
||||
# Build on dev branch pushes and version tag pushes only.
|
||||
# Mirrors the ref guard on the gate jobs above — main merge-commit
|
||||
# pushes skip here too, so no production image is ever built from a
|
||||
# raw main push (only from the v* tag the release creates).
|
||||
if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v')
|
||||
# Build on dev, main, and v* tag pushes. dev → :dev, main → :main,
|
||||
# tag → :latest + :<version>; every build also gets an immutable :<sha>.
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: python-ci
|
||||
container:
|
||||
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
||||
@@ -168,6 +170,10 @@ jobs:
|
||||
refs/heads/dev)
|
||||
TAGS="$TAGS,${{ env.IMAGE }}:dev"
|
||||
;;
|
||||
refs/heads/main)
|
||||
TAGS="$TAGS,${{ env.IMAGE }}:main"
|
||||
BUILD_VERSION="main"
|
||||
;;
|
||||
refs/tags/*)
|
||||
TAGS="$TAGS,${{ env.IMAGE }}:latest,${{ env.IMAGE }}:${{ github.ref_name }}"
|
||||
BUILD_VERSION="${{ github.ref_name }}"
|
||||
|
||||
Reference in New Issue
Block a user