Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
149 changes: 149 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
name: CI/CD

on:
pull_request:
branches:
- main
push:
branches:
- main
paths:
- 'UCD.Rosetta.Client/**'
- 'specs/**'
- 'Directory.Build.targets'
- '.config/dotnet-tools.json'
- 'UCD.Rosetta.sln'
- 'LICENSE'

permissions:
contents: read

env:
DOTNET_VERSION: 8.0.x
SOLUTION_PATH: UCD.Rosetta.sln
PACKAGE_PROJECT: UCD.Rosetta.Client/UCD.Rosetta.Client.csproj
PACKAGE_ID: UCD.Rosetta.Client
NUGET_SOURCE: https://api.nuget.org/v3/index.json

jobs:
build:
name: Build
runs-on: ubuntu-latest

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

- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Restore .NET tools
run: dotnet tool restore

- name: Restore
run: dotnet restore "${{ env.SOLUTION_PATH }}"

- name: Build
run: dotnet build "${{ env.SOLUTION_PATH }}" --configuration Release --no-restore /p:RestoreDotNetToolsOnBuild=false

publish:
name: Publish to NuGet
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
concurrency:
group: nuget-publish-main
cancel-in-progress: false

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

- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Restore .NET tools
run: dotnet tool restore

- name: Restore
run: dotnet restore "${{ env.SOLUTION_PATH }}"

- name: Compute package version
id: package-version
shell: python
run: |
import json
import os
import re
import sys
import urllib.error
import urllib.request
import xml.etree.ElementTree as ET

package_project = os.environ["PACKAGE_PROJECT"]
package_id = os.environ["PACKAGE_ID"]

version = ET.parse(package_project).findtext("./PropertyGroup/Version")
if not version:
raise SystemExit(f"No <Version> found in {package_project}")

match = re.fullmatch(r"(\d+)\.(\d+)(?:\.(\d+))?(?:[-+].*)?", version.strip())
if not match:
raise SystemExit(f"Project version must start with major.minor[.patch], got: {version}")

major, minor = match.group(1), match.group(2)
prefix = f"{major}.{minor}."
published_patches = []
index_url = f"https://api.nuget.org/v3-flatcontainer/{package_id.lower()}/index.json"

try:
with urllib.request.urlopen(index_url, timeout=30) as response:
versions = json.load(response).get("versions", [])
except urllib.error.HTTPError as ex:
if ex.code == 404:
versions = []
else:
raise

for published_version in versions:
if not published_version.startswith(prefix):
continue

patch_part = published_version[len(prefix):]
if re.fullmatch(r"\d+", patch_part):
published_patches.append(int(patch_part))

next_patch = max(published_patches, default=-1) + 1
next_version = f"{major}.{minor}.{next_patch}"

with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
output.write(f"version={next_version}\n")

print(f"Package version: {next_version}")

- name: Build
run: >
dotnet build "${{ env.SOLUTION_PATH }}"
--configuration Release
--no-restore
/p:RestoreDotNetToolsOnBuild=false
/p:Version=${{ steps.package-version.outputs.version }}

- name: Pack
run: >
dotnet pack "${{ env.PACKAGE_PROJECT }}"
--configuration Release
--no-build
--output artifacts
/p:RestoreDotNetToolsOnBuild=false
/p:Version=${{ steps.package-version.outputs.version }}

- name: Publish
run: >
dotnet nuget push "artifacts/*.nupkg"
--api-key "${{ secrets.NUGET_API_KEY }}"
--source "${{ env.NUGET_SOURCE }}"
9 changes: 7 additions & 2 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<Project>
<!-- Ensure local .NET tools declared in .config/dotnet-tools.json are installed -->
<Target Name="RestoreDotNetTools" BeforeTargets="BeforeBuild">
<PropertyGroup>
<RestoreDotNetToolsOnBuild Condition="'$(RestoreDotNetToolsOnBuild)' == '' AND '$(CI)' == 'true'">false</RestoreDotNetToolsOnBuild>
<RestoreDotNetToolsOnBuild Condition="'$(RestoreDotNetToolsOnBuild)' == ''">true</RestoreDotNetToolsOnBuild>
</PropertyGroup>

