diff --git a/packages/sdk/C#/.github/workflow/nuget-publish.yml b/packages/sdk/C#/.github/workflow/nuget-publish.yml new file mode 100644 index 00000000..bec65a92 --- /dev/null +++ b/packages/sdk/C#/.github/workflow/nuget-publish.yml @@ -0,0 +1,101 @@ +name: Build and Publish NuGet Package + +on: + workflow_dispatch: + push: + branches: [main, develop] + paths: + - "src/**" + - "Directory.*" + - "*.props" + - ".github/workflows/nuget-publish.yml" + - "GitVersion.yml" + pull_request: + branches: [main, develop] + paths: + - "src/**" + - "Directory.*" + - "*.props" + - ".github/workflows/nuget-publish.yml" + - "GitVersion.yml" + +jobs: + build: + runs-on: ubuntu-latest + env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 # Required for GitVersion to work properly + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + env: + NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.10.2 + with: + versionSpec: "5.x" + + - name: Determine Version + id: gitversion + uses: gittools/actions/gitversion/execute@v0.10.2 + with: + useConfigFile: true + + - name: Display GitVersion outputs + run: | + echo "SemVer: ${{ steps.gitversion.outputs.semVer }}" + echo "NuGetVersionV2: ${{ steps.gitversion.outputs.nuGetVersionV2 }}" + echo "AssemblySemVer: ${{ steps.gitversion.outputs.assemblySemVer }}" + echo "FullSemVer: ${{ steps.gitversion.outputs.fullSemVer }}" + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore /p:Version=${{ steps.gitversion.outputs.assemblySemVer }} + + - name: Run tests + run: dotnet test --no-build --verbosity normal --configuration Release + + - name: Package NuGet + run: dotnet pack --configuration Release --no-build --output nupkgs /p:Version=${{ steps.gitversion.outputs.nuGetVersionV2 }} /p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: nupkg + path: nupkgs/*.nupkg + + # Publish to GitHub Packages only on main branch or when triggered manually + - name: Publish NuGet package to GitHub Packages + if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' + run: dotnet nuget push nupkgs/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + + # Create a GitHub release with tag when pushing to main + - name: Create Release + uses: actions/create-release@v1 + if: github.ref == 'refs/heads/main' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.gitversion.outputs.semVer }} + release_name: Release v${{ steps.gitversion.outputs.semVer }} + draft: false + prerelease: false + + # Publish to NuGet.org only for releases from main branch + - name: Publish to NuGet.org + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + run: dotnet nuget push nupkgs/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} diff --git a/packages/sdk/C#/.github/workflow/release.yml b/packages/sdk/C#/.github/workflow/release.yml new file mode 100644 index 00000000..3fa1ce05 --- /dev/null +++ b/packages/sdk/C#/.github/workflow/release.yml @@ -0,0 +1,81 @@ +name: Create Release + +on: + workflow_dispatch: + inputs: + releaseType: + description: "Release type (patch, minor, major)" + required: true + default: "patch" + type: choice + options: + - patch + - minor + - major + +jobs: + create-release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.RELEASE_PAT }} # Personal access token with repo permissions + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.10.2 + with: + versionSpec: "5.x" + + - name: Get current version + id: gitversion + uses: gittools/actions/gitversion/execute@v0.10.2 + with: + useConfigFile: true + + - name: Display current version + run: echo "Current version is ${{ steps.gitversion.outputs.semVer }}" + + - name: Setup Git + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "github-actions@github.com" + + - name: Determine new version + id: newversion + run: | + CURRENT_VERSION=${{ steps.gitversion.outputs.majorMinorPatch }} + RELEASE_TYPE=${{ github.event.inputs.releaseType }} + + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" + + if [ "$RELEASE_TYPE" == "major" ]; then + NEW_VERSION="$((MAJOR + 1)).0.0" + elif [ "$RELEASE_TYPE" == "minor" ]; then + NEW_VERSION="$MAJOR.$((MINOR + 1)).0" + else # patch + NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" + fi + + echo "newVersion=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "New version will be $NEW_VERSION" + + - name: Create commit with version bump + run: | + echo "+semver: ${{ github.event.inputs.releaseType }}" > version-bump.txt + git add version-bump.txt + git commit -m "+semver: ${{ github.event.inputs.releaseType }}" + git push origin HEAD:main + + - name: Create tag + run: | + git tag -a v${{ steps.newversion.outputs.newVersion }} -m "Release v${{ steps.newversion.outputs.newVersion }}" + git push origin v${{ steps.newversion.outputs.newVersion }} + + # The actual release creation will be handled by the main workflow triggered by the tag push diff --git a/packages/sdk/C#/CI-CD-DOCUMENTATION.md b/packages/sdk/C#/CI-CD-DOCUMENTATION.md new file mode 100644 index 00000000..d9bac182 --- /dev/null +++ b/packages/sdk/C#/CI-CD-DOCUMENTATION.md @@ -0,0 +1,117 @@ +# CI/CD Pipeline Documentation + +This document explains how the CI/CD pipeline works for this project, including versioning, building, and publishing NuGet packages. + +## Overview + +The project uses GitHub Actions for CI/CD with the following key components: + +1. **GitVersion** - Handles automatic semantic versioning +2. **NuGet Package Build** - Builds and packages the library +3. **GitHub Packages** - Hosts NuGet packages for development +4. **NuGet.org** - Hosts public releases + +## Workflow Files + +The repository contains the following workflow files: + +- `.github/workflows/nuget-publish.yml` - Main pipeline that builds, tests, versions, and publishes packages +- `.github/workflows/release.yml` - Dedicated workflow for creating releases (manually triggered) + +## Versioning Strategy + +We use GitVersion to automatically calculate semantic versions based on Git history and branching patterns: + +- **GitVersion.yml** - Defines the versioning rules for different branches + +### Branch-Based Versioning + +Versions are automatically calculated based on the branch: + +- **main**: Production releases with clean semantic versions (e.g., `1.2.3`) +- **develop**: Beta pre-releases (e.g., `1.3.0-beta.1`) +- **feature branches**: Alpha pre-releases (e.g., `1.3.0-alpha.feature-name.1`) +- **hotfix branches**: Release candidates (e.g., `1.2.4-rc.hotfix-name.1`) +- **PR branches**: Pre-release with PR number (e.g., `1.3.0-pr.123.1`) + +### Commit Messages for Versioning + +You can control version increments using special commit messages: + +- `+semver: major` or `+semver: breaking` - Bump major version +- `+semver: minor` or `+semver: feature` - Bump minor version +- `+semver: patch` or `+semver: fix` - Bump patch version +- `+semver: none` or `+semver: skip` - Don't increment version + +## Build Process + +The build process follows these steps: + +1. Checkout code with full history (required for GitVersion) +2. Set up .NET environment +3. Calculate version using GitVersion +4. Restore dependencies +5. Build with the version from GitVersion +6. Run tests +7. Package as NuGet with appropriate version +8. Upload package as artifact + +## Publishing + +Packages are published based on the branch and event: + +- **GitHub Packages**: + - All packages from `main` branch + - All packages from manual workflow runs + +- **NuGet.org**: + - Only packages from `main` branch + - Only for push events (not PRs) + - Requires `NUGET_API_KEY` secret + +## Creating Releases + +To create a new release: + +1. Go to the "Actions" tab in GitHub +2. Select the "Create Release" workflow +3. Click "Run workflow" +4. Select the type of release (patch, minor, major) +5. Click "Run workflow" again + +This will: + +1. Create a commit with the selected semver increment +2. Push it to the main branch +3. Create a Git tag for the new version +4. Trigger the main workflow to build and publish the package + +## Required Secrets + +The pipeline requires the following GitHub secrets: + +- `GITHUB_TOKEN` (automatically provided by GitHub) +- `NUGET_API_KEY` (for publishing to NuGet.org) +- `RELEASE_PAT` (for the release workflow to push to protected branches) + +## NuGet Package Metadata + +Package metadata is defined in `Directory.Build.props` including: + +- Authors +- Description +- License +- Project URL +- Repository info +- Tags +- Icon +- README + +## Debugging + +If you encounter issues with the pipeline: + +1. Check the GitHub Actions logs +2. Look for GitVersion output (shows calculated version) +3. Review the package artifacts for correct versioning +4. Ensure required secrets are configured diff --git a/packages/sdk/C#/Directory.Build.props b/packages/sdk/C#/Directory.Build.props new file mode 100644 index 00000000..2d03a99f --- /dev/null +++ b/packages/sdk/C#/Directory.Build.props @@ -0,0 +1,54 @@ + + + + Your Company Name + Your Company Name + Copyright © $(Company) $([System.DateTime]::Now.Year) + MIT + https://github.com/$(GITHUB_REPOSITORY) + https://github.com/$(GITHUB_REPOSITORY) + git + icon.png + README.md + agent-protocol;client;api + Client library for interacting with the Agent Protocol API + + + latest + enable + enable + false + true + $(NoWarn);CS1591 + + + true + true + true + snupkg + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + true + $(MSBuildThisFileDirectory)nupkgs + 1.0.0 + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + true + + \ No newline at end of file diff --git a/packages/sdk/C#/GitVersion.yml b/packages/sdk/C#/GitVersion.yml new file mode 100644 index 00000000..0dac87fe --- /dev/null +++ b/packages/sdk/C#/GitVersion.yml @@ -0,0 +1,54 @@ +mode: Mainline +major-version-bump-message: '\+semver:\s?(breaking|major)' +minor-version-bump-message: '\+semver:\s?(feature|minor)' +patch-version-bump-message: '\+semver:\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' + +# Branch config +branches: + main: + regex: ^main$ + tag: "" + increment: Patch + prevent-increment-of-merged-branch-version: true + track-merge-target: false + is-release-branch: true + is-mainline: true + + develop: + regex: ^develop$ + tag: "beta" + increment: Minor + prevent-increment-of-merged-branch-version: false + track-merge-target: true + is-release-branch: false + source-branches: ["main"] + + feature: + regex: ^feature[/-] + tag: "alpha.{BranchName}" + increment: Inherit + source-branches: ["develop", "main"] + prevent-increment-of-merged-branch-version: false + track-merge-target: false + + hotfix: + regex: ^hotfix[/-] + tag: "rc.{BranchName}" + increment: Patch + source-branches: ["main"] + prevent-increment-of-merged-branch-version: false + track-merge-target: false + + pull-request: + regex: ^(pull|pull\-requests|pr)[/-] + tag: "pr.{BranchName}" + increment: Inherit + track-merge-target: false + tag-number-pattern: '[/-](?\d+)[-/]' + +# Global settings +ignore: + sha: [] +merge-message-formats: {} +tag-prefix: "v"