Skip to content

Commit b8f69ad

Browse files
authored
Merge pull request #37 from couchbaselabs/NCO-45
NCO-45: Add GHA workflow to build, test and publish releases to NuGet
2 parents edcd51a + 367f5e9 commit b8f69ad

10 files changed

Lines changed: 545 additions & 48 deletions

File tree

.github/workflows/apidocs.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: API Docs
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
inputs:
8+
version_tag:
9+
description: 'Git tag to build docs for (e.g., 3.8.0 or v3.8.0-rc1)'
10+
required: true
11+
smoke_test:
12+
description: 'Run as smoke test (build & artifact only, without pushing to S3)'
13+
type: boolean
14+
required: false
15+
default: false
16+
17+
env:
18+
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
19+
DOTNET_NOLOGO: 1
20+
21+
jobs:
22+
validate-inputs:
23+
name: Validate Inputs
24+
runs-on: ubuntu-latest
25+
outputs:
26+
tag: ${{ steps.compute.outputs.tag }}
27+
version: ${{ steps.compute.outputs.version }}
28+
publish: ${{ steps.compute.outputs.publish }}
29+
smoke: ${{ steps.compute.outputs.smoke }}
30+
steps:
31+
- id: compute
32+
name: Compute tag, version, smoke flag
33+
shell: bash
34+
run: |
35+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
36+
TAG_INPUT="${{ github.event.inputs.version_tag }}"
37+
SMOKE_INPUT="${{ github.event.inputs.smoke_test }}"
38+
else
39+
TAG_INPUT="${{ github.ref_name }}"
40+
SMOKE_INPUT="false"
41+
fi
42+
if [ -z "$TAG_INPUT" ]; then
43+
echo "Tag input is required." >&2
44+
exit 1
45+
fi
46+
if ! echo "$TAG_INPUT" | grep -Eq '^(v)?[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$'; then
47+
echo "Tag must be a semantic version (e.g., 3.8.0 or v3.8.0-rc1)." >&2
48+
exit 1
49+
fi
50+
VER="${TAG_INPUT#v}"
51+
echo "tag=$TAG_INPUT" >> $GITHUB_OUTPUT
52+
echo "version=$VER" >> $GITHUB_OUTPUT
53+
echo "smoke=$SMOKE_INPUT" >> $GITHUB_OUTPUT
54+
if [ "$SMOKE_INPUT" = "true" ]; then echo "publish=false" >> $GITHUB_OUTPUT; else echo "publish=true" >> $GITHUB_OUTPUT; fi
55+
56+
build-docs:
57+
name: Build API Docs
58+
runs-on: ubuntu-latest
59+
needs: validate-inputs
60+
env:
61+
PACKAGE_VERSION: ${{ needs.validate-inputs.outputs.version }}
62+
API_NAME: analytics-dotnet-client
63+
AWS_REGION: us-west-1
64+
S3_BUCKET: docs.couchbase.com
65+
steps:
66+
- name: Checkout repo at tag
67+
uses: actions/checkout@v4
68+
with:
69+
ref: ${{ needs.validate-inputs.outputs.tag }}
70+
71+
- name: Setup .NET SDK
72+
uses: actions/setup-dotnet@v4
73+
with:
74+
dotnet-version: 8.0.x
75+
cache: false
76+
77+
- name: Install DocFX
78+
run: dotnet tool install -g docfx
79+
80+
- name: Build docs
81+
run: |
82+
cd docs
83+
~/.dotnet/tools/docfx metadata
84+
~/.dotnet/tools/docfx build
85+
86+
- name: Validate site generated
87+
run: |
88+
test -d docs/_site/api || (echo "No API output found" && exit 1)
89+
test $(ls -1 docs/_site/api/*.html | wc -l) -ge 2 || (echo "Insufficient API HTML files" && exit 1)
90+
91+
- name: Archive docs
92+
run: |
93+
zip -rq "${{ env.API_NAME }}-${{ env.PACKAGE_VERSION }}.zip" docs/_site/*
94+
95+
- name: Upload API Docs Artifact
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: apidocs-zip
99+
path: ${{ env.API_NAME }}-${{ env.PACKAGE_VERSION }}.zip
100+
101+
- name: Configure AWS Credentials
102+
if: needs.validate-inputs.outputs.publish == 'true'
103+
uses: aws-actions/configure-aws-credentials@v4
104+
with:
105+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
106+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
107+
aws-region: ${{ env.AWS_REGION }}
108+
109+
- name: Publish to S3
110+
if: needs.validate-inputs.outputs.publish == 'true'
111+
run: |
112+
aws s3 sync "./docs/_site/" "s3://$S3_BUCKET/sdk-api/$API_NAME-$PACKAGE_VERSION/" --acl public-read --delete
113+
114+
115+

.github/workflows/release.yml

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
name: Release
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
inputs:
8+
version_tag:
9+
description: 'Git tag to release (e.g., 3.8.0 or 3.8.0-rc1; optional leading v)'
10+
required: true
11+
smoke_test:
12+
description: 'Run as smoke test (build, test, pack only; no NuGet publish)'
13+
type: boolean
14+
required: false
15+
default: false
16+
17+
env:
18+
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
19+
DOTNET_NOLOGO: 1
20+
21+
jobs:
22+
validate-inputs:
23+
name: Validate Inputs
24+
runs-on: ubuntu-latest
25+
outputs:
26+
tag: ${{ steps.compute.outputs.tag }}
27+
version: ${{ steps.compute.outputs.version }}
28+
smoke: ${{ steps.compute.outputs.smoke }}
29+
steps:
30+
- id: compute
31+
name: Compute tag, version, and smoke flag
32+
shell: bash
33+
run: |
34+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
35+
TAG_INPUT="${{ github.event.inputs.version_tag }}"
36+
SMOKE_INPUT="${{ github.event.inputs.smoke_test }}"
37+
else
38+
TAG_INPUT="${{ github.ref_name }}"
39+
SMOKE_INPUT="false"
40+
fi
41+
if [ -z "$TAG_INPUT" ]; then
42+
echo "Tag input is required." >&2
43+
exit 1
44+
fi
45+
if ! echo "$TAG_INPUT" | grep -Eq '^(v)?[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$'; then
46+
echo "Tag must be a semantic version (e.g., 3.8.0 or v3.8.0-rc1)." >&2
47+
exit 1
48+
fi
49+
VER="${TAG_INPUT#v}"
50+
echo "tag=$TAG_INPUT" >> $GITHUB_OUTPUT
51+
echo "version=$VER" >> $GITHUB_OUTPUT
52+
if [ "$SMOKE_INPUT" = "true" ]; then echo "smoke=true" >> $GITHUB_OUTPUT; else echo "smoke=false" >> $GITHUB_OUTPUT; fi
53+
54+
build-and-test:
55+
name: Build & Test (${{ matrix.name }})
56+
runs-on: ${{ matrix.os }}
57+
needs: validate-inputs
58+
strategy:
59+
fail-fast: false
60+
matrix:
61+
include:
62+
- name: ubuntu
63+
os: ubuntu-latest
64+
- name: windows
65+
os: windows-latest
66+
- name: macos
67+
os: macos-latest
68+
- name: centos
69+
os: ubuntu-latest
70+
container: quay.io/centos/centos:stream9
71+
container: ${{ matrix.container }}
72+
steps:
73+
- name: Checkout tag
74+
uses: actions/checkout@v4
75+
with:
76+
ref: ${{ needs.validate-inputs.outputs.tag }}
77+
fetch-depth: 0
78+
79+
- name: Setup .NET SDK
80+
uses: actions/setup-dotnet@v4
81+
with:
82+
dotnet-version: 8.0.x
83+
cache: false
84+
85+
- name: Install prerequisites (CentOS)
86+
if: matrix.name == 'centos'
87+
run: |
88+
yum -y update
89+
yum -y install tar gzip curl ca-certificates krb5-libs libicu zlib openssl
90+
91+
- name: Restore
92+
run: dotnet restore src/Couchbase.Analytics/Couchbase.Analytics.csproj
93+
94+
- name: Build (Release)
95+
run: dotnet build src/Couchbase.Analytics/Couchbase.Analytics.csproj --configuration Release --no-restore
96+
97+
- name: Test - Couchbase.Analytics.UnitTests (Release)
98+
run: dotnet test tests/Couchbase.Analytics.UnitTests/Couchbase.Analytics.UnitTests.csproj --configuration Release --no-build --logger "trx;LogFileName=unit-tests.trx" --results-directory "TestResults"
99+
100+
- name: Upload Test Results
101+
if: always()
102+
uses: actions/upload-artifact@v4
103+
with:
104+
name: unit-test-results-${{ matrix.name }}
105+
path: TestResults
106+
107+
pack:
108+
name: Pack (Windows)
109+
runs-on: windows-latest
110+
needs: [validate-inputs, build-and-test]
111+
env:
112+
PACKAGE_VERSION: ${{ needs.validate-inputs.outputs.version }}
113+
NETSDK_SIGNKEY: ${{ secrets.NETSDK_SIGNKEY }}
114+
steps:
115+
- name: Checkout tag
116+
uses: actions/checkout@v4
117+
with:
118+
ref: ${{ needs.validate-inputs.outputs.tag }}
119+
fetch-depth: 0
120+
121+
- name: Setup .NET SDK
122+
uses: actions/setup-dotnet@v4
123+
with:
124+
dotnet-version: 8.0.x
125+
cache: false
126+
127+
- name: Restore
128+
run: dotnet restore src/Couchbase.Analytics/Couchbase.Analytics.csproj
129+
130+
- name: Prepare strong-name key (SNK) (optional)
131+
if: env.NETSDK_SIGNKEY != ''
132+
shell: pwsh
133+
run: |
134+
$bytes = [System.Convert]::FromBase64String("${{ env.NETSDK_SIGNKEY }}")
135+
[IO.File]::WriteAllBytes("signkey.snk", $bytes)
136+
137+
- name: Build (Release, signed)
138+
shell: pwsh
139+
run: |
140+
$signProps = ''
141+
if ("${{ env.NETSDK_SIGNKEY }}" -ne '') { $signProps = "/p:SignAssembly=true /p:AssemblyOriginatorKeyFile=signkey.snk" }
142+
dotnet build src/Couchbase.Analytics/Couchbase.Analytics.csproj -c Release --no-restore /p:ContinuousIntegrationBuild=true /p:Version="${{ env.PACKAGE_VERSION }}" /p:IncludeSymbols=true /p:IncludeSource=true /p:SourceLinkCreate=true $signProps
143+
144+
- name: Pack (Release, signed)
145+
shell: pwsh
146+
run: |
147+
$signProps = ''
148+
if ("${{ env.NETSDK_SIGNKEY }}" -ne '') { $signProps = "/p:SignAssembly=true /p:AssemblyOriginatorKeyFile=signkey.snk" }
149+
dotnet pack src/Couchbase.Analytics/Couchbase.Analytics.csproj -c Release --no-build /p:ContinuousIntegrationBuild=true /p:Version="${{ env.PACKAGE_VERSION }}" /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:IncludeSource=true /p:SourceLinkCreate=true $signProps
150+
151+
- name: Upload Packages Artifact
152+
uses: actions/upload-artifact@v4
153+
with:
154+
name: nuget-packages
155+
path: |
156+
**/*.nupkg
157+
**/*.snupkg
158+
159+
publish:
160+
name: Publish to NuGet
161+
runs-on: windows-latest
162+
needs: [validate-inputs, pack]
163+
environment: nuget-publish
164+
# Only publish when not a smoke test
165+
if: needs.validate-inputs.outputs.smoke != 'true'
166+
env:
167+
PACKAGE_VERSION: ${{ needs.validate-inputs.outputs.version }}
168+
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
169+
steps:
170+
- name: Download Packages Artifact
171+
uses: actions/download-artifact@v4
172+
with:
173+
name: nuget-packages
174+
path: ./artifacts
175+
176+
- name: Publish to NuGet (nupkg)
177+
if: env.NUGET_API_KEY != ''
178+
shell: pwsh
179+
run: |
180+
$ver = "$Env:PACKAGE_VERSION"
181+
Get-ChildItem -Path ./artifacts -Recurse -Filter *.nupkg | Where-Object { $_.Name -like "*.$ver.nupkg" } | ForEach-Object {
182+
dotnet nuget push $_.FullName --source "https://api.nuget.org/v3/index.json" --api-key "${{ env.NUGET_API_KEY }}" --skip-duplicate
183+
}
184+
185+
- name: Publish symbols to NuGet (snupkg)
186+
if: env.NUGET_API_KEY != ''
187+
shell: pwsh
188+
run: |
189+
$ver = "$Env:PACKAGE_VERSION"
190+
Get-ChildItem -Path ./artifacts -Recurse -Filter *.snupkg | Where-Object { $_.Name -like "*.$ver.snupkg" } | ForEach-Object {
191+
dotnet nuget push $_.FullName --source "https://api.nuget.org/v3/index.json" --api-key "${{ env.NUGET_API_KEY }}" --skip-duplicate
192+
}
193+
194+
smoke-summary:
195+
name: Smoke Summary
196+
runs-on: ubuntu-latest
197+
needs: [validate-inputs, build-and-test, pack]
198+
if: github.event_name == 'workflow_dispatch' && github.event.inputs.smoke_test == 'true'
199+
env:
200+
PACKAGE_VERSION: ${{ needs.validate-inputs.outputs.version }}
201+
steps:
202+
- name: Download Packages Artifact
203+
uses: actions/download-artifact@v4
204+
with:
205+
name: nuget-packages
206+
path: ./artifacts
207+
208+
- name: Download Test Results Artifacts
209+
uses: actions/download-artifact@v4
210+
with:
211+
pattern: unit-test-results-*
212+
merge-multiple: true
213+
path: ./test-results
214+
215+
- name: Print tag, package names, test result files
216+
shell: bash
217+
run: |
218+
echo "Tag: ${{ needs.validate-inputs.outputs.tag }}"
219+
echo "Package version: $PACKAGE_VERSION"
220+
echo "Packages:"
221+
find ./artifacts -type f -name "*.$PACKAGE_VERSION.nupkg" -print | sed 's/^/ /' || true
222+
find ./artifacts -type f -name "*.$PACKAGE_VERSION.snupkg" -print | sed 's/^/ /' || true
223+
echo "Test result files:"
224+
find ./test-results -type f -name "*.trx" -print | sed 's/^/ /' || true
225+
226+
227+

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,8 @@ FodyWeavers.xsd
406406
*.sln.iml
407407

408408
# test config
409-
tests/Couchbase.Analytics.FunctionalTests/settings.json
409+
tests/Couchbase.Analytics.FunctionalTests/settings.json
410+
411+
#generated api docs
412+
docs/_site/
413+
docs/api/

0 commit comments

Comments
 (0)