<!-- Ensure local .NET tools declared in .config/dotnet-tools.json are installed for local builds. -->
<Target Name="RestoreDotNetTools" BeforeTargets="BeforeBuild" Condition="'$(RestoreDotNetToolsOnBuild)' == 'true'">
<Exec Command="dotnet tool restore" />
</Target>
</Project>
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,14 +400,14 @@ The REST client (`RosettaApiClient.g.cs`) is regenerated from the OpenAPI spec b

The GraphQL client (`obj/ZeroQL/rosetta.zeroql.json.g.cs`) is regenerated from `specs/rosetta-api.graphql` by ZeroQL on every build.

> **Note:** ZeroQL codegen runs via a local .NET tool declared in `.config/dotnet-tools.json`. A `Directory.Build.targets` at the repo root automatically runs `dotnet tool restore` before every build, so no manual setup is required after cloning.
> **Note:** ZeroQL codegen runs via a local .NET tool declared in `.config/dotnet-tools.json`. A `Directory.Build.targets` at the repo root automatically runs `dotnet tool restore` before local builds, so no manual setup is required after cloning. CI restores tools explicitly once per job to avoid concurrent tool restore races during solution builds.

To update both specs to a new API version, use the convenience script:
```bash
./update-spec.sh <version> # e.g. 1.0.33
```

To find the latest version number: open the [Rosetta API Exchange page](https://anypoint.mulesoft.com/exchange/portals/university-of-california-346/9b04bfa8-6eeb-4d85-b676-91db930f8411/iam-rosetta-api/), open the **Download** dropdown, and hover over any link — the version appears in the URL shown in the browser status bar.
The [Rosetta API Anypoint Exchange page](https://anypoint.mulesoft.com/exchange/portals/university-of-california-346/9b04bfa8-6eeb-4d85-b676-91db930f8411/iam-rosetta-api/) is the source of truth for the API. To find the latest version number, open the **Download** dropdown and hover over any link — the version appears in the URL shown in the browser status bar.

The script downloads the spec from MuleSoft Exchange, extracts the embedded GraphQL SDL into `specs/rosetta-api.graphql` (appending the `schema { query: Query }` root required by ZeroQL), and updates the README version badge. Then rebuild:
```bash
Expand Down Expand Up @@ -448,7 +448,7 @@ Running integration tests:

- The integration test project `IntegrationTests` uses the same `.env` file as the example project.
- Tests also read environment variables, which is useful for CI. The supported environment variable names use double underscores for hierarchy (for example: `RosettaClient__ClientId` and `RosettaClient__ClientSecret`).
- See [IntegrationTests/README.md](IntegrationTests/README.md) for more details.
- See [IntegrationTests/README.md](https://github.com/ucdavis/Rosetta-API-Client-DotNet/blob/main/IntegrationTests/README.md) for more details.

Run tests locally with:

Expand Down Expand Up @@ -487,6 +487,6 @@ MIT License - See LICENSE file for details
## Support

For issues, questions, or contributions:
- **Issues**: [GitHub Issues](https://github.com/ucdavis/UCD.Rosetta.Client/issues)
- **Issues**: [GitHub Issues](https://github.com/ucdavis/Rosetta-API-Client-DotNet/issues)
- **Documentation**: [Rosetta API Docs](https://anypoint.mulesoft.com/exchange/portals/university-of-california-346/9b04bfa8-6eeb-4d85-b676-91db930f8411/iam-rosetta-api/)
- **UC Davis IAM Team**: Contact your IAM representative for credentials and API access
6 changes: 3 additions & 3 deletions UCD.Rosetta.Client/UCD.Rosetta.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
<Company>University of California, Davis</Company>
<Description>Official .NET client library for the UC Davis IAM Rosetta API. Provides easy access to identity and access management data from UC Davis IAM services.</Description>
<PackageTags>ucdavis;iam;identity;rosetta;api-client</PackageTags>
<PackageProjectUrl>https://github.com/ucdavis/UCD.Rosetta.Client</PackageProjectUrl>
<RepositoryUrl>https://github.com/ucdavis/UCD.Rosetta.Client</RepositoryUrl>
<PackageProjectUrl>https://github.com/ucdavis/Rosetta-API-Client-DotNet</PackageProjectUrl>
<RepositoryUrl>https://github.com/ucdavis/Rosetta-API-Client-DotNet</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand All @@ -36,7 +36,7 @@
</ItemGroup>

<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<!-- Run NSwag to generate client code before build -->
Expand Down