Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1261a94
Commit changes before fixing global.json file(s).
mozts2005 May 5, 2026
be4480f
Upgrade solution to .NET 8.0 and update dependencies
mozts2005 May 5, 2026
f813e65
Upgrade solution to .NET 8.0 and update dependencies
mozts2005 May 5, 2026
dc82635
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 5, 2026
6bd88b3
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 5, 2026
012a1f0
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 5, 2026
09be1e5
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 5, 2026
d4b4622
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
a522209
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
dbdc53c
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
df53e24
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
8823e1b
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
192ef5e
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
0eff88b
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
6f99f56
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
5f32594
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
7312f06
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
8d419f1
Merge branch 'upgrade-to-NET8' of https://github.com/Speedygeek/Zende…
mozts2005 May 6, 2026
8755d68
Implement CI/CD workflows with GitHub Actions and remove legacy Azure…
mozts2005 Jun 14, 2026
e9f09b7
[CodeFactor] Apply fixes
code-factor Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions .github/workflows/ci-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
name: CI and Release

on:
pull_request:
branches:
- main
paths-ignore:
- docs/**
push:
branches:
- main
tags:
- v3.*

permissions:
id-token: write
contents: read

env:
BUILD_CONFIGURATION: Release
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
NUGET_OUTPUT_DIR: artifacts/nuget

jobs:
build-test-pack:
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup .NET SDK from global.json
uses: actions/setup-dotnet@v4
with:
global-json-file: src/global.json

- name: Restore
run: dotnet restore src/ZendeskApi_v2.sln

- name: Build
run: dotnet build src/ZendeskApi_v2.sln --configuration ${{ env.BUILD_CONFIGURATION }} --no-restore
env:
TF_BUILD: true

- name: Test (with Zendesk credentials)
if: ${{ secrets.ADMIN_ID != '' && secrets.ADMIN_EMAIL != '' && secrets.ADMIN_API_TOKEN != '' }}
run: dotnet test tests/ZendeskApi_v2.Tests/ZendeskApi_v2.Tests.csproj --configuration ${{ env.BUILD_CONFIGURATION }} --no-build
env:
admin__id: ${{ secrets.ADMIN_ID }}
admin__email: ${{ secrets.ADMIN_EMAIL }}
admin__password: not-used
admin__apiToken: ${{ secrets.ADMIN_API_TOKEN }}

- name: Test skipped notice
if: ${{ secrets.ADMIN_ID == '' || secrets.ADMIN_EMAIL == '' || secrets.ADMIN_API_TOKEN == '' }}
shell: pwsh
run: |
Write-Host "Zendesk integration tests were skipped because required secrets are missing."
Write-Host "Set ADMIN_ID, ADMIN_EMAIL, and ADMIN_API_TOKEN to enable test execution in CI."

- name: Pack
run: dotnet pack src/ZendeskApi_v2/ZendeskApi_v2.csproj --configuration ${{ env.BUILD_CONFIGURATION }} --no-build --output ${{ env.NUGET_OUTPUT_DIR }}
env:
TF_BUILD: true

- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: |
${{ env.NUGET_OUTPUT_DIR }}/*.nupkg
${{ env.NUGET_OUTPUT_DIR }}/*.snupkg

publish:
needs: build-test-pack
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v3.')) }}
runs-on: windows-latest
permissions:
id-token: write
contents: write
packages: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Download packages
uses: actions/download-artifact@v4
with:
name: nuget-packages
path: artifacts/nuget

- name: Determine prerelease
id: prerelease
shell: pwsh
run: |
$packages = Get-ChildItem "artifacts/nuget/*.nupkg" | Where-Object { -not $_.Name.EndsWith(".symbols.nupkg") }
if (-not $packages) {
throw "No NuGet package found to publish."
}

$packageName = $packages[0].Name
$versionMatch = [regex]::Match($packageName, '\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?(?:[-+][^\\.]+(?:\\.[^\\.]+)*)?')
if (-not $versionMatch.Success) {
throw "Unable to parse package version from $packageName"
}

$version = $versionMatch.Value
$isPrerelease = $version.Contains("-") -or $version.Contains("+")

"package_version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"is_prerelease=$($isPrerelease.ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"release_date=$(Get-Date -Format 'dd MMMM yyyy')" | Out-File -FilePath $env:GITHUB_OUTPUT -Append

- name: Azure login (OIDC federated identity)
if: ${{ secrets.AZURE_TENANT_ID != '' && secrets.AZURE_CLIENT_ID != '' && secrets.AZURE_SUBSCRIPTION_ID != '' && secrets.AZURE_TRUSTED_SIGNING_ENDPOINT != '' && secrets.AZURE_TRUSTED_SIGNING_ACCOUNT != '' && secrets.AZURE_TRUSTED_SIGNING_PROFILE != '' }}
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Sign packages with Azure Artifact Signing
if: ${{ secrets.AZURE_TENANT_ID != '' && secrets.AZURE_CLIENT_ID != '' && secrets.AZURE_SUBSCRIPTION_ID != '' && secrets.AZURE_TRUSTED_SIGNING_ENDPOINT != '' && secrets.AZURE_TRUSTED_SIGNING_ACCOUNT != '' && secrets.AZURE_TRUSTED_SIGNING_PROFILE != '' }}
uses: azure/trusted-signing-action@v0
with:
endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
signing-account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT }}
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_PROFILE }}
files-folder: ${{ github.workspace }}\artifacts\nuget
files-folder-filter: nupkg

- name: Fail publish when signing config is missing
if: ${{ secrets.AZURE_TENANT_ID == '' || secrets.AZURE_CLIENT_ID == '' || secrets.AZURE_SUBSCRIPTION_ID == '' || secrets.AZURE_TRUSTED_SIGNING_ENDPOINT == '' || secrets.AZURE_TRUSTED_SIGNING_ACCOUNT == '' || secrets.AZURE_TRUSTED_SIGNING_PROFILE == '' }}
shell: pwsh
run: |
throw "Azure Artifact Signing federated identity configuration is incomplete. Set AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_SUBSCRIPTION_ID, AZURE_TRUSTED_SIGNING_ENDPOINT, AZURE_TRUSTED_SIGNING_ACCOUNT, and AZURE_TRUSTED_SIGNING_PROFILE."

- name: Publish prerelease to GitHub Packages
if: ${{ steps.prerelease.outputs.is_prerelease == 'true' && secrets.GITHUB_FEED_URL != '' && secrets.GITHUB_FEED_API_KEY != '' }}
shell: pwsh
run: dotnet nuget push "artifacts/nuget/*.nupkg" --skip-duplicate --api-key "${{ secrets.GITHUB_FEED_API_KEY }}" --source "${{ secrets.GITHUB_FEED_URL }}"

- name: Publish prerelease to MyGet
if: ${{ steps.prerelease.outputs.is_prerelease == 'true' && secrets.MYGET_FEED_URL != '' && secrets.MYGET_API_KEY != '' }}
shell: pwsh
run: dotnet nuget push "artifacts/nuget/*.nupkg" --skip-duplicate --api-key "${{ secrets.MYGET_API_KEY }}" --source "${{ secrets.MYGET_FEED_URL }}"

- name: Publish stable to NuGet.org
if: ${{ startsWith(github.ref, 'refs/tags/v3.') && steps.prerelease.outputs.is_prerelease == 'false' && secrets.NUGET_API_KEY != '' }}
shell: pwsh
run: dotnet nuget push "artifacts/nuget/*.nupkg" --skip-duplicate --api-key "${{ secrets.NUGET_API_KEY }}" --source "https://api.nuget.org/v3/index.json"

- name: Create GitHub Release (draft)
if: ${{ startsWith(github.ref, 'refs/tags/v3.') && steps.prerelease.outputs.is_prerelease == 'false' }}
uses: softprops/action-gh-release@v2
with:
draft: true
name: ${{ steps.prerelease.outputs.package_version }} (${{ steps.prerelease.outputs.release_date }})
files: |
artifacts/nuget/*.nupkg
artifacts/nuget/*.snupkg
92 changes: 92 additions & 0 deletions .github/workflows/deploy-infrastructure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Deploy Infrastructure (Prod)

on:
workflow_dispatch:
inputs:
phase:
description: Deployment phase (bootstrap creates RG/account, finalize creates certificate profile)
required: true
default: bootstrap
type: choice
options:
- bootstrap
- finalize
- full
deployment_location:
description: Azure region for subscription deployment metadata
required: true
default: eastus
type: string
identity_validation_id:
description: Identity validation ID required for finalize/full phase certificate profile creation
required: false
type: string

permissions:
id-token: write
contents: read

jobs:
deploy-prod:
runs-on: ubuntu-latest
environment: prod

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Register Azure Artifact Signing provider
shell: bash
run: az provider register --namespace Microsoft.CodeSigning --wait

- name: Resolve phase parameters
id: phase
shell: bash
run: |
phase="${{ inputs.phase }}"
identityValidationId="${{ inputs.identity_validation_id }}"

if [[ "$phase" == "bootstrap" ]]; then
echo "create_certificate_profile=false" >> "$GITHUB_OUTPUT"
echo "identity_validation_id=" >> "$GITHUB_OUTPUT"
exit 0
fi

if [[ -z "$identityValidationId" ]]; then
echo "identity_validation_id input is required for '$phase' phase" >&2
exit 1
fi

echo "create_certificate_profile=true" >> "$GITHUB_OUTPUT"
echo "identity_validation_id=$identityValidationId" >> "$GITHUB_OUTPUT"

- name: Validate template with what-if
shell: bash
run: |
az deployment sub what-if \
--name "zendeskapi-prod-${{ github.run_id }}" \
--location "${{ inputs.deployment_location }}" \
--template-file infra/main.bicep \
--parameters @infra/parameters/prod.parameters.json \
location="${{ inputs.deployment_location }}" \
createCertificateProfile=${{ steps.phase.outputs.create_certificate_profile }} \
identityValidationId="${{ steps.phase.outputs.identity_validation_id }}"

- name: Deploy template
shell: bash
run: |
az deployment sub create \
--name "zendeskapi-prod-${{ github.run_id }}" \
--location "${{ inputs.deployment_location }}" \
--template-file infra/main.bicep \
--parameters @infra/parameters/prod.parameters.json \
location="${{ inputs.deployment_location }}" \
createCertificateProfile=${{ steps.phase.outputs.create_certificate_profile }} \
identityValidationId="${{ steps.phase.outputs.identity_validation_id }}"
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[![CodeFactor](https://www.codefactor.io/repository/github/speedygeek/zendeskapi_v2/badge)](https://www.codefactor.io/repository/github/speedygeek/zendeskapi_v2)

[![Build Status](https://dev.azure.com/speedygeek/Zendesk/_apis/build/status/Speedygeek.ZendeskApi_v2?branchName=main)](https://dev.azure.com/speedygeek/Zendesk/_build/latest?definitionId=15&branchName=main)
[![CI and Release](https://github.com/Speedygeek/ZendeskApi_v2/actions/workflows/ci-release.yml/badge.svg?branch=main)](https://github.com/Speedygeek/ZendeskApi_v2/actions/workflows/ci-release.yml)

| Prerelease | Stable |
|---|---|
Expand All @@ -20,6 +21,10 @@ about the client please feel to ask them in our [GitHub Discussions][discussions

If you have questions about your account or the api its self please contact the zendesk team at [api@zendesk.com](mailto:api@zendesk.com)

## CI/CD Documentation

CI/CD and signing documentation has moved to `docs/ci-cd.md`.

## Contributing

Any and all are welcome to contribute to this project.
Expand Down
23 changes: 7 additions & 16 deletions ci/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,24 @@

resources:
- repo: self

variables:
- name: BuildConfiguration
value: 'Release'
- name: TF_BUILD
value: 'true'
#- name: System.Debug
# value: true
- name: System.Debug
value: true
- name: DOTNET_CLI_TELEMETRY_OPTOUT
value: true
- name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE
value: true
- group: GitHub_Feed
- group: Zendesk_Creds

trigger:
batch: true
branches:
include:
- main
- refs/tags/*
pr:
branches:
include:
- main
paths:
exclude:
- docs/*
# Disabled: CI/CD moved to GitHub Actions workflows under .github/workflows.
trigger: none
pr: none

pool:
vmImage: 'windows-latest'
Expand Down Expand Up @@ -65,7 +56,7 @@ jobs:
env:
admin__id: $(Admin.ID)
admin__email: $(Admin.Email)
admin__password: $(Admin.Password)
admin__password: "not used nay more"
admin__apiToken: $(Admin.ApiToken)

- task: DotNetCoreCLI@2
Expand Down
41 changes: 41 additions & 0 deletions docs/ci-cd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# CI/CD and Signing

Build, package, signing, and publishing are managed in GitHub Actions:

1. `.github/workflows/ci-release.yml`
2. `.github/workflows/deploy-infrastructure.yml`

The legacy Azure DevOps pipeline in `ci/azure-pipelines.yml` is disabled (`trigger: none`, `pr: none`) and retained only as historical reference.

## Required Signing Secrets

Configure these repository or environment secrets for Azure Artifact Signing with federated identity:

1. `AZURE_TENANT_ID`
2. `AZURE_CLIENT_ID`
3. `AZURE_SUBSCRIPTION_ID`
4. `AZURE_TRUSTED_SIGNING_ENDPOINT`
5. `AZURE_TRUSTED_SIGNING_ACCOUNT`
6. `AZURE_TRUSTED_SIGNING_PROFILE`

The workflows use OIDC via `azure/login@v2`. Configure a federated credential on the Microsoft Entra application backing `AZURE_CLIENT_ID` for this repository/environment.

## Package Publish Secrets

1. `NUGET_API_KEY` (stable tag releases)
2. `GITHUB_FEED_URL` and `GITHUB_FEED_API_KEY` (prerelease feed)
3. `MYGET_FEED_URL` and `MYGET_API_KEY` (prerelease feed)

## Test Secrets

Integration tests require:

1. `ADMIN_ID`
2. `ADMIN_EMAIL`
3. `ADMIN_API_TOKEN`

If test secrets are missing, the workflow skips integration tests and logs a notice.

## Related Docs

Infrastructure-specific documentation lives in `infra/README.md`.
Loading