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
11 changes: 11 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can find the
full documentation for it [in our repository](https://github.com/changesets/changesets).

We have a quick list of common questions to get you started engaging with this project in
[our docs](https://github.com/changesets/changesets/blob/main/docs/common-questions.md). We also have a
[introductory guide](https://github.com/changesets/changesets/blob/main/docs/intro-to-using-changesets.md)
and information about how we [version things](https://github.com/changesets/changesets/blob/main/docs/versioning-policy.md)
for further context.
14 changes: 14 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "kjldev/purview-telemetry-sourcegenerator" }
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
7 changes: 7 additions & 0 deletions .changeset/embedded-attribute-definition.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"purview-telemetry-sourcegenerator": minor
---

Use `AddEmbeddedAttributeDefinition` to inject `Microsoft.CodeAnalysis.EmbeddedAttribute` via Roslyn's official API, and apply `[global::Microsoft.CodeAnalysis.Embedded]` to all generated marker attribute types.

This ensures generated marker types are invisible to IDE tooling and consumer code, eliminating spurious symbol conflicts. Requires Roslyn 4.14.0+ (ships with Visual Studio 2022 17.14+ and .NET 10 SDK).
141 changes: 61 additions & 80 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@ on:
branches:
- main
paths:
- .github/workflows/cd.yml
- Justfile
- global.json
- package.json
- benchmarks/**
- samples/**
- src/**

jobs:
release:
Expand All @@ -35,36 +29,8 @@ jobs:
with:
fetch-depth: 0

- id: release-trigger
name: Determine whether to publish a release
env:
EVENT_NAME: ${{ github.event_name }}
BEFORE_SHA: ${{ github.event.before }}
CURRENT_SHA: ${{ github.sha }}
shell: bash
run: |
set -euo pipefail

if [[ "${EVENT_NAME}" == "workflow_dispatch" ]]; then
echo "should_release=true" >> "${GITHUB_OUTPUT}"
exit 0
fi

if [[ "${BEFORE_SHA}" == "0000000000000000000000000000000000000000" ]]; then
changed_files="$(git show --name-only --pretty='' "${CURRENT_SHA}")"
else
changed_files="$(git diff --name-only "${BEFORE_SHA}" "${CURRENT_SHA}")"
fi

if grep -Fxq "package.json" <<< "${changed_files}"; then
echo "should_release=true" >> "${GITHUB_OUTPUT}"
else
echo "should_release=false" >> "${GITHUB_OUTPUT}"
fi

- id: version
name: Read release version
if: ${{ steps.release-trigger.outputs.should_release == 'true' }}
shell: bash
run: |
set -euo pipefail
Expand All @@ -79,74 +45,62 @@ jobs:
echo "prerelease=false" >> "${GITHUB_OUTPUT}"
fi

- name: Block duplicate version
if: ${{ steps.release-trigger.outputs.should_release == 'true' }}
- id: release-check
name: Check if release already exists
env:
GITHUB_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
shell: bash
run: |
set -euo pipefail

if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_TAG}" > /dev/null 2>&1; then
echo "::error::Tag ${RELEASE_TAG} already exists on origin. Refusing to replace an existing release version."
exit 1
fi

release_status="$(
curl -sS \
-o release.json \
-w "%{http_code}" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}"
)"

if [[ "${release_status}" == "200" ]]; then
echo "::error::Release ${RELEASE_TAG} already exists. Refusing to replace an existing release version."
cat release.json
exit 1
fi

if [[ "${release_status}" != "404" ]]; then
echo "::error::Unexpected GitHub API response while checking ${RELEASE_TAG} (HTTP ${release_status})."
cat release.json
exit 1
if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" &>/dev/null; then
echo "::notice::Release ${RELEASE_TAG} already exists — skipping."
echo "should_release=false" >> "${GITHUB_OUTPUT}"
else
echo "should_release=true" >> "${GITHUB_OUTPUT}"
fi

rm -f release.json

- name: Set up .NET
if: ${{ steps.release-check.outputs.should_release == 'true' }}
uses: actions/setup-dotnet@v5
with:
global-json-file: ./global.json

- name: Restore dotnet tools
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet tool restore

- name: CSharpier format check
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet csharpier check .

- name: Restore main solution
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet restore ${{ env.MAIN_SOLUTION }}

- name: Build main solution
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet build ${{ env.MAIN_SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }}

- name: Test main solution
run: dotnet test --solution ${{ env.MAIN_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet test ${{ env.MAIN_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal

- name: Restore sample solution
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet restore ${{ env.SAMPLE_SOLUTION }}

- name: Build sample solution
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet build ${{ env.SAMPLE_SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }}

- name: Test sample solution
run: dotnet test --solution ${{ env.SAMPLE_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal
if: ${{ steps.release-check.outputs.should_release == 'true' }}
run: dotnet test ${{ env.SAMPLE_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal

- name: Pack NuGet artifacts
if: ${{ steps.release-trigger.outputs.should_release == 'true' }}
if: ${{ steps.release-check.outputs.should_release == 'true' }}
env:
PACKAGE_VERSION: ${{ steps.version.outputs.value }}
shell: bash
Expand All @@ -165,20 +119,47 @@ jobs:
"/p:RepositoryCommit=${GITHUB_SHA}" \
"/p:COPYRIGHT_YEAR=$(date -u +%Y)"

- name: Create GitHub release
if: ${{ steps.release-trigger.outputs.should_release == 'true' }}
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.version.outputs.tag }}
name: ${{ steps.version.outputs.tag }}
target_commitish: ${{ github.sha }}
generate_release_notes: true
prerelease: ${{ steps.version.outputs.prerelease }}
fail_on_unmatched_files: true
files: |
./artifacts/nuget/*.nupkg
- name: Publish GitHub Release
if: ${{ steps.release-check.outputs.should_release == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
PRERELEASE: ${{ steps.version.outputs.prerelease }}
shell: bash
run: |
set -euo pipefail

# On any error: delete the draft release and its tag so the next run starts clean.
# Draft releases are never immutable, so this always succeeds.
cleanup() {
echo "::warning::Release failed — cleaning up draft and tag for ${RELEASE_TAG}..."
gh release delete "${RELEASE_TAG}" --yes --cleanup-tag 2>/dev/null || true
}
trap cleanup ERR

PRERELEASE_FLAG=""
if [[ "${PRERELEASE}" == "true" ]]; then
PRERELEASE_FLAG="--prerelease"
fi

# Create as draft first — draft releases are never immutable, so assets
# can be freely attached. The release becomes immutable only after --draft=false.
gh release create "${RELEASE_TAG}" \
--draft \
--title "${RELEASE_TAG}" \
--generate-notes \
${PRERELEASE_FLAG} \
./artifacts/nuget/*.nupkg \
./artifacts/nuget/*.snupkg

# Publish: marks as latest and becomes immutable. Any retry will clean up above.
gh release edit "${RELEASE_TAG}" \
--draft=false \
--latest

echo "✅ Release ${RELEASE_TAG} published successfully."

- name: Skip release publish
if: ${{ steps.release-trigger.outputs.should_release != 'true' }}
run: echo "package.json did not change on this push, so release publishing was skipped."
if: ${{ steps.release-check.outputs.should_release != 'true' }}
run: echo "Release ${{ steps.version.outputs.tag }} already exists — skipping."

51 changes: 49 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
run: dotnet build ${{ env.SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }}

- name: Run tests
run: dotnet test --solution ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }}
run: dotnet test ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }}

build-and-test-samples:
name: Build and Test Samples
Expand All @@ -78,7 +78,51 @@ jobs:
run: dotnet build ${{ env.SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }}

- name: Run tests
run: dotnet test --solution ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }}
run: dotnet test ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }}

changeset-check:
name: Changeset Check
runs-on: ubuntu-latest

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

- name: Check for changeset
env:
HEAD_REF: ${{ github.head_ref }}
PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
shell: bash
run: |
set -euo pipefail

# Version Packages PRs are created by the release bot — no changeset needed
if [[ "${HEAD_REF}" == changeset-release/* ]]; then
echo "Version Packages PR — changeset check skipped."
exit 0
fi

# PRs labelled skip-changeset are explicitly opted out (docs-only, CI-only, etc.)
if echo "${PR_LABELS}" | grep -q '"skip-changeset"'; then
echo "skip-changeset label present — changeset check skipped."
exit 0
fi

git fetch origin main --depth=1

changed_files="$(git diff --name-only origin/main...HEAD)"
changeset_files="$(echo "${changed_files}" | grep -E '^\.changeset/[^/]+\.md$' | grep -v 'README\.md' || true)"

if [[ -z "${changeset_files}" ]]; then
echo "::error::No changeset file found."
echo "::error::Run 'just changeset' to create one, or add the 'skip-changeset' label if this PR needs no release note."
exit 1
fi

echo "✅ Changeset found:"
echo "${changeset_files}"

ci-gate:
name: CI Gate
Expand All @@ -88,13 +132,15 @@ jobs:
- format-check
- build-and-test
- build-and-test-samples
- changeset-check

steps:
- name: Verify upstream jobs succeeded
env:
FORMAT_CHECK_RESULT: ${{ needs['format-check'].result }}
BUILD_AND_TEST_RESULT: ${{ needs['build-and-test'].result }}
BUILD_AND_TEST_SAMPLES_RESULT: ${{ needs['build-and-test-samples'].result }}
CHANGESET_CHECK_RESULT: ${{ needs['changeset-check'].result }}
shell: bash
run: |
set -euo pipefail
Expand All @@ -103,6 +149,7 @@ jobs:
["Format Check"]="${FORMAT_CHECK_RESULT}"
["Build and Test"]="${BUILD_AND_TEST_RESULT}"
["Build and Test Samples"]="${BUILD_AND_TEST_SAMPLES_RESULT}"
["Changeset Check"]="${CHANGESET_CHECK_RESULT}"
)

failures=0
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/release-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Release PR

permissions:
contents: write
pull-requests: write

on:
push:
branches:
- main

jobs:
release-pr:
name: Create or update Release PR
runs-on: ubuntu-latest

steps:
- name: Generate GitHub App token
id: app-token
if: ${{ secrets.APP_ID != '' }}
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token || secrets.CHANGESET_TOKEN }}

- name: Set up Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun install

- name: Create or update Release PR
uses: changesets/action@v1
with:
version: bun run version-packages
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token || secrets.CHANGESET_TOKEN }}
2 changes: 1 addition & 1 deletion .wiki
Submodule .wiki updated from c6b117 to 39c51b
Loading
Loading