From dea81caa61d05c8da713e269e2f3c082fbac1957 Mon Sep 17 00:00:00 2001 From: alphago9 Date: Mon, 30 Mar 2026 01:37:08 +0800 Subject: [PATCH] build(release): add tag-based release automation and version metadata --- .github/workflows/docker-alpine.yml | 44 ++++++++++++----- .github/workflows/release.yml | 62 +++++++++++++++++++++++ Dockerfile | 6 ++- RELEASING.md | 74 ++++++++++++++++++++++++++++ src/.goreleaser.yaml | 76 ++++++++++++++++++++--------- src/bootstrap/bootstrap.go | 23 ++++----- src/command/banner.go | 18 +++++++ src/command/http_server.go | 12 +++-- src/command/version.go | 26 ++++++++++ src/config/config.go | 31 ++++++++---- src/main.go | 9 ++-- 11 files changed, 312 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 RELEASING.md create mode 100644 src/command/banner.go create mode 100644 src/command/version.go diff --git a/.github/workflows/docker-alpine.yml b/.github/workflows/docker-alpine.yml index 13fa017..8616180 100644 --- a/.github/workflows/docker-alpine.yml +++ b/.github/workflows/docker-alpine.yml @@ -1,28 +1,46 @@ name: docker-alpine-epusdt -on: - workflow_dispatch: #github页面手动触发 - push: - branches: [ "main" ] -env: - IMAGE_NAME: epusdt #这是您的镜像名 -jobs: - push-docker-hub: +on: + workflow_dispatch: + push: + branches: + - "main" + +env: + IMAGE_NAME: epusdt + +jobs: + push-docker-hub: runs-on: ubuntu-latest env: TZ: Asia/Shanghai + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 + + - name: Compute Build Metadata + id: meta + shell: bash + run: | + echo "version=main-${GITHUB_SHA}" >> "$GITHUB_OUTPUT" + echo "commit=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" + echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + - name: Login - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build && Push - uses: docker/build-push-action@v4 + + - name: Build And Push + uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile push: true - tags: | + build-args: | + VERSION=${{ steps.meta.outputs.version }} + COMMIT=${{ steps.meta.outputs.commit }} + DATE=${{ steps.meta.outputs.date }} + tags: | ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:alpine diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c09aeb6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + id-token: write + attestations: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + goreleaser: + name: Build And Publish Binaries + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Ensure Tag Commit Is On Default Branch + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + run: | + git fetch origin "$DEFAULT_BRANCH" --depth=1 + if ! git merge-base --is-ancestor "$GITHUB_SHA" "origin/$DEFAULT_BRANCH"; then + echo "tag ${GITHUB_REF_NAME} must reference a commit reachable from ${DEFAULT_BRANCH}" + exit 1 + fi + + - name: Set Up Go + uses: actions/setup-go@v6 + with: + go-version-file: src/go.mod + + - name: Install Cosign + uses: sigstore/cosign-installer@v4.1.1 + + - name: Install Syft + uses: anchore/sbom-action/download-syft@v0.24.0 + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v7 + with: + distribution: goreleaser + version: "~> v2" + workdir: src + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Attest Release Checksums + uses: actions/attest@v4 + with: + subject-checksums: src/dist/epusdt_${{ github.ref_name }}_checksums.txt diff --git a/Dockerfile b/Dockerfile index 0b4fa96..eca1c12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,15 @@ FROM golang:alpine AS builder RUN apk add --no-cache --update git build-base ENV CGO_ENABLED=1 +ARG VERSION=0.0.0-dev +ARG COMMIT=none +ARG DATE=unknown WORKDIR /app COPY ./src/go.mod ./src/go.sum ./ RUN go mod download COPY ./src . -RUN go mod tidy \ - && go build -ldflags "-s -w" \ +RUN go build -ldflags "-s -w -X github.com/assimon/luuu/config.BuildVersion=${VERSION} -X github.com/assimon/luuu/config.BuildCommit=${COMMIT} -X github.com/assimon/luuu/config.BuildDate=${DATE}" \ -o epusdt . FROM alpine:latest AS runner diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..64cf7f8 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,74 @@ +# Releasing + +This repository publishes versioned binaries to GitHub Releases when a tag is pushed. + +## What gets published + +For each tag like `v1.2.3`, GitHub Actions builds release archives for: + +- `linux/amd64` +- `linux/arm64` +- `darwin/amd64` +- `darwin/arm64` +- `windows/amd64` +- `windows/arm64` + +Each archive includes: + +- the `epusdt` binary +- `src/.env.example` +- `src/static/` + +Windows artifacts are published as `.zip`. Other platforms are published as `.tar.gz`. + +Each release also publishes: + +- a checksum file +- a Sigstore keyless signature bundle for the checksum file +- archive SBOM documents generated by Syft +- a GitHub build provenance attestation for the checksum file + +## How to cut a release + +1. Make sure the release commit has already landed on the default branch. +2. Create an annotated tag: + +```bash +git tag -a v1.0.0 -m "release v1.0.0" +``` + +3. Push the tag: + +```bash +git push origin v1.0.0 +``` + +4. Wait for the `release` workflow to finish on GitHub. +5. Download the generated binaries from the GitHub Release page. + +## Verify a release + +Verify the checksum signature with Cosign: + +```bash +cosign verify-blob \ + --bundle epusdt_v1.0.0_checksums.txt.sigstore.json \ + epusdt_v1.0.0_checksums.txt +``` + +Verify the GitHub build provenance attestation: + +```bash +gh attestation verify epusdt_v1.0.0_checksums.txt -R GMwalletApp/epusdt +``` + +## Local validation + +Install `cosign` and `syft`, then run a snapshot build locally before pushing a tag: + +```bash +cd src +go run github.com/goreleaser/goreleaser/v2@latest release --snapshot --clean +``` + +The resulting artifacts will be generated under `src/dist/`. diff --git a/src/.goreleaser.yaml b/src/.goreleaser.yaml index 205f477..8ca3083 100644 --- a/src/.goreleaser.yaml +++ b/src/.goreleaser.yaml @@ -1,44 +1,72 @@ -# This is an example .goreleaser.yml file with some sensible defaults. -# Make sure to check the documentation at https://goreleaser.com +version: 2 + project_name: epusdt + before: hooks: - # You may remove this if you don't use go modules. - #- go mod tidy - # you may remove this if you don't need go generate - #- go generate ./... + - go test ./... + builds: - - env: + - id: epusdt + main: . + binary: epusdt + env: - CGO_ENABLED=0 + flags: + - -trimpath + ldflags: + - -s -w -X github.com/assimon/luuu/config.BuildVersion={{ .Version }} -X github.com/assimon/luuu/config.BuildCommit={{ .Commit }} -X github.com/assimon/luuu/config.BuildDate={{ .Date }} goos: - - darwin - linux + - darwin - windows goarch: - amd64 - - arm - - 386 + - arm64 + archives: - - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 - id: epusdt - name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" - format: tar.gz + - id: epusdt + ids: + - epusdt + name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" files: - .env.example - - static/* - - runtime/* + - static/** + format_overrides: + - goos: windows + formats: + - zip + checksum: - name_template: 'checksums.txt' + name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt" + +signs: + - cmd: cosign + signature: "${artifact}.sigstore.json" + args: + - "sign-blob" + - "--bundle=${signature}" + - "${artifact}" + - "--yes" + artifacts: checksum + +sboms: + - id: archives + artifacts: archive + snapshot: name_template: "{{ incpatch .Version }}-next" + +release: + github: + owner: GMwalletApp + name: epusdt + changelog: sort: asc filters: exclude: - - '^docs:' - - '^test:' + - "^docs:" + - "^test:" + - "^chore:" + - "^ci:" diff --git a/src/bootstrap/bootstrap.go b/src/bootstrap/bootstrap.go index a615e9e..5452ee3 100644 --- a/src/bootstrap/bootstrap.go +++ b/src/bootstrap/bootstrap.go @@ -1,7 +1,8 @@ package bootstrap import ( - "github.com/assimon/luuu/command" + "sync" + "github.com/assimon/luuu/config" "github.com/assimon/luuu/model/dao" "github.com/assimon/luuu/mq" @@ -10,15 +11,15 @@ import ( "github.com/assimon/luuu/util/log" ) -func Start() { - config.Init() - log.Init() - dao.Init() - mq.Start() - go telegram.BotStart() - go task.Start() +var initOnce sync.Once - if err := command.Execute(); err != nil { - panic(err) - } +func InitApp() { + initOnce.Do(func() { + config.Init() + log.Init() + dao.Init() + mq.Start() + go telegram.BotStart() + go task.Start() + }) } diff --git a/src/command/banner.go b/src/command/banner.go new file mode 100644 index 0000000..bbbf8d7 --- /dev/null +++ b/src/command/banner.go @@ -0,0 +1,18 @@ +package command + +import ( + "github.com/assimon/luuu/config" + "github.com/gookit/color" +) + +func printBanner() { + color.Green.Printf("%s\n", " _____ _ _ \n | ____|_ __ _ _ ___ __| | |_ \n | _| | '_ \\| | | / __|/ _` | __|\n | |___| |_) | |_| \\__ \\ (_| | |_ \n |_____| .__/ \\__,_|___/\\__,_|\\__|\n |_| ") + color.Infof( + "Epusdt version(%s) commit(%s) built(%s) Powered by %s %s \n", + config.GetAppVersion(), + config.GetBuildCommit(), + config.GetBuildDate(), + "GMwalletApp", + "https://github.com/GMwalletApp/epusdt", + ) +} diff --git a/src/command/http_server.go b/src/command/http_server.go index f9e4dae..917737d 100644 --- a/src/command/http_server.go +++ b/src/command/http_server.go @@ -2,6 +2,12 @@ package command import ( "context" + "net/http" + "os" + "os/signal" + "time" + + "github.com/assimon/luuu/bootstrap" "github.com/assimon/luuu/config" "github.com/assimon/luuu/middleware" "github.com/assimon/luuu/route" @@ -12,10 +18,6 @@ import ( echoMiddleware "github.com/labstack/echo/v4/middleware" "github.com/spf13/cobra" "github.com/spf13/viper" - "net/http" - "os" - "os/signal" - "time" ) var httpCmd = &cobra.Command{ @@ -35,6 +37,8 @@ var startCmd = &cobra.Command{ Short: "启动", Long: "启动http服务", Run: func(cmd *cobra.Command, args []string) { + bootstrap.InitApp() + printBanner() HttpServerStart() }, } diff --git a/src/command/version.go b/src/command/version.go new file mode 100644 index 0000000..6394222 --- /dev/null +++ b/src/command/version.go @@ -0,0 +1,26 @@ +package command + +import ( + "fmt" + + "github.com/assimon/luuu/config" + "github.com/spf13/cobra" +) + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print build version information", + RunE: func(cmd *cobra.Command, args []string) error { + _, err := fmt.Fprintf(cmd.OutOrStdout(), + "version: %s\ncommit: %s\nbuilt: %s\n", + config.GetAppVersion(), + config.GetBuildCommit(), + config.GetBuildDate(), + ) + return err + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/src/config/config.go b/src/config/config.go index f9cadb4..a13d7c0 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -11,15 +11,18 @@ import ( ) var ( - AppDebug bool - MysqlDns string - RuntimePath string - LogSavePath string - StaticPath string - TgBotToken string - TgProxy string - TgManage int64 - UsdtRate float64 + AppDebug bool + MysqlDns string + RuntimePath string + LogSavePath string + StaticPath string + TgBotToken string + TgProxy string + TgManage int64 + UsdtRate float64 + BuildVersion = "0.0.0-dev" + BuildCommit = "none" + BuildDate = "unknown" ) func Init() { @@ -65,7 +68,15 @@ func mustMkdir(path string) { } func GetAppVersion() string { - return "0.0.2" + return BuildVersion +} + +func GetBuildCommit() string { + return BuildCommit +} + +func GetBuildDate() string { + return BuildDate } func GetAppName() string { diff --git a/src/main.go b/src/main.go index f6015c2..522e672 100644 --- a/src/main.go +++ b/src/main.go @@ -1,8 +1,7 @@ package main import ( - "github.com/assimon/luuu/bootstrap" - "github.com/assimon/luuu/config" + "github.com/assimon/luuu/command" "github.com/gookit/color" ) @@ -12,7 +11,7 @@ func main() { color.Error.Println("[Start Server Err!!!] ", err) } }() - color.Green.Printf("%s\n", " _____ _ _ \n | ____|_ __ _ _ ___ __| | |_ \n | _| | '_ \\| | | / __|/ _` | __|\n | |___| |_) | |_| \\__ \\ (_| | |_ \n |_____| .__/ \\__,_|___/\\__,_|\\__|\n |_| ") - color.Infof("Epusdt version(%s) Powered by %s %s \n", config.GetAppVersion(), "GMwalletApp", "https://github.com/GMwalletApp/epusdt") - bootstrap.Start() + if err := command.Execute(); err != nil { + panic(err) + } }