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
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push:
branches:
- main
pull_request:

permissions:
contents: read

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Restore library
run: dotnet restore src/Community.Microsoft.Extensions.AI.CoreML/Community.Microsoft.Extensions.AI.CoreML.csproj

- name: Restore tests
run: dotnet restore src/Community.Microsoft.Extensions.AI.CoreML.Tests/Community.Microsoft.Extensions.AI.CoreML.Tests.csproj

- name: Build library
run: dotnet build src/Community.Microsoft.Extensions.AI.CoreML/Community.Microsoft.Extensions.AI.CoreML.csproj --configuration Release --no-restore

- name: Test
run: dotnet test src/Community.Microsoft.Extensions.AI.CoreML.Tests/Community.Microsoft.Extensions.AI.CoreML.Tests.csproj --configuration Release --no-restore
Comment thread
Copilot marked this conversation as resolved.
56 changes: 56 additions & 0 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Package

on:
workflow_call:
inputs:
version:
description: Package version override (for example: 1.2.3 or 1.2.3-preview1)
required: false
type: string
workflow_dispatch:
inputs:
version:
description: Package version override (for example: 1.2.3 or 1.2.3-preview1)
required: false
type: string

permissions:
contents: read

jobs:
package:
runs-on: macos-latest-xlarge
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Build native bridge
run: make bridge

- name: Pack NuGet package
shell: bash
run: |
mkdir -p artifacts/nuget
PACK_ARGS=(
"-c" "Release"
"-o" "artifacts/nuget"
"-p:ContinuousIntegrationBuild=true"
)

if [ -n "${{ inputs.version }}" ]; then
PACK_ARGS+=("-p:Version=${{ inputs.version }}")
Comment on lines +45 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the version through an environment variable

When this reusable workflow is invoked from release.yml, inputs.version comes from the pushed tag, and Git refs can contain shell-substitution syntax such as $(...) or backticks. Because the expression is interpolated directly into the bash script before execution, a tag like v$(id) turns these lines into command substitutions in both the test and the array assignment, so the package job runs attacker-chosen shell commands before the protected release environment is reached. Put the input in an env var (and/or validate it as SemVer) and reference that variable inside bash instead of embedding the expression in the script text.

Useful? React with 👍 / 👎.

fi

dotnet pack src/Community.Microsoft.Extensions.AI.CoreML/Community.Microsoft.Extensions.AI.CoreML.csproj "${PACK_ARGS[@]}"

- name: Upload NuGet artifacts
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: artifacts/nuget/*
if-no-files-found: error
85 changes: 85 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
derive-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.value }}
steps:
- name: Derive version from tag
id: version
shell: bash
run: |
TAG="${GITHUB_REF_NAME}"
VERSION="${TAG#v}"
if [ -z "$VERSION" ] || [ "$VERSION" = "$TAG" ]; then
echo "Tag must start with 'v' and include a version (example: v1.2.3)." >&2
exit 1
fi
echo "value=$VERSION" >> "$GITHUB_OUTPUT"

package:
needs: derive-version
uses: ./.github/workflows/package.yml
with:
version: ${{ needs.derive-version.outputs.version }}

publish:
needs:
- derive-version
- package
runs-on: ubuntu-latest
environment: release
steps:
- name: Download package artifacts
uses: actions/download-artifact@v4
with:
name: nuget-packages
path: artifacts/nuget

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Publish to NuGet
shell: bash
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "NUGET_API_KEY secret is required." >&2
exit 1
fi

shopt -s nullglob

for pkg in artifacts/nuget/*.nupkg; do
if [[ "$pkg" == *.snupkg ]]; then
continue
fi
dotnet nuget push "$pkg" \
--source "https://api.nuget.org/v3/index.json" \
--api-key "$NUGET_API_KEY" \
--skip-duplicate
done

for sym in artifacts/nuget/*.snupkg; do
dotnet nuget push "$sym" \
--source "https://api.nuget.org/v3/index.json" \
--api-key "$NUGET_API_KEY" \
--skip-duplicate
done

- name: Attach artifacts to GitHub release
uses: softprops/action-gh-release@v2
with:
files: artifacts/nuget/*
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ flowchart LR
Supports plain completions, streaming, multi-turn conversations, **and tool / function calling** (compatible with `Microsoft.Agents.AI`).

For a deeper look at the design, see [docs/architecture.md](docs/architecture.md).
For release automation and publishing steps, see [docs/releasing.md](docs/releasing.md).

---

Expand Down
38 changes: 38 additions & 0 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Releasing

This repository ships three workflows:

- `CI` (`.github/workflows/ci.yml`) for restore/build/test on push and pull request.
- `Package` (`.github/workflows/package.yml`) for building the native bridge and packing NuGet artifacts.
- `Release` (`.github/workflows/release.yml`) for tag-triggered publication to NuGet.

## Tagging convention

Release tags must start with `v` and the remainder is used as package version:

- `v1.2.3` -> `1.2.3`
- `v1.2.3-preview1` -> `1.2.3-preview1`

## Required repository configuration

### Secrets

- `NUGET_API_KEY` (repository or environment secret): API key with permission to publish `Community.Microsoft.Extensions.AI.CoreML` on NuGet.org.

### Environment

- Environment name: `release` (used by `release.yml`).
- Recommended: configure required reviewers on the `release` environment for gated production publishes.

### Runner requirement

The package/release flow requires an Apple Silicon macOS runner label: `macos-latest-xlarge`.

## How to cut a release

1. Ensure `CI` is green on `main`.
2. Create and push a tag using the release version format (`vX.Y.Z` or `vX.Y.Z-suffix`).
3. Wait for the `Release` workflow to finish.
4. Verify:
- NuGet package and symbols are published.
- Artifacts are attached to the GitHub release.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<!-- NuGet package metadata -->
<PropertyGroup>
<PackageId>Community.Microsoft.Extensions.AI.CoreML</PackageId>
<Version>0.1.0-preview1</Version>
<Version Condition="'$(Version)' == ''">0.1.0-preview1</Version>
<Authors>Frederiek Vandepitte and contributors</Authors>
<Company>Community</Company>
<Description>An IChatClient implementation for Microsoft.Extensions.AI that calls Apple's on-device Foundation Models (Apple Intelligence) directly from .NET via P/Invoke. macOS 26+ on Apple Silicon. No MAUI, no HTTP server.</Description>
Expand Down
Loading