name: release on: workflow_dispatch: inputs: tag: description: "已存在的正式版本标签,例如 v0.3.17" required: true type: string push: tags: ["v*"] permissions: contents: read concurrency: group: release-${{ inputs.tag || github.ref_name }} cancel-in-progress: false jobs: prepare: runs-on: ubuntu-latest outputs: tag: ${{ steps.version.outputs.tag }} commit: ${{ steps.commit.outputs.sha }} steps: - name: Validate release version id: version env: RELEASE_TAG: ${{ inputs.tag || github.ref_name }} ARTEX_DOCKER_IMAGE: ${{ vars.ARTEX_DOCKER_IMAGE }} run: | if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then echo "正式版本必须是 v主版本.次版本.修订号,例如 v0.3.17" >&2 exit 1 fi if [[ ! "$ARTEX_DOCKER_IMAGE" =~ ^[a-z0-9][a-z0-9_-]*/[a-z0-9]+([._-][a-z0-9]+)*$ ]]; then echo "请先配置 Actions 变量 ARTEX_DOCKER_IMAGE,格式为 Docker Hub 用户名/镜像名" >&2 exit 1 fi echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" - uses: actions/checkout@v4 with: ref: refs/tags/${{ steps.version.outputs.tag }} - name: Pin release commit id: commit run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" # ① 前端静态导出(一次),产物给交叉编译各平台复用 frontend: needs: prepare runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ needs.prepare.outputs.commit }} - uses: actions/setup-node@v4 with: node-version: "22" cache: npm cache-dependency-path: web/package-lock.json - run: npm ci working-directory: web - run: npm run build:static working-directory: web - uses: actions/upload-artifact@v4 with: name: web-dist path: web/out # ② 五平台交叉编译(内嵌前端)+ 打 zip binaries: needs: [prepare, frontend] runs-on: ubuntu-latest strategy: matrix: include: - { goos: linux, goarch: amd64 } - { goos: linux, goarch: arm64 } - { goos: darwin, goarch: amd64 } - { goos: darwin, goarch: arm64 } - { goos: windows, goarch: amd64 } steps: - uses: actions/checkout@v4 with: ref: ${{ needs.prepare.outputs.commit }} - uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - uses: actions/download-artifact@v4 with: name: web-dist path: server/webui/dist - name: Build and package with build.sh env: ARTEX_TARGET_OS: ${{ matrix.goos }} ARTEX_TARGET_ARCH: ${{ matrix.goarch }} ARTEX_BUILD_VERSION: ${{ needs.prepare.outputs.tag }} ARTEX_SKIP_FRONTEND: "1" ARTEX_SKIP_NPM_CI: "1" # UPX 自解压 ELF 在部分 Linux 内核、虚拟化和安全策略下会崩溃。 # Release 使用 Go linker 裁剪和 zip 压缩,优先保证可移植性。 ARTEX_COMPRESS: "0" ARTEX_PACKAGE: "1" ARTEX_PACKAGE_DIR: dist run: | ./build.sh --target "${ARTEX_TARGET_OS}/${ARTEX_TARGET_ARCH}" - name: Smoke test Linux amd64 binary if: matrix.goos == 'linux' && matrix.goarch == 'amd64' run: dist/artex-linux-amd64/artex -h - uses: actions/upload-artifact@v4 with: name: pkg-${{ matrix.goos }}-${{ matrix.goarch }} path: "dist/*.zip" # Linux 生二进制单独上传,供 docker job 复用(免在镜像里重编前端+Go) - if: matrix.goos == 'linux' uses: actions/upload-artifact@v4 with: name: bin-linux-${{ matrix.goarch }} path: dist/artex-linux-${{ matrix.goarch }}/artex # ③ 汇总所有 zip → 创建 GitHub Release release: needs: [prepare, binaries, docker-publish] runs-on: ubuntu-latest permissions: contents: write env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} RELEASE_TAG: ${{ needs.prepare.outputs.tag }} steps: - uses: actions/download-artifact@v4 with: pattern: pkg-* merge-multiple: true path: dist - name: Generate checksums working-directory: dist run: | version="${RELEASE_TAG#v}" for target in linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64; do test -s "artex-${version}-${target}.zip" done sha256sum *.zip > SHA256SUMS sha256sum --check SHA256SUMS - name: Refuse to overwrite a published release run: | if gh release view "$RELEASE_TAG" --json isDraft > /tmp/artex-release.json 2>/tmp/artex-release.err; then test "$(jq -r .isDraft /tmp/artex-release.json)" = true || { echo "该版本已发布,请使用新的版本标签" >&2 exit 1 } elif ! grep -qi 'release not found\|HTTP 404' /tmp/artex-release.err; then cat /tmp/artex-release.err >&2 exit 1 fi - uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.prepare.outputs.tag }} target_commitish: ${{ needs.prepare.outputs.commit }} draft: true fail_on_unmatched_files: true files: | dist/*.zip dist/SHA256SUMS generate_release_notes: true body: | Docker Hub 镜像:`${{ vars.ARTEX_DOCKER_IMAGE }}:${{ needs.prepare.outputs.tag }}`,同时提供 `latest`。 支持 Linux AMD64 / ARM64,Docker 会自动选择宿主机架构。 程序包提供 Linux、macOS 的 AMD64 / ARM64 及 Windows AMD64,并附 SHA256SUMS。 部署和在线更新说明见本仓库 README。 - name: Publish complete release run: gh release edit "$RELEASE_TAG" --draft=false --latest # ④ 在原生 AMD64 / ARM64 runner 上构建和测试,再推送架构镜像。 docker: needs: [prepare, binaries] strategy: fail-fast: false matrix: include: - { arch: amd64, runner: ubuntu-24.04 } - { arch: arm64, runner: ubuntu-24.04-arm } runs-on: ${{ matrix.runner }} services: postgres: image: postgres:16-alpine env: POSTGRES_USER: artex POSTGRES_PASSWORD: artex-ci-test POSTGRES_DB: artex ports: - 5432:5432 options: >- --health-cmd "pg_isready -U artex -d artex" --health-interval 5s --health-timeout 3s --health-retries 10 steps: - uses: actions/checkout@v4 # 取 Dockerfile 与 skills/ with: ref: ${{ needs.prepare.outputs.commit }} - uses: actions/download-artifact@v4 with: name: bin-linux-${{ matrix.arch }} path: dist/${{ matrix.arch }} - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build image for native smoke test uses: docker/build-push-action@v6 with: context: . platforms: linux/${{ matrix.arch }} load: true tags: artex:smoke cache-from: type=gha,scope=docker-${{ matrix.arch }} cache-to: type=gha,mode=max,scope=docker-${{ matrix.arch }} - name: Test application startup and embedded frontend env: RELEASE_TAG: ${{ needs.prepare.outputs.tag }} run: | docker run --rm --entrypoint /app/artex artex:smoke -h docker run -d --name artex-smoke --network host \ -e ARTEX_PG_DSN='postgres://artex:artex-ci-test@127.0.0.1:5432/artex?sslmode=disable' artex:smoke trap 'docker logs artex-smoke; docker rm -f artex-smoke' EXIT curl --fail --retry 30 --retry-connrefused --retry-delay 2 --max-time 3 \ http://127.0.0.1:8787/api/health > /tmp/artex-health.json jq -e --arg version "${RELEASE_TAG#v}" '.ok == true and .version == $version' /tmp/artex-health.json curl --fail http://127.0.0.1:8787/ > /tmp/artex-index.html grep -qi ' /tmp/artex-manifest.json jq -e '[.manifests[].platform | select(.os == "linux") | .architecture] | index("amd64") != null and index("arm64") != null' /tmp/artex-manifest.json