From 5104bc68aac03d474f18f823b99ed0cdc604b38e Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 30 Nov 2025 02:26:44 +0100 Subject: [PATCH 1/6] Refactor workflows with reusable build-test-pack logic Refactored `cd-release-preview.yml` and `cd-release.yml` to use a new reusable workflow, `cd-build-test-pack.yml`, for centralized build, test, and pack steps. This improves maintainability, reduces redundancy, and simplifies artifact handling. Key changes: - Introduced `cd-build-test-pack.yml` reusable workflow. - Replaced `build` jobs with `build-and-pack` in both workflows. - Simplified `publish` jobs to use artifact-based NuGet push. - Added `prepare` job in `cd-release.yml` for release notes handling. - Removed redundant steps for setting version, building, and packing. - Improved modularity by uploading and downloading artifacts between jobs. --- .github/workflows/cd-build-test-pack.yml | 47 ++++++++++++++++++++++++ .github/workflows/cd-release-preview.yml | 32 ++++++---------- .github/workflows/cd-release.yml | 44 +++++++++++----------- 3 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/cd-build-test-pack.yml diff --git a/.github/workflows/cd-build-test-pack.yml b/.github/workflows/cd-build-test-pack.yml new file mode 100644 index 0000000..ab12285 --- /dev/null +++ b/.github/workflows/cd-build-test-pack.yml @@ -0,0 +1,47 @@ +name: "Build, Test & Pack (Reusable)" + +on: + workflow_call: + inputs: + version: + required: true + type: string + configuration: + required: true + type: string + default: Release + runner: + required: false + type: string + default: ubuntu-latest + +jobs: + build: + runs-on: ${{ inputs.runner }} + timeout-minutes: 15 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Build + run: dotnet build --configuration ${{ inputs.configuration }} /p:Version=${{ inputs.version }} + + - name: Test + run: dotnet test --configuration ${{ inputs.configuration }} /p:Version=${{ inputs.version }} --no-build + + - name: Pack + run: dotnet pack --configuration ${{ inputs.configuration }} /p:Version=${{ inputs.version }} --output . + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: nuget-packages + path: | + *.nupkg + *.snupkg \ No newline at end of file diff --git a/.github/workflows/cd-release-preview.yml b/.github/workflows/cd-release-preview.yml index 02102ef..23dc8d7 100644 --- a/.github/workflows/cd-release-preview.yml +++ b/.github/workflows/cd-release-preview.yml @@ -6,26 +6,18 @@ on: - "v[0-9]+\\.[0-9]+\\.[0-9]+-preview*" jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 15 + build-and-pack: + uses: ./.github/workflows/cd-build-test-pack.yml + with: + version: ${{ github.ref_name }} + configuration: Release + publish: + needs: build-and-pack + runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - - name: Set VERSION variable from tag - run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV - - - name: Pack - run: dotnet pack --configuration Release /p:Version=${VERSION} --output . + - name: Download artifacts + uses: actions/download-artifact@v4 - - name: Push - run: dotnet nuget push CryptoNet.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN} - env: - NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} + - name: Push to NuGet + run: dotnet nuget push nuget-packages/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_TOKEN }} diff --git a/.github/workflows/cd-release.yml b/.github/workflows/cd-release.yml index 520a867..bc160fc 100644 --- a/.github/workflows/cd-release.yml +++ b/.github/workflows/cd-release.yml @@ -6,11 +6,8 @@ on: - "v[0-9]+.[0-9]+.[0-9]+" jobs: - build: + prepare: runs-on: ubuntu-latest - - timeout-minutes: 15 - steps: - name: Checkout uses: actions/checkout@v4 @@ -24,24 +21,25 @@ jobs: run: | git log --pretty=format:'%d %s' ${GITHUB_REF} | perl -pe 's| \(.*tag: v(\d+.\d+.\d+(-preview\d{3})?)(, .*?)*\)|\n## \1\n|g' > RELEASE-NOTES - - name: Setup .NET - uses: actions/setup-dotnet@v4 + - name: Upload release notes + uses: actions/upload-artifact@v4 with: - dotnet-version: 8.0.x - - - name: Set VERSION variable from tag - run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV - - - name: Build - run: dotnet build --configuration Release /p:Version=${VERSION} - - - name: Test - run: dotnet test --configuration Release /p:Version=${VERSION} --no-build - - - name: Pack - run: dotnet pack --configuration Release /p:Version=${VERSION} --output . + name: release-notes + path: RELEASE-NOTES + + build-and-pack: + needs: prepare + uses: ./.github/workflows/cd-build-test-pack.yml + with: + version: ${{ github.ref_name }} + configuration: Release + + publish: + needs: build-and-pack + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 - - name: Push - run: dotnet nuget push CryptoNet.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN} - env: - NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} + - name: Push to NuGet + run: dotnet nuget push nuget-packages/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_TOKEN }} From ea59a4f5b14b39904c2a1781c51a1ccded0c5cdb Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 30 Nov 2025 02:33:20 +0100 Subject: [PATCH 2/6] Simplify workflow and sanitize version input Removed the `runner` input and hardcoded `ubuntu-latest` as the runner for the build job. Added a "Sanitize version" step to clean the `version` input by stripping leading `v` or `V`. Updated `dotnet build`, `dotnet test`, and `dotnet pack` to use the sanitized version. Upgraded `actions/checkout` and `actions/setup-dotnet` to `v4` for improved compatibility. Simplified workflow configuration by removing unused inputs. --- .github/workflows/cd-build-test-pack.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cd-build-test-pack.yml b/.github/workflows/cd-build-test-pack.yml index ab12285..b60b5e8 100644 --- a/.github/workflows/cd-build-test-pack.yml +++ b/.github/workflows/cd-build-test-pack.yml @@ -10,14 +10,10 @@ on: required: true type: string default: Release - runner: - required: false - type: string - default: ubuntu-latest jobs: build: - runs-on: ${{ inputs.runner }} + runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -29,14 +25,23 @@ jobs: with: dotnet-version: 8.0.x + - name: Sanitize version + id: sanitize + run: | + v='${{ inputs.version }}' + # Remove leading v or V if present + v="${v#v}" + v="${v#V}" + echo "sanitized_version=$v" >> $GITHUB_OUTPUT + - name: Build - run: dotnet build --configuration ${{ inputs.configuration }} /p:Version=${{ inputs.version }} + run: dotnet build --configuration ${{ inputs.configuration }} /p:Version=${{ steps.sanitize.outputs.sanitized_version }} - name: Test - run: dotnet test --configuration ${{ inputs.configuration }} /p:Version=${{ inputs.version }} --no-build + run: dotnet test --configuration ${{ inputs.configuration }} /p:Version=${{ steps.sanitize.outputs.sanitized_version }} --no-build - name: Pack - run: dotnet pack --configuration ${{ inputs.configuration }} /p:Version=${{ inputs.version }} --output . + run: dotnet pack --configuration ${{ inputs.configuration }} /p:Version=${{ steps.sanitize.outputs.sanitized_version }} --output . - name: Upload artifacts uses: actions/upload-artifact@v4 From 6ecb2ee74f558625b73f3401cfb552a3debb2eac Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 30 Nov 2025 02:50:02 +0100 Subject: [PATCH 3/6] Add contributing guidelines for CI/CD and versioning Expanded CONTRIBUTING.md with detailed guidelines: - Added purpose and intent to help contributors avoid build issues. - Documented workflow reuse conventions with examples. - Defined versioning/tagging rules aligned with SemVer/NuGet. - Provided CI version sanitization steps and usage examples. - Included troubleshooting tips for common CI issues. - Emphasized maintenance alignment with GitHub Actions/NuGet. --- CONTRIBUTING.md | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a965657 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,100 @@ +# Contributing Guidelines + +## Purpose + +This document explains project-level development and CI/CD conventions +contributors must follow to avoid common build and packaging failures. + +## Workflow Reuse Guidelines + +### Calling a Reusable Workflow + +- When invoking a reusable workflow using `uses:` at **job level**, + **do NOT specify `runs-on`** in the caller. +- The **reusable workflow must**: + - Declare `on: workflow_call` + - Define `runs-on` for its internal jobs +- Inputs are passed via `with:`, and secrets via `secrets:`. + +### Example: Caller (no `runs-on`) + +``` yaml +jobs: + call-build: + uses: ./.github/workflows/cd-build-test-pack.yml + with: + version: ${{ github.ref_name }} + configuration: Release +``` + +### Example: Reusable Workflow + +``` yaml +on: + workflow_call: + inputs: + version: + required: true + type: string + configuration: + required: true + type: string + default: Release + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + # … +``` + +## Versioning and Git Tag Guidelines + +- Tags used for packaging and publishing---whether passed to + `/p:Version=` or `dotnet pack`---**must be valid SemVer/NuGet + version strings**. +- **Do NOT include a leading `v`** when passing the version into + `dotnet` commands. + +### Valid Examples + +- `3.2.6` +- `3.2.6-preview` +- `3.2.6-preview20251130229` +- `3.2.6-alpha.1+build.123` + +### Invalid Examples + +- `v3.2.6-preview20251130229` +- `3.2.6 preview` + +### Recommended Tagging Practice + +- Tag with leading `v` locally if desired, but CI must sanitize it. + +## CI Behavior and Version Sanitization + +### Sanitization Example + +``` yaml +- name: Sanitize version + id: sanitize + run: | + v="${{ inputs.version }}" + v="${v#v}" + v="${v#V}" + echo "sanitized_version=$v" >> "$GITHUB_OUTPUT" +``` + +Use via: `${{ steps.sanitize.outputs.sanitized_version }}` + +## Troubleshooting + +- If CI reports "not a valid version string," check for leading `v` or + invalid characters. + +## Maintenance + +- Keep conventions aligned with GitHub Actions and NuGet rules. From fd2f046fa5fdb316b18c16977b93267982ba64ce Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 30 Nov 2025 02:55:21 +0100 Subject: [PATCH 4/6] Enhance CI/CD workflow with commit verification Added a step to fetch the `origin/main` branch with a depth of 1 in the `cd-release.yml` workflow. Introduced a step to verify that the current commit exists in the `origin/main` branch by checking if the branch contains the commit. These changes ensure consistency and validate that the release process operates on a valid commit from the main branch. --- .github/workflows/cd-release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/cd-release.yml b/.github/workflows/cd-release.yml index bc160fc..3d5fe93 100644 --- a/.github/workflows/cd-release.yml +++ b/.github/workflows/cd-release.yml @@ -14,6 +14,9 @@ jobs: with: fetch-depth: 0 + - name: Fetch origin/main + run: git fetch --no-tags origin main:refs/remotes/origin/main --depth=1 + - name: Verify commit exists in origin/main run: git branch --remote --contains | grep origin/main From 992fba41a71bc02d7c4ebb21cfafa1aa3e7feb6c Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 30 Nov 2025 02:57:36 +0100 Subject: [PATCH 5/6] Temp disable Verify --- .github/workflows/cd-release.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cd-release.yml b/.github/workflows/cd-release.yml index 3d5fe93..6555a58 100644 --- a/.github/workflows/cd-release.yml +++ b/.github/workflows/cd-release.yml @@ -14,11 +14,8 @@ jobs: with: fetch-depth: 0 - - name: Fetch origin/main - run: git fetch --no-tags origin main:refs/remotes/origin/main --depth=1 - - - name: Verify commit exists in origin/main - run: git branch --remote --contains | grep origin/main + # - name: Verify commit exists in origin/main + # run: git branch --remote --contains | grep origin/main - name: Extract release notes run: | From a90639d795e83d4f91d676582177b22dc304ccec Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 30 Nov 2025 03:04:35 +0100 Subject: [PATCH 6/6] Update workflows and solution file configuration Updated `cd-release.yml` to use `actions/checkout@v4` with `fetch-depth: 0` and modified the "Verify commit exists in origin/main" step. Updated `CryptoNet.sln` to reflect `VisualStudioVersion` 18.3.11222.16 and added a reference to the new workflow file `.github/workflows/cd-build-test-pack.yml` under the `DevOps` project section. --- .github/workflows/cd-release.yml | 4 ++-- CryptoNet.sln | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd-release.yml b/.github/workflows/cd-release.yml index 6555a58..bc160fc 100644 --- a/.github/workflows/cd-release.yml +++ b/.github/workflows/cd-release.yml @@ -14,8 +14,8 @@ jobs: with: fetch-depth: 0 - # - name: Verify commit exists in origin/main - # run: git branch --remote --contains | grep origin/main + - name: Verify commit exists in origin/main + run: git branch --remote --contains | grep origin/main - name: Extract release notes run: | diff --git a/CryptoNet.sln b/CryptoNet.sln index de449c6..ca2ae6a 100644 --- a/CryptoNet.sln +++ b/CryptoNet.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.3.11222.16 d18.3 +VisualStudioVersion = 18.3.11222.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CryptoNet", "CryptoNet\CryptoNet.csproj", "{8F85375C-A0DC-4B67-B934-F58ED4114F5A}" EndProject @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevOps", "DevOps", "{1733D1 .gitattributes = .gitattributes .gitignore = .gitignore build.ps1 = build.ps1 + .github\workflows\cd-build-test-pack.yml = .github\workflows\cd-build-test-pack.yml .github\workflows\cd-release-preview.yml = .github\workflows\cd-release-preview.yml .github\workflows\cd-release.yml = .github\workflows\cd-release.yml .github\workflows\ci.yml = .github\workflows\ci.yml