From cc0c6cd61496d525cd8710601c59d13a1b306219 Mon Sep 17 00:00:00 2001 From: im10furry Date: Mon, 6 Jul 2026 00:02:20 -0500 Subject: [PATCH] ci: auto-publish releases after CI --- .github/workflows/ci.yml | 3 + .github/workflows/release.yml | 117 +++++++++++++++++++++++++++++--- internal/bundle/manifest.json | 10 +-- internal/bundle/registry.zip | Bin 720620 -> 720833 bytes registry/database/README.md | 4 ++ registry/database/src/go/go.mod | 2 +- 6 files changed, 119 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55ee651..2889937 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,7 @@ jobs: middleware auth crud + database rbac ratelimit validation @@ -100,6 +101,7 @@ jobs: middleware auth crud + database rbac ratelimit validation @@ -147,6 +149,7 @@ jobs: middleware auth crud + database rbac ratelimit validation diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99c0129..9a15542 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: push: tags: - "v*.*.*" + workflow_run: + workflows: ["CI"] + types: [completed] + branches: [master] workflow_dispatch: inputs: tag: @@ -15,34 +19,45 @@ permissions: contents: write concurrency: - group: release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} + group: release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref_name }} cancel-in-progress: false jobs: release: name: Build and publish release runs-on: ubuntu-latest + if: github.event_name != 'workflow_run' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'master') steps: - - name: Resolve tag + - name: Resolve release source id: release shell: bash run: | set -euo pipefail if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then tag="${{ inputs.tag }}" + ref="$tag" + automatic="false" + elif [ "${{ github.event_name }}" = "workflow_run" ]; then + tag="" + ref="${{ github.event.workflow_run.head_sha }}" + automatic="true" else tag="${{ github.ref_name }}" + ref="$tag" + automatic="false" fi - if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [ -n "$tag" ] && [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "::error::Release tag must match vMAJOR.MINOR.PATCH, got: $tag" exit 1 fi echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "ref=$ref" >> "$GITHUB_OUTPUT" + echo "automatic=$automatic" >> "$GITHUB_OUTPUT" - - name: Checkout tag + - name: Checkout release source uses: actions/checkout@v4 with: - ref: ${{ steps.release.outputs.tag }} + ref: ${{ steps.release.outputs.ref }} fetch-depth: 0 - name: Setup Go @@ -50,13 +65,59 @@ jobs: with: go-version: "1.22" + - name: Resolve automatic tag + id: auto + if: steps.release.outputs.automatic == 'true' + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + sha="${{ github.event.workflow_run.head_sha }}" + existing="$(git tag --points-at "$sha" --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1 || true)" + create_tag="false" + + if [ -n "$existing" ]; then + tag="$existing" + else + latest="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1 || true)" + if [ -z "$latest" ]; then + latest="v0.0.0" + fi + + version="${latest#v}" + IFS=. read -r major minor patch <<< "$version" + tag="v${major}.${minor}.$((patch + 1))" + create_tag="true" + fi + + should_publish="true" + if gh release view "$tag" >/dev/null 2>&1; then + should_publish="false" + fi + + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "create_tag=$create_tag" >> "$GITHUB_OUTPUT" + echo "should_publish=$should_publish" >> "$GITHUB_OUTPUT" + + - name: Create automatic tag + if: steps.release.outputs.automatic == 'true' && steps.auto.outputs.create_tag == 'true' + shell: bash + run: | + set -euo pipefail + tag="${{ steps.auto.outputs.tag }}" + sha="${{ github.event.workflow_run.head_sha }}" + git tag "$tag" "$sha" + git push origin "refs/tags/$tag" + - name: Resolve release commit id: commit shell: bash run: | set -euo pipefail - tag="${{ steps.release.outputs.tag }}" + tag="${{ steps.auto.outputs.tag || steps.release.outputs.tag }}" sha="$(git rev-list -n 1 "$tag")" + echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "sha=$sha" >> "$GITHUB_OUTPUT" - name: Ensure release does not already exist @@ -65,13 +126,18 @@ jobs: shell: bash run: | set -euo pipefail - tag="${{ steps.release.outputs.tag }}" + tag="${{ steps.commit.outputs.tag }}" if gh release view "$tag" >/dev/null 2>&1; then + if [ "${{ steps.release.outputs.automatic }}" = "true" ] && [ "${{ steps.auto.outputs.should_publish }}" = "false" ]; then + echo "Release already exists for automatic tag: $tag" + exit 0 + fi echo "::error::Release already exists: $tag" exit 1 fi - name: Run release gates + if: steps.release.outputs.automatic != 'true' || steps.auto.outputs.should_publish == 'true' shell: bash run: | set -euo pipefail @@ -83,6 +149,31 @@ jobs: go vet ./cmd/... ./internal/... echo "::endgroup::" + modules=( + middleware + auth + crud + database + rbac + ratelimit + validation + file-upload + health + cache + pagination + mail + ) + + for mod in "${modules[@]}"; do + echo "::group::module test: $mod" + (cd "registry/$mod/src/go" && go test -count=1 -timeout 10m ./...) + echo "::endgroup::" + + echo "::group::module vet: $mod" + (cd "registry/$mod/src/go" && go vet ./...) + echo "::endgroup::" + done + echo "::group::doctor strict" go run ./cmd/scion doctor --strict echo "::endgroup::" @@ -93,10 +184,11 @@ jobs: echo "::endgroup::" - name: Build release assets + if: steps.release.outputs.automatic != 'true' || steps.auto.outputs.should_publish == 'true' shell: bash run: | set -euo pipefail - tag="${{ steps.release.outputs.tag }}" + tag="${{ steps.commit.outputs.tag }}" sha="${{ steps.commit.outputs.sha }}" ldflags="-s -w -X github.com/DarkInno/scion/internal/version.Version=$tag -X github.com/DarkInno/scion/internal/version.Commit=$sha" @@ -140,20 +232,22 @@ jobs: (cd dist && sha256sum scion_*.tar.gz scion_*.zip > SHA256SUMS) - name: Verify built binary version + if: steps.release.outputs.automatic != 'true' || steps.auto.outputs.should_publish == 'true' shell: bash run: | set -euo pipefail - tag="${{ steps.release.outputs.tag }}" + tag="${{ steps.commit.outputs.tag }}" version="$(./dist/scion_${tag}_linux_amd64/scion version)" echo "$version" grep -q "scion $tag" <<< "$version" grep -q "commit: ${{ steps.commit.outputs.sha }}" <<< "$version" - name: Write release notes + if: steps.release.outputs.automatic != 'true' || steps.auto.outputs.should_publish == 'true' shell: bash run: | set -euo pipefail - tag="${{ steps.release.outputs.tag }}" + tag="${{ steps.commit.outputs.tag }}" cat > release-notes.md <ll@Z+JTAfQ(8K@kVnYFmrN+NfQtGs1{dVFqEQ)~p-c zq-mvAtJ}t7)3l_ek91dz+D%+-u*I&AT0u}jM1`RuAW9V!A1Juz&iR-^a`W;3o&UY} z+Pf=pcLM(-*1JoVthFn}ZksdLmhW=f6>FX=slb(MRXoM^V#P|odh*GT z<8V0>b8T+hYFn}0syLj*Zo4g4ajk(N%O63vOR>G>a^xyGuKavQuEUw9Y;d^OD)U_v zi`A)$auf3%tBY(!8?zLvzb7~f3SC8R#a(2#D=w$9(BbsFtrU5jPJ2;tk|HxLwB@X` z<=GXdt-!7n*$Zrx$*>WU(B&2x5SwnYd|(O)_`rl!`*qJ?Wv?*eidqOiIOrCZ8G2q0 zW>kn#@aM4o_r?VT1dI##C_2Sw4S8>Og`lRPrlqE%rl)407DUZ>aCZe{#tfgO?F_6I zr5@a02jOy}+2Z5|DI#(51`&qI-4KEoYhjGIvXgO09azNiau&`ym^k*2JY+6L%7}ly z`bSlTv@H>-d)ZFeh2R?}X~@j8G^FPMx0Y>AhdU#IkiD3aTns6+vrJNe2XK$RuQRr*{J^p@OXX0&|)e0L#Rz+04q)H6wWWFI^z(=Qck!;G#NhD5p^&WAl!2>{NJdQv5m1Pb{K3gGEAyE*MuOE}^Pz4jdQLOX&+?rhRI;We_p4X^U!2Ki^-76 zOAPIzOH&r>LuP!b3#Mz{oT#cZ6AWSO`X9N3B>}x%w3o|f%5NN6Z_r7UWJwW)Kiq&> z!eC{QzsfL@JI=aE9oKKfowukIYTl;td?meI;%k3r{L@V;iX$abn9*hw8uG zf<1R&B!2lT#9(<34Y>7rCLQyF)K~R^#Jd(qJQgSSQWbb!WwE0d=8LMOEPiRG5)HpY z-l@x&EWATSH{(se0CXNQs)b!Ijmu8R*h(=o2D9&iRir1&Z#?cz8nO-ECEKQFB{rjX zsxgFjWN9Da9aQoX@93i&Iz9{Q`e>-{{)$uXL5}!w74EtR3q@%z*5(-F`1y>uFDG4t zmWT8<73LcaSZOz!aOHg}!LR~9R^OXnq}x=;`0;&uDT{p~0vA1?qb=PoBR)ZYXSNMx zeEI?Ps@x-E(ZkMg{6oU}!xFC$8;@|;#!6a{@3LNy;Sxh$S5Ei1&d68W8g#|9x<4@ZIi8Tmg`q4@Lw delta 2435 zcmY+G2~bo=5Qf=#d$w{c5D;WV@Q9*8qyjMtvS?zI2*Eosk;9WjjaU(5l?7_FJg{O= zOWIo^R;&_HsUlHSqKJwbO+-Y+gG)d;L=F{1j`X~Fth!aRZ~w1*rf0fm=50>bkh+c` ztJkSA+0tbg!A4;XXHdS@i7a zpXLE9Skia6x34dRf2q^Q>^_(1Vr6CJg2T-k(}wV4jG9DEO-(~hOHD^jPt9;L{1`-f zf+a=|%(@A)+397tG#?x>D~>thzB^!t)AGTUyob<+y zROXB)a>35cG7p-IyOn{y#plqPrlZ35^$fi&p11{GEIM6~#mAT(ZYiX2&IL4RN`}B; z1rWs8X(4RRuwHTmyv2R?ToTy&7So~KZSZHRd<#(=H{T}5H$?*L9xx*=&Z8KQIZkd8 z*tHPM(iwwpN%pv;oCccC>0J+*mhj?H z#=KZjKLUMlV+DAk;V$>^m%+6g@FD)dfk%K0d;%po4|bT#>AXOhcHD(Pb~UQ|w+>69 zC8L3ZtZqaAiD~P8ft&xr6Nj4XR=^gV3f7 zyrhgoS-Ii4%^D@+E6T`gW~$OLt&B3>oUW)h$22aYm_y2G5xE)ISPo7&s~iHD{YfEQ zPHB|GS4ci8S8OPUSu7!&TL)s?V=9;bKU|pbKQ49^ScC?>D~KGg79_hyV~_juX{#Qo zplE-0$#}+V6kF&hTa&9`Bzx@D{acN* zswmFl38L%D5Uo+fxmQIaZl5Bs@d=pOxF{iFpTMUqOA%L|&?b*rCM?V2w90{<_>_8g zZ4uZysT-$r?35+2xf(QB`;-p+{OcBCX1hwcaL3MS(BknjZBJ3s+-mB2_=&|9tTLjx zhSJ%;CmQQ$)$Uzd1CFxRDYdJrA^&Thimet#upI-1SYAt0UgX6^e=Mq{_-jXS5y_5( z@@F4Rs-qnFMp}d#>+2{oy7!S`WeO-$WtO>uJLT926oXRi_km*mLS|_=3Ql?UauVoVex+a{f6L+z&T3(9*_MauLDOY|x{TmKg5GpC&xe2%}j|phf7DgY-(~1~k#8q2xBf zWcJtR7~BLO;Ms9{N6c=bOEKpQK_bWLwRmH^-UUs~gi|KJkF7ClO80=-B z#0@jJ3!eh+bJ$nl9hZ#P`EviH;#Ja%?`R%FKZPvx{