diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6bc24a8..1037127 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,99 +3,35 @@ name: Release on: push: tags: - - "v*" - - "[0-9]*" - workflow_dispatch: - -permissions: - contents: write + - "*" env: + GITEA_SERVER_URL: https://git.pchuan.top + RUST_IMAGE: docker.m.daocloud.io/library/rust:1-bookworm + CARGO_REGISTRY: sparse+https://rsproxy.cn/index/ CARGO_TERM_COLOR: always jobs: - build: - name: Build ${{ matrix.target }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - target: windows-x64 - os: windows-latest - script-target: win - - target: linux-x64 - os: ubuntu-latest - script-target: linux-x64 - - target: linux-arm64 - os: ubuntu-latest - script-target: linux-arm64 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Resolve version - shell: pwsh - run: | - if ($env:GITHUB_REF_TYPE -eq "tag") { - $version = $env:GITHUB_REF_NAME -replace '^v', '' - } else { - $cargoToml = Get-Content -Raw -Path "Cargo.toml" - if ($cargoToml -notmatch '(?m)^version\s*=\s*"([^"]+)"') { - throw "Cannot read package version from Cargo.toml" - } - $version = $Matches[1] - } - "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 - - - name: Install Windows Rust target - if: runner.os == 'Windows' - shell: pwsh - run: rustup target add x86_64-pc-windows-msvc - - - name: Set up QEMU - if: runner.os == 'Linux' - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - if: runner.os == 'Linux' - uses: docker/setup-buildx-action@v3 - - - name: Build release binary - shell: pwsh - run: ./scripts/publish-binaries.ps1 -Version "${env:VERSION}" -Target "${{ matrix.script-target }}" -Clean - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: cdxs-${{ matrix.target }} - path: dist/* - if-no-files-found: error - - release: - name: Publish GitHub Release - needs: build + prepare-release: + name: Prepare release runs-on: ubuntu-latest - if: github.ref_type == 'tag' - steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - path: dist - merge-multiple: true - - - name: Generate changelog + - name: Create or update Gitea release shell: bash + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | - current_tag="${GITHUB_REF_NAME}" + set -euo pipefail + + current_tag="$(git describe --tags --exact-match)" + version="${current_tag#v}" previous_tag="$(git describe --tags --abbrev=0 "${current_tag}^{}^" 2>/dev/null || true)" + api_base="${GITEA_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" { echo "## Changes" @@ -113,13 +49,188 @@ jobs: echo echo "## Artifacts" echo - echo "- \`cdxs-${current_tag#v}-windows-x64.exe\`" - echo "- \`cdxs-${current_tag#v}-linux-x64\`" - echo "- \`cdxs-${current_tag#v}-linux-arm64\`" + echo "- \`cdxs-${version}-windows-x64.exe\`" + echo "- \`cdxs-${version}-linux-x64\`" + echo "- \`cdxs-${version}-linux-arm64\`" } > RELEASE_NOTES.md - - name: Publish release - uses: softprops/action-gh-release@v2 - with: - files: dist/* - body_path: RELEASE_NOTES.md + body="$(python3 -c 'import json, pathlib; print(json.dumps(pathlib.Path("RELEASE_NOTES.md").read_text()))')" + payload="$(printf '{"tag_name":"%s","target_commitish":"%s","name":"%s","body":%s,"draft":false,"prerelease":false}' \ + "${current_tag}" "${GITHUB_SHA}" "${current_tag}" "${body}")" + + status="$(curl -sS -o release.json -w '%{http_code}' \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${api_base}/releases/tags/${current_tag}")" + + if [[ "${status}" == "200" ]]; then + release_id="$(python3 -c 'import json; print(json.load(open("release.json"))["id"])')" + curl -sS -X PATCH \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "${payload}" \ + "${api_base}/releases/${release_id}" >/dev/null + elif [[ "${status}" == "404" ]]; then + curl -sS -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "${payload}" \ + "${api_base}/releases" >/dev/null + else + cat release.json + echo "Unexpected Gitea release lookup status: ${status}" >&2 + exit 1 + fi + + build-windows-x64: + name: Build windows-x64 + runs-on: windows-latest + needs: prepare-release + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust target + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.cargo" | Out-Null + @" + [source.crates-io] + replace-with = "mirror" + + [source.mirror] + registry = "$env:CARGO_REGISTRY" + "@ | Set-Content -Path "$env:USERPROFILE\.cargo\config.toml" -Encoding utf8 + rustup target add x86_64-pc-windows-msvc + + - name: Build release binary + shell: pwsh + run: | + $tag = git describe --tags --exact-match + $version = $tag -replace '^v', '' + ./scripts/publish-binaries.ps1 -Version $version -Target win -Clean + + - name: Upload release asset + shell: pwsh + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + $tag = git describe --tags --exact-match + $release = Invoke-RestMethod ` + -Headers @{ Authorization = "token $env:GITEA_TOKEN" } ` + -Uri "$env:GITEA_SERVER_URL/api/v1/repos/$env:GITHUB_REPOSITORY/releases/tags/$tag" + $file = Get-ChildItem -Path dist -File | Select-Object -First 1 + curl.exe -fsS -X POST ` + -H "Authorization: token $env:GITEA_TOKEN" ` + -F "attachment=@$($file.FullName)" ` + "$env:GITEA_SERVER_URL/api/v1/repos/$env:GITHUB_REPOSITORY/releases/$($release.id)/assets?name=$($file.Name)" + + build-linux-x64: + name: Build linux-x64 + runs-on: ubuntu-latest + needs: prepare-release + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build release binary + shell: bash + run: | + set -euo pipefail + + tag="$(git describe --tags --exact-match)" + version="${tag#v}" + target_name="linux-x64" + rust_target="x86_64-unknown-linux-gnu" + platform="linux/amd64" + image_tag="cdxs-build:${target_name}-${GITHUB_RUN_ID:-manual}" + container_name="cdxs-build-${target_name}-${GITHUB_RUN_ID:-manual}" + + mkdir -p dist + docker buildx build \ + --platform "${platform}" \ + --target builder \ + --build-arg "RUST_TARGET=${rust_target}" \ + --build-arg "RUST_IMAGE=${RUST_IMAGE}" \ + --build-arg "CARGO_REGISTRY=${CARGO_REGISTRY}" \ + --load \ + -t "${image_tag}" \ + -f scripts/docker/Dockerfile.release \ + . + + container_id="$(docker create --name "${container_name}" "${image_tag}")" + trap 'docker rm -f "${container_name}" >/dev/null 2>&1 || true' EXIT + docker cp "${container_id}:/out/cdxs" "dist/cdxs-${version}-${target_name}" + + - name: Upload release asset + shell: bash + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + set -euo pipefail + + tag="$(git describe --tags --exact-match)" + api_base="${GITEA_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" + curl -sS -H "Authorization: token ${GITEA_TOKEN}" "${api_base}/releases/tags/${tag}" > release.json + release_id="$(python3 -c 'import json; print(json.load(open("release.json"))["id"])')" + file="$(find dist -maxdepth 1 -type f | head -n 1)" + name="$(basename "${file}")" + curl -fsS -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -F "attachment=@${file}" \ + "${api_base}/releases/${release_id}/assets?name=${name}" + + build-linux-arm64: + name: Build linux-arm64 + runs-on: ubuntu-latest + needs: prepare-release + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build release binary + shell: bash + run: | + set -euo pipefail + + tag="$(git describe --tags --exact-match)" + version="${tag#v}" + target_name="linux-arm64" + rust_target="aarch64-unknown-linux-gnu" + platform="linux/arm64" + image_tag="cdxs-build:${target_name}-${GITHUB_RUN_ID:-manual}" + container_name="cdxs-build-${target_name}-${GITHUB_RUN_ID:-manual}" + + mkdir -p dist + docker buildx build \ + --platform "${platform}" \ + --target builder \ + --build-arg "RUST_TARGET=${rust_target}" \ + --build-arg "RUST_IMAGE=${RUST_IMAGE}" \ + --build-arg "CARGO_REGISTRY=${CARGO_REGISTRY}" \ + --load \ + -t "${image_tag}" \ + -f scripts/docker/Dockerfile.release \ + . + + container_id="$(docker create --name "${container_name}" "${image_tag}")" + trap 'docker rm -f "${container_name}" >/dev/null 2>&1 || true' EXIT + docker cp "${container_id}:/out/cdxs" "dist/cdxs-${version}-${target_name}" + + - name: Upload release asset + shell: bash + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + set -euo pipefail + + tag="$(git describe --tags --exact-match)" + api_base="${GITEA_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" + curl -sS -H "Authorization: token ${GITEA_TOKEN}" "${api_base}/releases/tags/${tag}" > release.json + release_id="$(python3 -c 'import json; print(json.load(open("release.json"))["id"])')" + file="$(find dist -maxdepth 1 -type f | head -n 1)" + name="$(basename "${file}")" + curl -fsS -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -F "attachment=@${file}" \ + "${api_base}/releases/${release_id}/assets?name=${name}" diff --git a/scripts/docker/Dockerfile.release b/scripts/docker/Dockerfile.release index 4e9e84d..c5287f4 100644 --- a/scripts/docker/Dockerfile.release +++ b/scripts/docker/Dockerfile.release @@ -2,9 +2,13 @@ ARG RUST_IMAGE=rust:1-bookworm FROM --platform=$BUILDPLATFORM ${RUST_IMAGE} AS builder ARG RUST_TARGET=x86_64-unknown-linux-gnu +ARG CARGO_REGISTRY=sparse+https://rsproxy.cn/index/ WORKDIR /app +RUN mkdir -p /usr/local/cargo \ + && printf '[source.crates-io]\nreplace-with = "mirror"\n\n[source.mirror]\nregistry = "%s"\n' "${CARGO_REGISTRY}" > /usr/local/cargo/config.toml + RUN apt-get update \ && apt-get install -y --no-install-recommends \ ca-certificates \