1- name : ' 跨平台 Release 打包'
1+ name : ' Release 打包'
2+
3+ # 触发时机:
4+ # 1. 用户在 GitHub 上「发布」一个版本(Release published / 从草稿发布)——主路径;
5+ # 2. 推送 v* 标签;
6+ # 3. 手动触发(需指定标签)。
7+ # 产物:六平台可执行文件 tar/zip + linux/amd64、linux/arm64 两个 Docker 镜像 tar 包。
28on :
39 workflow_dispatch :
10+ inputs :
11+ tag :
12+ description : ' 要发布/上传的版本标签,例如 v1.0.20260920'
13+ required : false
14+ type : string
415 push :
516 tags :
617 - ' v*'
18+ release :
19+ types : [published, released]
720
821permissions :
922 contents : write
1023
24+ # 同一标签可能同时被「推标签」和「发布 Release」触发,串行执行避免重复上传
25+ concurrency :
26+ group : ' release-${{ github.workflow }}-${{ github.ref }}'
27+ cancel-in-progress : false
28+
29+ env :
30+ BIN_NAME : simple-web-shell
31+
1132jobs :
33+ meta :
34+ name : ' 解析发布标签'
35+ runs-on : ubuntu-latest
36+ outputs :
37+ tag : ${{ steps.vars.outputs.tag }}
38+ publish : ${{ steps.vars.outputs.publish }}
39+ steps :
40+ - name : ' 检出代码'
41+ uses : actions/checkout@v4
42+ with :
43+ ref : ${{ inputs.tag || github.event.release.tag_name || github.ref }}
44+
45+ - id : vars
46+ name : ' 解析标签并校验版本号'
47+ shell : bash
48+ run : |
49+ set -euo pipefail
50+ tag='${{ inputs.tag || github.event.release.tag_name || github.ref_name }}'
51+ publish=false
52+ case "$tag" in
53+ v*) publish=true ;;
54+ *) publish=false ;;
55+ esac
56+ echo "tag=$tag" >> "$GITHUB_OUTPUT"
57+ echo "publish=$publish" >> "$GITHUB_OUTPUT"
58+ echo "发布标签: ${tag:-<未指定>}"
59+ echo "是否发布 Release: $publish"
60+ if [ "$publish" = "true" ]; then
61+ version="$(grep -oP '(?<=version := ")[^"]+' main.go | head -n1)"
62+ echo "源码版本号: ${version}"
63+ if [ "$tag" != "v${version}" ]; then
64+ echo "::error::发布标签 ${tag} 与源码版本号 v${version} 不一致,请先把 main.go 里的版本号改成 ${tag#v} 再发布。"
65+ exit 1
66+ fi
67+ fi
68+
1269 build :
13- name : ' 构建 ${{ matrix.target }}'
70+ name : ' 编译打包 ${{ matrix.target }}'
71+ needs : meta
1472 runs-on : ubuntu-latest
1573 strategy :
1674 fail-fast : false
@@ -23,50 +81,141 @@ jobs:
2381 - { target: windows-amd64, goos: windows, goarch: amd64, ext: .exe }
2482 - { target: windows-arm64, goos: windows, goarch: arm64, ext: .exe }
2583 steps :
26- - uses : actions/checkout@v4
27- - uses : actions/setup-go@v5
28- with :
29- go-version : ' 1.25'
30- - name : ' 编译 ${{ matrix.target }}'
31- run : |
32- mkdir -p dist
33- CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \
34- go build -trimpath -ldflags="-s -w" -o dist/simple-web-shell${{ matrix.ext }} .
35- - name : ' 打包'
36- run : |
37- cd dist
38- if [ "${{ matrix.goos }}" = "windows" ]; then
39- zip -qr ../simple-web-shell-${{ matrix.target }}.zip simple-web-shell.exe
40- else
41- tar czf ../simple-web-shell-${{ matrix.target }}.tar.gz simple-web-shell
42- fi
43- - uses : actions/upload-artifact@v4
44- with :
45- name : ' simple-web-shell-${{ matrix.target }}'
46- path : ' ${{ github.workspace }}/simple-web-shell-${{ matrix.target }}.*'
47- if-no-files-found : ' error'
48- retention-days : 1
84+ - name : ' 检出代码'
85+ uses : actions/checkout@v4
86+ with :
87+ ref : ${{ inputs.tag || github.event.release.tag_name || github.ref }}
88+
89+ - name : ' 安装 Go'
90+ uses : actions/setup-go@v5
91+ with :
92+ go-version : ' 1.25'
93+
94+ - name : ' 单元测试'
95+ run : go test ./...
96+
97+ - name : ' 静态检查 go vet'
98+ run : go vet ./...
99+
100+ - name : ' 编译 ${{ matrix.target }}'
101+ run : |
102+ CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \
103+ go build -trimpath -ldflags="-s -w" \
104+ -o "$BIN_NAME${{ matrix.ext }}" .
105+
106+ - name : ' 打包 tar/zip'
107+ run : |
108+ set -euo pipefail
109+ target='${{ matrix.target }}'
110+ bin="$BIN_NAME${{ matrix.ext }}"
111+ stage="$BIN_NAME-$target"
112+ rm -rf "$stage" && mkdir -p "$stage"
113+ cp "$bin" "$stage/"
114+ for doc in README.md LICENSE; do
115+ [ -f "$doc" ] && cp "$doc" "$stage/"
116+ done
117+ files=()
118+ for f in "$stage"/*; do files+=("${f##*/}"); done
119+ if [ '${{ matrix.goos }}' = 'windows' ]; then
120+ ( cd "$stage" && zip -qr "../$stage.zip" "${files[@]}" )
121+ else
122+ tar czf "$stage.tar.gz" -C "$stage" "${files[@]}"
123+ fi
124+ ls -l "$stage".tar.gz "$stage".zip 2>/dev/null || true
125+
126+ - name : ' 上传打包工件'
127+ uses : actions/upload-artifact@v4
128+ with :
129+ name : ' bin-${{ matrix.target }}'
130+ path : ' simple-web-shell-${{ matrix.target }}.*'
131+ if-no-files-found : ' error'
132+ retention-days : 7
133+
134+ docker :
135+ name : ' 构建 Docker 镜像 ${{ matrix.arch }}'
136+ needs : meta
137+ runs-on : ubuntu-latest
138+ strategy :
139+ fail-fast : false
140+ matrix :
141+ include :
142+ - { arch: amd64, platform: 'linux/amd64' }
143+ - { arch: arm64, platform: 'linux/arm64' }
144+ steps :
145+ - name : ' 检出代码'
146+ uses : actions/checkout@v4
147+ with :
148+ ref : ${{ inputs.tag || github.event.release.tag_name || github.ref }}
149+
150+ - name : ' 启用 QEMU(arm64 镜像构建)'
151+ uses : docker/setup-qemu-action@v3
152+
153+ - name : ' 启用 Buildx'
154+ uses : docker/setup-buildx-action@v3
155+
156+ - name : ' 构建镜像并导出 tar 包'
157+ run : |
158+ set -euo pipefail
159+ out="$BIN_NAME-image-${{ matrix.arch }}.tar"
160+ docker buildx build \
161+ --platform ${{ matrix.platform }} \
162+ --tag "$BIN_NAME:${{ matrix.arch }}" \
163+ --output "type=docker,dest=$out" \
164+ .
165+ gzip -9 -f "$out"
166+
167+ - name : ' 上传镜像工件'
168+ uses : actions/upload-artifact@v4
169+ with :
170+ name : ' docker-image-${{ matrix.arch }}'
171+ path : ' simple-web-shell-image-${{ matrix.arch }}.tar.gz'
172+ if-no-files-found : ' error'
173+ retention-days : 7
49174
50175 release :
51176 name : ' 发布 GitHub Release'
52- needs : build
177+ needs : [build, docker]
178+ if : needs.meta.outputs.publish == 'true'
53179 runs-on : ubuntu-latest
54180 steps :
55- - uses : actions/download-artifact@v4
56- with :
57- path : artifacts
58- merge-multiple : true
59- - name : ' 整理产物'
60- run : |
61- mkdir -p release
62- find artifacts -type f | while read -r f; do
63- case "$f" in
64- *.tar.gz|*.zip) cp "$f" release/ ;;
65- esac
66- done
67- - name : ' 上传到 Release'
68- uses : softprops/action-gh-release@v2
69- with :
70- files : ' release/*'
71- generate_release_notes : true
72- fail_on_unmatched_files : false
181+ - name : ' 汇总全部产物'
182+ uses : actions/download-artifact@v4
183+ with :
184+ pattern : ' *'
185+ path : artifacts
186+ merge-multiple : true
187+
188+ - name : ' 生成 SHA256/MD5/SHA1 校验文件'
189+ run : |
190+ set -euo pipefail
191+ cd artifacts
192+ find . -type f \
193+ ! -name 'SHA256SUMS.txt' \
194+ ! -name 'checksums-md5.txt' \
195+ ! -name 'checksums-sha1.txt' -print0 \
196+ | sort -z | xargs -0 -r sha256sum > SHA256SUMS.txt
197+ find . -type f \
198+ ! -name 'SHA256SUMS.txt' \
199+ ! -name 'checksums-md5.txt' \
200+ ! -name 'checksums-sha1.txt' -print0 \
201+ | sort -z | xargs -0 -r md5sum > checksums-md5.txt
202+ find . -type f \
203+ ! -name 'SHA256SUMS.txt' \
204+ ! -name 'checksums-md5.txt' \
205+ ! -name 'checksums-sha1.txt' -print0 \
206+ | sort -z | xargs -0 -r sha1sum > checksums-sha1.txt
207+ cat SHA256SUMS.txt checksums-md5.txt checksums-sha1.txt
208+
209+ - name : ' 上传到 Release'
210+ uses : softprops/action-gh-release@v2
211+ with :
212+ tag_name : ${{ needs.meta.outputs.tag }}
213+ name : ${{ needs.meta.outputs.tag }}
214+ generate_release_notes : true
215+ files : |
216+ artifacts/*.tar.gz
217+ artifacts/*.zip
218+ artifacts/SHA256SUMS.txt
219+ artifacts/checksums-md5.txt
220+ artifacts/checksums-sha1.txt
221+ fail_on_unmatched_files : true
0 commit comments