This is an automated email from the ASF dual-hosted git repository. EmryZhang pushed a commit to branch prepare/net-release-workflow in repository https://gitbox.apache.org/repos/asf/casbin-Casbin.NET.git
commit 0b2f8047e3fc92cfdffdcc45db5e88985d5148af Author: Zhangyanrui123 <[email protected]> AuthorDate: Sat Nov 7 23:25:29 2026 +0800 ci: restrict release automation to reviewed source candidates --- .github/workflows/build.yml | 1 + .github/workflows/release.yml | 236 +++++++++++++----------------------------- RELEASING.md | 62 +++++++++++ 3 files changed, 136 insertions(+), 163 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6f60590..6ed526b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,7 @@ jobs: uses: actions/checkout@v4 with: repository: apache/casbin + ref: c92cc6a070e65dc621210c8d451de3092683cd1c path: casbin-conformance sparse-checkout: conformance diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9d4b030..a97aaca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,184 +1,94 @@ -name: Release +name: Release candidate -# Releases are driven by manually pushed git tags (no automatic versioning): -# v1.4.0-rc1 -> GitHub pre-release only (the artifact used for the Apache vote) -# v1.4.0 -> GitHub release + push packages to the package registries +# Final publication is a separate release-manager action after the applicable +# votes. Neither branch pushes nor final tags publish packages or moving aliases. on: push: tags: - - 'v*' + - 'v*-rc*' -env: - SHA: ${{ GITHUB.SHA }} - REF: ${{ GITHUB.REF }} - RUN_ID: ${{ GITHUB.RUN_ID }} - RUN_NUMBER: ${{ GITHUB.RUN_NUMBER }} - GITHUB_TOKEN: ${{ SECRETS.GITHUB_TOKEN }} +permissions: + contents: read jobs: - build: - runs-on: windows-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 3.1.x - 5.0.x - 6.0.x - 7.0.x - 8.0.x - 9.0.x - include-prerelease: true - - - name: Check .NET info - run: dotnet --info - - - name: Install dependencies - run: dotnet restore - - - name: Build solution - run: dotnet build -c Release --no-restore - - - name: Test solution - run: dotnet test -c Release --no-build --no-restore --verbosity normal --results-directory test-results - - - name: Upload test results artefacts - uses: actions/upload-artifact@v4 - with: - name: "drop-ci-test-results" - path: './test-results' - - github-release: - runs-on: ubuntu-latest - needs: build + verify: if: github.repository_owner == 'apache' - - permissions: - contents: write - + runs-on: windows-latest outputs: version: ${{ steps.version.outputs.version }} - prerelease: ${{ steps.version.outputs.prerelease }} - steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Resolve version from tag + - name: Check tag against committed package version id: version + shell: pwsh run: | - TAG="${GITHUB_REF_NAME}" - VERSION="${TAG#v}" - if [[ "$VERSION" =~ ^(.+)-[Rr][Cc][0-9]+$ ]]; then - PRERELEASE=true - BASE_VERSION="${BASH_REMATCH[1]}" - else - PRERELEASE=false - BASE_VERSION="$VERSION" - fi - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT" - echo "prerelease=$PRERELEASE" >> "$GITHUB_OUTPUT" - echo "Tag: $TAG, version: $VERSION, base version: $BASE_VERSION, prerelease: $PRERELEASE" + if ($env:GITHUB_REF_NAME -cnotmatch '^v(\d+\.\d+\.\d+)-rc[1-9]\d*$') { + throw 'Expected vMAJOR.MINOR.PATCH-rcN' + } + $version = $Matches[1] + [xml]$project = Get-Content Casbin/Casbin.csproj -Raw + $committedVersion = @($project.Project.PropertyGroup.Version | Where-Object { $_ }) + if ($committedVersion.Count -ne 1 -or $committedVersion[0] -cne $version) { + throw 'The tag must match the committed Version in Casbin.csproj' + } + "version=$version" >> $env:GITHUB_OUTPUT + - name: Checkout pinned conformance cases + uses: actions/checkout@v4 + with: + repository: apache/casbin + ref: c92cc6a070e65dc621210c8d451de3092683cd1c + path: casbin-conformance + sparse-checkout: conformance + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 3.1.x + 5.0.x + 6.0.x + 7.0.x + 8.0.x + 9.0.x + - run: dotnet restore + - run: dotnet build -c Release --no-restore + - name: Test with conformance cases enabled + env: + CASBIN_CONFORMANCE_DIR: ${{ github.workspace }}/casbin-conformance/conformance + run: dotnet test -c Release --no-build --no-restore --verbosity normal - # The source package must carry the name the final release will have, so the - # artifact voted on as a release candidate is bit-identical to the released one. - # The release manager downloads it, signs it locally and uses it for the vote. - - name: Create Apache source release package - id: source + source: + runs-on: ubuntu-latest + needs: verify + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Prepare source originals for release-manager review + env: + VERSION: ${{ needs.verify.outputs.version }} run: | - BASE_VERSION="${{ steps.version.outputs.base_version }}" - NAME="apache-casbin-net-${BASE_VERSION}-incubating-src" - mkdir -p dist - git archive --format=tar.gz --prefix="${NAME}/" -o "dist/${NAME}.tar.gz" "${GITHUB_REF_NAME}" + set -euo pipefail + NAME="apache-casbin-net-${VERSION}-incubating-src" + mkdir dist + git archive --format=tar.gz --prefix="${NAME}/" -o "dist/${NAME}.tar.gz" HEAD (cd dist && sha512sum "${NAME}.tar.gz" > "${NAME}.tar.gz.sha512") - echo "name=$NAME" >> "$GITHUB_OUTPUT" - ls -l dist - - - name: Upload source release artefacts - uses: actions/upload-artifact@v4 + cat DISCLAIMER > dist/release-notes.txt + printf '\nRelease candidate for review. This is not an approved Apache release.\n\nThe release manager must review and sign the source archive, stage it at the ASF and obtain the applicable release votes. Final publication must reuse the approved originals; see RELEASING.md.\n' >> dist/release-notes.txt + - uses: actions/upload-artifact@v4 with: - name: "drop-ci-source-release" - path: './dist' - - - name: Create GitHub release + name: source-candidate + path: dist/ + if-no-files-found: error + - name: Create candidate without changing latest env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} run: | - if [[ "${{ steps.version.outputs.prerelease }}" == "true" ]]; then - PRERELEASE_FLAG="--prerelease" - else - PRERELEASE_FLAG="" - fi - gh release create "${GITHUB_REF_NAME}" \ - --title "${GITHUB_REF_NAME}" \ - --generate-notes \ - $PRERELEASE_FLAG \ - "dist/${{ steps.source.outputs.name }}.tar.gz" \ - "dist/${{ steps.source.outputs.name }}.tar.gz.sha512" - - push-packages: - runs-on: windows-latest - needs: github-release - # Release candidates are only voted on, they are never published to the registries. - if: github.repository_owner == 'apache' && needs.github-release.outputs.prerelease == 'false' - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 3.1.x - 5.0.x - 6.0.x - 7.0.x - 8.0.x - 9.0.x - include-prerelease: true - - - name: Check .NET info - run: dotnet --info - - - name: Install dependencies - run: dotnet restore - - - name: Build solution - run: dotnet build -c Release --no-restore - - - name: Pack packages - run: | - $Version = "${{ needs.github-release.outputs.version }}"; - echo "Publishing version: $Version"; - dotnet pack -c Release -o packages /p:PackageVersion=$Version /p:Version=$Version; - - - name: Upload packages artefacts - uses: actions/upload-artifact@v4 - with: - name: "drop-ci-packages" - path: './packages' - - - name: Add github nuget source - run: dotnet nuget add source https://nuget.pkg.github.com/apache/index.json --name github.com --username apache --password $env:GITHUB_TOKEN - - - name: Push packages to github.com - run: dotnet nuget push .\packages\*.nupkg -s github.com -k $env:GITHUB_TOKEN --skip-duplicate --timeout 600; - - - name: Push packages to nuget.org - env: - NUGET_API_TOKEN: ${{ secrets.NUGET_API_KEY }} - run: dotnet nuget push .\packages\*.nupkg -s nuget.org -k $env:NUGET_API_TOKEN --skip-duplicate + set -euo pipefail + # Creation fails if the release already exists. Never clobber artifacts. + gh release create "$TAG" --verify-tag --prerelease --latest=false \ + --title "Apache Casbin.NET (Incubating) $TAG" \ + --notes-file dist/release-notes.txt dist/*.tar.gz dist/*.sha512 diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..7710647 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,62 @@ +# Releasing Apache Casbin.NET (Incubating) + +The ASF source distribution is the release of record. NuGet packages are +convenience binaries. A Git tag, successful CI or a GitHub pre-release is not +release approval. + +## Prepare and review + +1. Commit the intended final `Version` in `Casbin/Casbin.csproj`. Check both Git + tags and NuGet: they can differ. Never reuse an existing package version. +2. Resolve source provenance, license, NOTICE, DISCLAIMER, dependency and package + metadata findings. Confirm the package identity with the project; keep C# + namespaces and assembly compatibility separate from NuGet naming. +3. Build and test all supported target frameworks. Enable the pinned upstream + conformance corpus with `CASBIN_CONFORMANCE_DIR`; the test returns early when + that variable is absent. Inspect a real `.nupkg` and `.snupkg`, including all + target frameworks and their legal/source contents. +4. Push `vVERSION-rcN` only after release-manager authorization. The candidate + workflow checks the committed version, builds/tests and creates an unsigned + source original named `apache-casbin-net-VERSION-incubating-src.tar.gz`. + It never publishes to NuGet or GitHub Packages and never changes `latest`. +5. Download that exact archive, inspect its full inventory and run Apache RAT + including hidden directories. Review all exceptions and binary files. A + human must resolve applicable legal/provenance findings before signing. + +## Stage and vote + +The release manager signs the reviewed original locally and stages the archive, +SHA-512 checksum and detached signature under the ASF development distribution. +Publish review links and KEYS/verification instructions. Independently download, +verify, unpack, build and test the staged original. Obtain and record the required +project and Incubator PMC approvals for these exact bytes. A replacement archive +requires a new RC and the applicable fresh votes. + +## Publish approved originals + +Final tags deliberately have no publication workflow. After the applicable votes +and explicit publication authorization: + +1. Promote the approved ASF source archive, checksum and signature unchanged to + the ASF release distribution. Compare public downloads byte-for-byte with + the voted originals. Do not run `git archive` again for final publication. +2. Create any final Git tag at the reviewed commit. A final GitHub release must + use the approved source originals, link the official ASF downloads and + verification material, include the full DISCLAIMER and link the vote results. + Update the latest-release alias only after approval and publication. +3. Build NuGet convenience packages from a fresh extraction of the approved + source with the committed version. Inspect the embedded metadata, README, + legal files, DLL/XML and symbol/source contents. Record package hashes and + their source correspondence before granting registry credentials. +4. Check whether that exact package ID/version exists in each intended registry. + An existing version is a stop-and-reconcile condition. Do not overwrite it, + regenerate a different package under it or mask a conflict with + `--skip-duplicate`. If a retry finds a prior upload, establish equality with + the recorded original before continuing. Handle symbols separately. +5. The authorized release manager publishes the verified packages using the + separately confirmed NuGet/GitHub Packages permissions. Verify fresh registry + downloads and a clean consumer, then update downloads and send the approved + announcement. Never test a publication route by doing a real upload. + +Keep historical non-ASF/unapproved package entries distinct from new approved +ASF releases; their presence in a registry is not evidence of an ASF vote. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
