Skip to content

Publish SDK packages #70

Publish SDK packages

Publish SDK packages #70

Workflow file for this run

name: Publish SDK packages
env:
HUSKY: 0
on:
workflow_dispatch:
inputs:
dist-tag:
description: "Tag to publish under"
type: choice
required: true
default: "prerelease"
options:
- latest
- prerelease
- unstable
version:
description: "Version override (optional, e.g., 1.0.0). If empty, auto-increments."
type: string
required: false
permissions:
contents: write
id-token: write # Required for OIDC
actions: write # Required to trigger changelog workflow
concurrency:
group: publish
cancel-in-progress: false
jobs:
# Shared job to calculate version once for all publish jobs
version:
name: Calculate Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.VERSION }}
current: ${{ steps.version.outputs.CURRENT }}
current-prerelease: ${{ steps.version.outputs.CURRENT_PRERELEASE }}
defaults:
run:
working-directory: ./nodejs
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-node@v6
with:
node-version: "22.x"
- run: npm ci --ignore-scripts
- name: Get version
id: version
run: |
CURRENT="$(node scripts/get-version.js current)"
echo "CURRENT=$CURRENT" >> $GITHUB_OUTPUT
echo "Current latest version: $CURRENT" >> $GITHUB_STEP_SUMMARY
CURRENT_PRERELEASE="$(node scripts/get-version.js current-prerelease)"
echo "CURRENT_PRERELEASE=$CURRENT_PRERELEASE" >> $GITHUB_OUTPUT
echo "Current prerelease version: $CURRENT_PRERELEASE" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
# Validate version format matches dist-tag
# TEMPORARY: skips validation for "latest" so prerelease versions
# can be published under that tag. To ship stable 1.0.0, revert the
# commit that introduced this temporary change.
if [ "${{ github.event.inputs.dist-tag }}" != "latest" ]; then
if [[ "$VERSION" != *-* ]]; then
echo "❌ Error: Version '$VERSION' has no prerelease suffix but dist-tag is '${{ github.event.inputs.dist-tag }}'" >> $GITHUB_STEP_SUMMARY
echo "Use a version with suffix (e.g., '1.0.0-preview.0') for prerelease/unstable"
exit 1
fi
fi
echo "Using manual version override: $VERSION" >> $GITHUB_STEP_SUMMARY
else
VERSION="$(node scripts/get-version.js ${{ github.event.inputs.dist-tag }})"
echo "Auto-incremented version: $VERSION" >> $GITHUB_STEP_SUMMARY
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
publish-nodejs:
name: Publish Node.js SDK
needs: version
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./nodejs
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-node@v6
with:
node-version: "22.x"
- name: Update npm for OIDC support
run: npm i -g "npm@11.6.3"
- run: npm ci --ignore-scripts
- name: Set version
run: node scripts/set-version.js
env:
VERSION: ${{ needs.version.outputs.version }}
- name: Build
run: npm run build
- name: Pack
run: npm pack
- name: Upload artifact
uses: actions/upload-artifact@v7.0.0
with:
name: nodejs-package
path: nodejs/*.tgz
- name: Publish to npm
if: github.ref == 'refs/heads/main' || github.event.inputs.dist-tag == 'unstable'
run: npm publish --tag ${{ github.event.inputs.dist-tag }} --access public --registry https://registry.npmjs.org
publish-dotnet:
name: Publish .NET SDK
if: github.event.inputs.dist-tag != 'unstable'
needs: version
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./dotnet
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"
- name: Restore dependencies
run: dotnet restore
- name: Build and pack
run: dotnet pack src/GitHub.Copilot.SDK.csproj -c Release -p:Version=${{ needs.version.outputs.version }} -o ./artifacts
- name: Upload artifact
uses: actions/upload-artifact@v7.0.0
with:
name: dotnet-package
path: |
dotnet/artifacts/*.nupkg
dotnet/artifacts/*.snupkg
- name: NuGet login (OIDC)
if: github.ref == 'refs/heads/main'
uses: NuGet/login@v1
id: nuget-login
with:
# The following must be a username, not an organization name, and that user must have configured Trusted Publishing
# for this owner/repo/workflow combination in their NuGet.org account settings. We could set up a dedicated user for
# this purpose if needed, but then we'd have to manage that account separately. Other GitHub-owned packages on NuGet
# are associated with individual maintainers' accounts too.
user: stevesanderson
- name: Publish to NuGet
if: github.ref == 'refs/heads/main'
run: |
dotnet nuget push ./artifacts/*.nupkg --api-key ${{ steps.nuget-login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols
dotnet nuget push ./artifacts/*.snupkg --api-key ${{ steps.nuget-login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
publish-rust:
name: Publish Rust SDK
if: github.event.inputs.dist-tag != 'unstable'
needs: version
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./rust
steps:
- uses: actions/checkout@v6.0.2
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.94.0"
- uses: Swatinem/rust-cache@v2
with:
workspaces: "rust"
- name: Set version
run: sed -i -E 's/^version = ".*"$/version = "${{ needs.version.outputs.version }}"/' Cargo.toml
- name: Snapshot CLI version + hashes for build.rs
run: bash scripts/snapshot-bundled-cli-version.sh
- name: Verify cli-version.txt exists
run: |
if [[ ! -f cli-version.txt ]]; then
echo "::error::cli-version.txt was not generated. The Snapshot step must run before packaging."
exit 1
fi
- name: Package (dry run)
run: cargo publish --dry-run --allow-dirty
- name: Upload artifact
uses: actions/upload-artifact@v7.0.0
with:
name: rust-package
path: rust/target/package/*.crate
- name: Publish to crates.io
if: github.ref == 'refs/heads/main'
run: cargo publish --allow-dirty
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
publish-python:
name: Publish Python SDK
if: github.event.inputs.dist-tag != 'unstable'
needs: version
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./python
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- uses: actions/setup-node@v6
with:
node-version: "22.x"
- name: Set up uv
uses: astral-sh/setup-uv@v7
- name: Install Node.js dependencies (for CLI version)
working-directory: ./nodejs
run: npm ci --ignore-scripts
- name: Set version
run: sed -i "s/^version = .*/version = \"${{ needs.version.outputs.version }}\"/" pyproject.toml
- name: Build platform wheels
run: node scripts/build-wheels.mjs --output-dir dist
- name: Upload artifact
uses: actions/upload-artifact@v7.0.0
with:
name: python-package
path: python/dist/*
- name: Publish to PyPI
if: github.ref == 'refs/heads/main'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist/
github-release:
name: Create GitHub Release
needs: [version, publish-nodejs, publish-dotnet, publish-python, publish-rust]
if: github.ref == 'refs/heads/main' && github.event.inputs.dist-tag != 'unstable'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
# TEMPORARY: both "latest" and "prerelease" create GitHub pre-releases
# since "latest" publishes beta versions. To ship stable 1.0.0, revert
# the commit that introduced this temporary change.
- name: Create GitHub Release
if: github.event.inputs.dist-tag == 'latest' || github.event.inputs.dist-tag == 'prerelease'
run: |
NOTES_FLAG=""
if git rev-parse "v${{ needs.version.outputs.current-prerelease }}" >/dev/null 2>&1; then
NOTES_FLAG="--notes-start-tag v${{ needs.version.outputs.current-prerelease }}"
fi
gh release create "v${{ needs.version.outputs.version }}" \
--prerelease \
--title "v${{ needs.version.outputs.version }}" \
--generate-notes $NOTES_FLAG \
--target ${{ github.sha }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger changelog generation
run: gh workflow run release-changelog.lock.yml -f tag="v${{ needs.version.outputs.version }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Tag Go SDK submodule
if: github.event.inputs.dist-tag == 'latest' || github.event.inputs.dist-tag == 'prerelease'
run: |
set -e
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch --tags
TAG_NAME="go/v${{ needs.version.outputs.version }}"
# Try to create the tag - will fail if it already exists
if git tag "$TAG_NAME" ${{ github.sha }} 2>/dev/null; then
git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git "$TAG_NAME"
echo "Created and pushed tag $TAG_NAME"
else
echo "Tag $TAG_NAME already exists, skipping"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Tag Rust SDK and create Rust GitHub Release
# Rust gets its own version-scoped GitHub Release with notes
# derived from PR titles since the previous Rust tag. The
# cross-language `vX.Y.Z` release above still exists; this one
# is the canonical reference for Rust users.
if: github.event.inputs.dist-tag == 'latest' || github.event.inputs.dist-tag == 'prerelease'
run: |
set -e
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch --tags
VERSION="${{ needs.version.outputs.version }}"
TAG_NAME="rust/v${VERSION}"
if git tag "$TAG_NAME" ${{ github.sha }} 2>/dev/null; then
git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git "$TAG_NAME"
echo "Created and pushed tag $TAG_NAME"
else
echo "Tag $TAG_NAME already exists, skipping tag push"
fi
# Find the previous Rust tag for note generation. Prefer rust/v*,
# fall back to the historical rust-v* tags from the release-plz era.
PREV_TAG=$(git tag --list 'rust/v*' --sort=-v:refname | grep -vFx "$TAG_NAME" | head -n1)
if [ -z "$PREV_TAG" ]; then
PREV_TAG=$(git tag --list 'rust-v*' --sort=-v:refname | head -n1)
fi
NOTES_FLAG=""
if [ -n "$PREV_TAG" ]; then
NOTES_FLAG="--notes-start-tag $PREV_TAG"
echo "Generating notes from $PREV_TAG..$TAG_NAME"
else
echo "No previous Rust tag found; generating notes from full history"
fi
PRERELEASE_FLAG=""
if [ "${{ github.event.inputs.dist-tag }}" = "prerelease" ]; then
PRERELEASE_FLAG="--prerelease"
fi
gh release create "$TAG_NAME" \
--title "$TAG_NAME" \
--generate-notes $NOTES_FLAG $PRERELEASE_FLAG \
--target ${{ github.sha }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}