name: Release NuGet Packages on: release: types: [published] workflow_dispatch: inputs: package_version: description: Optional SemVer package version. Defaults to a timestamped dev prerelease. type: string required: false push_to_baget: description: Push to internal BaGet (nuget.sabp.ir) type: boolean default: true push_to_nuget_org: description: Also push to nuget.org (manual opt-in only) type: boolean default: false env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true HARBOR_REGISTRY: reg.sabp.ir SDK_IMAGE: reg.sabp.ir/sufi-chain/dotnet-sdk-build:10.0.202 GIT_HOST: git.sabp.ir REPOSITORY: ${{ gitea.repository }} REF_NAME: ${{ gitea.ref_name }} RELEASE_TAG: ${{ gitea.event.release.tag_name }} INPUT_PACKAGE_VERSION: ${{ inputs.package_version }} INPUT_PUSH_TO_BAGET: ${{ inputs.push_to_baget }} INPUT_PUSH_TO_NUGET_ORG: ${{ inputs.push_to_nuget_org }} BAGET_SOURCE_URL: https://nuget.sabp.ir/v3/index.json NUGET_ORG_SOURCE_URL: https://api.nuget.org/v3/index.json NUGET_PUSH_PARALLELISM: 4 PACKAGE_OUTPUT: ./artifacts/nuget PACKAGE_PROJECTS: src/SufiChain.SufiBlazor/SufiChain.SufiBlazor.csproj ROOT_SLNX: SufiChain.SufiBlazor.slnx WORKSPACE_DIR: ${{ github.workspace }} concurrency: group: release-nuget-${{ gitea.ref_name }} cancel-in-progress: false jobs: pack-and-push: runs-on: ubuntu-latest steps: - name: Check runner prerequisites shell: sh run: | set -eu if ! command -v docker >/dev/null 2>&1; then echo "docker CLI is required on the runner." >&2 echo "Map ubuntu-latest to docker:27-cli and mount /var/run/docker.sock into act_runner." >&2 exit 1 fi if ! docker version >/dev/null 2>&1; then echo "docker CLI exists, but cannot reach Docker daemon." >&2 echo "Check Docker service and /var/run/docker.sock mount/permissions for the Gitea runner." >&2 exit 1 fi - name: Login to Harbor shell: sh env: HARBOR_USERNAME: ${{ secrets.HARBOR_USERNAME }} HARBOR_PASSWORD: ${{ secrets.HARBOR_PASSWORD }} run: | set -eu if [ -z "${HARBOR_USERNAME:-}" ] || [ -z "${HARBOR_PASSWORD:-}" ]; then echo "HARBOR_USERNAME and HARBOR_PASSWORD secrets are required." >&2 exit 1 fi echo "$HARBOR_PASSWORD" | docker login "$HARBOR_REGISTRY" \ -u "$HARBOR_USERNAME" \ --password-stdin - name: Checkout shell: sh env: GITEATOKEN: ${{ secrets.GITEATOKEN }} run: | set -eu workspace="${WORKSPACE_DIR:-${GITHUB_WORKSPACE:-$PWD}}" cd "$workspace" echo "Using workspace: $workspace" if [ -f "$ROOT_SLNX" ]; then echo "Repository already checked out by the runner ($ROOT_SLNX)." exit 0 fi echo "Runner workspace is empty; cloning with SDK container." CLONE_URL="https://${GIT_HOST}/${REPOSITORY}.git" if [ -n "${GITEATOKEN:-}" ]; then CLONE_URL="$(printf '%s' "$CLONE_URL" | sed "s#^https://#https://x-access-token:${GITEATOKEN}@#")" fi export CLONE_URL container_id="$(docker run -d \ -w /src \ -e REF_NAME \ -e CLONE_URL \ "$SDK_IMAGE" \ sleep 3600)" trap 'docker rm -f "$container_id" >/dev/null 2>&1 || true' EXIT INT TERM docker exec "$container_id" sh -c ' set -eu if [ -n "${REF_NAME:-}" ]; then git clone --depth 1 --branch "$REF_NAME" "$CLONE_URL" /src else git clone --depth 1 "$CLONE_URL" /src fi test -f "'"$ROOT_SLNX"'" ' docker cp "${container_id}:/src/." "${workspace}/" docker rm -f "$container_id" trap - EXIT INT TERM if [ ! -f "$ROOT_SLNX" ]; then echo "Checkout did not produce $ROOT_SLNX under $workspace" >&2 find "$workspace" -maxdepth 2 -type f -name '*.slnx' 2>/dev/null | head -20 >&2 || true exit 1 fi - name: Resolve package version id: version shell: sh run: | set -eu if [ -n "${RELEASE_TAG:-}" ]; then version="$RELEASE_TAG" elif [ -n "${INPUT_PACKAGE_VERSION:-}" ]; then version="$INPUT_PACKAGE_VERSION" else version="0.0.0-dev.$(date +%Y%m%d%H%M%S)" fi version="${version#v}" if ! printf '%s' "$version" | grep -Eq '^[0-9]+([.][0-9]+){2}(-[0-9A-Za-z][0-9A-Za-z.-]*)?([+][0-9A-Za-z][0-9A-Za-z.-]*)?$'; then echo "Invalid NuGet package version: $version" >&2 echo "Use SemVer like 1.2.3, 1.2.3-beta.1, or v1.2.3." >&2 exit 1 fi echo "version=$version" >> "$GITHUB_OUTPUT" echo "Resolved version: $version" - name: Restore, build and pack packages shell: sh env: VERSION: ${{ steps.version.outputs.version }} run: | set -eu if [ -z "${VERSION:-}" ]; then echo "Package version was not resolved (steps.version.outputs.version is empty)." >&2 exit 1 fi workspace="${WORKSPACE_DIR:-${GITHUB_WORKSPACE:-$PWD}}" cd "$workspace" echo "Using workspace: $workspace" package_dir="${workspace}/${PACKAGE_OUTPUT#./}" container_id="$(docker run -d \ -v "${workspace}:/src" \ -w /src \ -e VERSION \ -e ROOT_SLNX \ -e PACKAGE_PROJECTS \ -e DOTNET_NOLOGO \ -e DOTNET_CLI_TELEMETRY_OPTOUT \ -e DOTNET_SKIP_FIRST_TIME_EXPERIENCE \ "$SDK_IMAGE" \ sleep 3600)" trap 'docker rm -f "$container_id" >/dev/null 2>&1 || true' EXIT INT TERM docker exec "$container_id" sh -c ' set -eu rm -rf /src/artifacts/nuget mkdir -p /src/artifacts/nuget for project in $PACKAGE_PROJECTS; do dotnet restore "$project" \ --verbosity minimal \ -p:NuGetAudit=false dotnet build "$project" \ --configuration Release \ --no-restore \ --verbosity minimal \ -m \ -p:PackageVersion="$VERSION" \ -p:ContinuousIntegrationBuild=true \ -p:BuildInParallel=true \ -p:PackageOutputPath="/src/artifacts/nuget" \ -p:GeneratePackageOnBuild=true done ' rm -rf "$package_dir" mkdir -p "$package_dir" docker cp "${container_id}:/src/artifacts/nuget/." "$package_dir/" docker rm -f "$container_id" trap - EXIT INT TERM package_count="$(find "$package_dir" -type f -name '*.nupkg' ! -name '*.symbols.nupkg' | wc -l | tr -d ' ')" if [ "$package_count" = "0" ]; then echo "No NuGet packages were produced in $package_dir." >&2 exit 1 fi echo "Packed $package_count package(s) into $package_dir." - name: Push packages to BaGet if: ${{ gitea.event_name == 'release' || inputs.push_to_baget == true }} shell: sh env: NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} run: | set -eu if [ -z "${NUGET_API_KEY:-}" ]; then echo "NUGET_API_KEY secret is required." >&2 exit 1 fi workspace="${WORKSPACE_DIR:-${GITHUB_WORKSPACE:-$PWD}}" cd "$workspace" docker run --rm \ -v "${workspace}:/src" \ -w /src \ -e NUGET_API_KEY \ -e BAGET_SOURCE_URL \ -e NUGET_PUSH_PARALLELISM \ -e PACKAGE_OUTPUT \ "$SDK_IMAGE" \ sh -c ' set -eu package_dir="/src/${PACKAGE_OUTPUT#./}" find "$package_dir" -type f -name "*.nupkg" ! -name "*.symbols.nupkg" -print0 | \ xargs -0 -n 1 -P "$NUGET_PUSH_PARALLELISM" sh -c '\'' dotnet nuget push "$1" \ --source "$BAGET_SOURCE_URL" \ --api-key "$NUGET_API_KEY" \ --skip-duplicate '\'' sh ' - name: Push packages to nuget.org if: ${{ gitea.event_name == 'workflow_dispatch' && inputs.push_to_nuget_org == true && inputs.package_version != '' }} shell: sh env: NUGET_ORG_API_KEY: ${{ secrets.NUGET_ORG_API_KEY }} run: | set -eu if [ -z "${NUGET_ORG_API_KEY:-}" ]; then echo "NUGET_ORG_API_KEY secret is required for nuget.org push." >&2 exit 1 fi workspace="${WORKSPACE_DIR:-${GITHUB_WORKSPACE:-$PWD}}" cd "$workspace" docker run --rm \ -v "${workspace}:/src" \ -w /src \ -e NUGET_ORG_API_KEY \ -e NUGET_ORG_SOURCE_URL \ -e NUGET_PUSH_PARALLELISM \ -e PACKAGE_OUTPUT \ "$SDK_IMAGE" \ sh -c ' set -eu package_dir="/src/${PACKAGE_OUTPUT#./}" find "$package_dir" -type f -name "*.nupkg" ! -name "*.symbols.nupkg" -print0 | \ xargs -0 -n 1 -P "$NUGET_PUSH_PARALLELISM" sh -c '\'' dotnet nuget push "$1" \ --source "$NUGET_ORG_SOURCE_URL" \ --api-key "$NUGET_ORG_API_KEY" \ --skip-duplicate '\'' sh '