name: Release on: push: tags: - "v*" workflow_dispatch: inputs: release_tag: description: Existing Git tag to publish required: true default: v1.0.0 release_name: description: Human-readable release name required: true default: aria2-rust-pro 1.0.0 draft: description: Publish as draft required: true default: "false" validation_only: description: Run release gates without publishing required: true default: "true" permissions: contents: read releases: write jobs: release: name: Build and Publish Release runs-on: ubuntu-latest timeout-minutes: 180 env: CARGO_TERM_COLOR: always CARGO_BUILD_JOBS: "2" CARGO_TARGET_DIR: target/release-ci CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse CARGO_HTTP_TIMEOUT: "60" CARGO_HTTP_MULTIPLEXING: "false" CARGO_NET_RETRY: "2" GITEA_SERVER_URL: ${{ github.server_url }} GITEA_REPOSITORY: ${{ github.repository }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} GITEA_CURL_INSECURE: "true" steps: - name: Checkout timeout-minutes: 5 env: GITEA_ACTIONS_TOKEN: ${{ secrets.GITEA_TOKEN }} GITEA_INTERNAL_URL: http://gitea:3000 run: | set -euo pipefail repository="${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" sha="${GITHUB_SHA:?GITHUB_SHA is required}" workspace="${GITHUB_WORKSPACE:?GITHUB_WORKSPACE is required}" token="${GITEA_ACTIONS_TOKEN:?GITEA_ACTIONS_TOKEN is required}" server_url="${GITEA_INTERNAL_URL%/}" remote_url="${server_url}/${repository}" auth="$(printf 'x-access-token:%s' "${token}" | base64 | tr -d '\n')" extra_header="AUTHORIZATION: basic ${auth}" mkdir -p "${workspace}" cd "${workspace}" git init . if git remote get-url origin >/dev/null 2>&1; then git remote set-url origin "${remote_url}" else git remote add origin "${remote_url}" fi git -c "http.${server_url}/.extraheader=${extra_header}" \ fetch --prune --no-recurse-submodules origin \ +refs/heads/*:refs/remotes/origin/* \ +refs/tags/*:refs/tags/* if ! git cat-file -e "${sha}^{commit}" 2>/dev/null; then git -c "http.${server_url}/.extraheader=${extra_header}" \ fetch --no-recurse-submodules origin "${sha}" fi git clean -ffdx git checkout --force "${sha}" git clean -ffdx git log -1 --format=%H - name: Select release ref env: RELEASE_EVENT_NAME: ${{ github.event_name }} RELEASE_INPUT_TAG: ${{ inputs.release_tag }} RELEASE_INPUT_NAME: ${{ inputs.release_name }} RELEASE_INPUT_DRAFT: ${{ inputs.draft }} RELEASE_INPUT_VALIDATION_ONLY: ${{ inputs.validation_only }} run: | set -euo pipefail if [ "${RELEASE_EVENT_NAME}" = "workflow_dispatch" ]; then validation_only="${RELEASE_INPUT_VALIDATION_ONLY:-true}" tag="${RELEASE_INPUT_TAG}" name="${RELEASE_INPUT_NAME}" draft="${RELEASE_INPUT_DRAFT}" case "${validation_only}" in true|false) ;; *) echo "release validation_only input must be true or false: ${validation_only}" >&2 exit 1 ;; esac if [ "${validation_only}" = "true" ]; then publish="false" else publish="true" git checkout "refs/tags/${tag}" fi else tag="${GITHUB_REF_NAME}" name="aria2-rust-pro ${tag#v}" draft="false" publish="true" fi if ! printf '%s\n' "${tag}" | grep -Eq '^v[0-9]+[.][0-9]+[.][0-9]+$'; then echo "release tag must look like vMAJOR.MINOR.PATCH: ${tag}" >&2 exit 1 fi case "${draft}" in true|false) ;; *) echo "release draft input must be true or false: ${draft}" >&2 exit 1 ;; esac version="${tag#v}" manifest_version="$( awk ' $0 == "[workspace.package]" { in_workspace_package = 1; next } /^\[/ { in_workspace_package = 0 } in_workspace_package && $1 == "version" { gsub(/"/, "", $3) print $3 exit } ' Cargo.toml )" if [ -z "${manifest_version}" ]; then echo "could not read workspace package version from Cargo.toml" >&2 exit 1 fi if [ "${version}" != "${manifest_version}" ]; then echo "release tag ${tag} does not match Cargo.toml version ${manifest_version}" >&2 exit 1 fi target="$(git rev-parse HEAD)" notes_file="docs/release/${tag}.md" { echo "RELEASE_TAG=${tag}" echo "RELEASE_NAME=${name}" echo "RELEASE_DRAFT=${draft}" echo "RELEASE_VERSION=${version}" echo "RELEASE_TARGET=${target}" echo "RELEASE_BODY_FILE=${notes_file}" echo "RELEASE_PUBLISH=${publish}" } >> "${GITHUB_ENV}" - name: Install Linux dependencies timeout-minutes: 10 run: ./scripts/ci/install-linux-deps.sh - name: Bootstrap Rust toolchains timeout-minutes: 25 run: ./scripts/ci/bootstrap-rust.sh - name: Install Cargo tools timeout-minutes: 30 run: ./scripts/ci/install-cargo-tools.sh strict - name: Generate release notes when absent timeout-minutes: 5 run: | set -euo pipefail if [ -f "${RELEASE_BODY_FILE}" ]; then exit 0 fi mkdir -p "$(dirname "${RELEASE_BODY_FILE}")" git-cliff --config cliff.toml --tag "${RELEASE_TAG}" > "${RELEASE_BODY_FILE}" test -s "${RELEASE_BODY_FILE}" - name: Run fast quality gates timeout-minutes: 45 run: ./scripts/ci/run-fast-gates.sh - name: Run strict quality gates timeout-minutes: 45 run: ./scripts/ci/run-strict-gates.sh - name: Check SemVer API compatibility timeout-minutes: 45 run: ./scripts/ci/run-semver-checks.sh "${RELEASE_TAG}" - name: Build local release artifact timeout-minutes: 30 run: cargo run --manifest-path ./xtask/Cargo.toml -- release package-local --build - name: Export Docker image tar timeout-minutes: 20 run: cargo run --manifest-path ./xtask/Cargo.toml -- docker export-local --build --tag aria2-rust-pro:release - name: Publish Gitea release timeout-minutes: 10 run: | if [ "${RELEASE_PUBLISH:-true}" != "true" ]; then echo "Release validation only; skipping Gitea publish" exit 0 fi ./scripts/ci/publish-gitea-release.sh