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]

Reply via email to