Skip to content

ci: NuGet trusted-publishing workflow for 4 semconv packages - #158

Merged
ANcpLua merged 1 commit into
mainfrom
nuget-publish-workflow
Apr 22, 2026
Merged

ci: NuGet trusted-publishing workflow for 4 semconv packages#158
ANcpLua merged 1 commit into
mainfrom
nuget-publish-workflow

Conversation

@ANcpLua

@ANcpLua ANcpLua commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Summary

Adds .github/workflows/nuget-publish.yml — OIDC trusted-publishing workflow that publishes all 4 semconv packages to nuget.org via short-lived tokens (no long-lived NUGET_API_KEY secret).

Packages published (once this PR and PRs #156 + #157 are all merged):

  1. Qyl.SemanticConventions (core types) — from PR feat(semconv): donation-ready NuGet packages + runtime cutover #156
  2. Qyl.OpenTelemetry.SemanticConventions (OTel stable) — from PR feat(semconv): donation-ready NuGet packages + runtime cutover #156
  3. Qyl.OpenTelemetry.SemanticConventions.Incubating (OTel experimental) — from PR feat(semconv): donation-ready NuGet packages + runtime cutover #156
  4. Qyl.OpenTelemetry.SemanticConventions.Analyzers (Roslyn + codefixes) — from PR feat(analyzers): 245 per-entry deprecated semconv diagnostics + codefixes #157

Triggers:

  • push of any v* tag
  • workflow_dispatch with a version string

188 lines, 1 file.

Scope

Target achieved: 100% of the workflow itself.

  • OIDC permissions correctly scoped (id-token: write, contents: read)
  • concurrency guard prevents overlapping tag pushes
  • Version derivation: from tag on push, from input on workflow_dispatch
  • Matrix fan-out across the 4 packages with fail-fast: false (documented reason: during bootstrap, tag pushes on a feature branch only contain a subset)

Missing to 100%: nothing on this branch. External dependencies (out of scope):

Risk

15% — very certain goal is met, small residual risk on first-publish activation path.

  • Workflow logic is standard GitHub Actions + dotnet nuget push with NuGet.Protocol OIDC support
  • Residual 15%: first publish of each package will activate its pending nuget.org policy; if a policy hasn't finished the 7-day activation window the push will fail on that specific package (easy retry). The matrix's fail-fast: false limits blast radius.

What's left to do or delete to reach 100%

Test plan

🤖 Generated with Claude Code

Publishes Qyl.SemanticConventions, Qyl.OpenTelemetry.SemanticConventions,
Qyl.OpenTelemetry.SemanticConventions.Incubating, and
Qyl.OpenTelemetry.SemanticConventions.Analyzers to nuget.org via OIDC
(NuGet/login@v1, environment `nuget`, no long-lived API key).

Matrix publish isolates failures per package. fail-fast:false keeps
partial publishes working during the 3-PR bootstrap window (workflow,
semconv feature, analyzer feature) — once the two feature PRs merge,
all 4 legs succeed on every tag push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 22, 2026 13:07
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A new GitHub Actions workflow file is added at .github/workflows/nuget-publish.yml containing four jobs. The version job determines package versioning based on tag pushes matching v* or manual dispatch. The build job compiles four .NET projects across ubuntu-latest, windows-latest, and macos-latest. The publish job executes a four-leg matrix, packing each project, authenticating via NuGet OIDC, and pushing packages to https://api.nuget.org/v3/index.json. The release job creates GitHub release tags with generated notes. Release and CI-style versioning strategies are applied conditionally based on trigger type.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commits format with 'ci' prefix, includes scope '(NuGet trusted-publishing workflow for 4 semconv packages)', is 60 characters, and clearly summarizes the main change.
Description check ✅ Passed Description is directly related to the changeset: it details the workflow being added, its purpose (OIDC trusted publishing), trigger conditions, matrix configuration, scope coverage, external dependencies, and risk assessment.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new GitHub Actions workflow to publish the upcoming semconv-related NuGet packages via NuGet.org OIDC trusted publishing (no long-lived API key secret), and to create a GitHub Release for the version.

Changes:

  • Introduces .github/workflows/nuget-publish.yml triggered by v* tags or manual dispatch with a version.
  • Builds the targeted package projects on Ubuntu/Windows/macOS before publishing.
  • Publishes via NuGet/login@v1 (OIDC) and then creates a GitHub Release for the tag.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +107
- name: Build Qyl.SemanticConventions
run: dotnet build packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Incubating
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Analyzers

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

The header says partial publishes are expected during the bootstrap window when only a subset of the 4 csprojs exist on a given ref, but this build job hard-builds all four projects unconditionally. If any of the package csprojs are missing (or intentionally not present yet), the build job will fail and block the publish matrix entirely. Consider making the build job conditional per project (e.g., check file existence and skip) or restructuring so the per-package publish matrix does the build/pack for just the packages that exist.

Suggested change
- name: Build Qyl.SemanticConventions
run: dotnet build packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Incubating
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Analyzers
- name: Build Qyl.SemanticConventions
if: ${{ hashFiles('packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj') != '' }}
run: dotnet build packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions
if: ${{ hashFiles('packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj') != '' }}
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Incubating
if: ${{ hashFiles('packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj') != '' }}
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Analyzers
if: ${{ hashFiles('packages/Qyl.OpenTelemetry.SemanticConventions.Analyzers/Qyl.OpenTelemetry.SemanticConventions.Analyzers.csproj') != '' }}

Copilot uses AI. Check for mistakes.
# waits for all matrix combinations.
release:
needs: [version, publish]
if: needs.version.outputs.is_release == 'true'

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

The comments say this release job runs “after every publish leg finishes (success or partial)”, but needs: [version, publish] means it only runs if the overall publish job succeeds. With a matrix, a single failing leg makes publish fail, so this job will be skipped and you won’t get a GitHub release on partial bootstrap publishes. If you want the release job to run after the matrix completes regardless of failures, use an if: always() && needs.version.outputs.is_release == 'true' condition (and optionally gate creation based on needs.publish.result).

Suggested change
if: needs.version.outputs.is_release == 'true'
if: always() && needs.version.outputs.is_release == 'true'

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +78
version:
description: 'Version to publish (without v prefix, e.g. 1.0.0)'
required: true
type: string

concurrency:
group: nuget-publish-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: 1
CI: true

jobs:
version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.ver.outputs.version }}
is_release: ${{ steps.ver.outputs.is_release }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- id: ver
env:
EVENT_NAME: ${{ github.event_name }}
DISPATCH_VERSION: ${{ github.event.inputs.version }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "version=${DISPATCH_VERSION}" >> "$GITHUB_OUTPUT"
echo "is_release=true" >> "$GITHUB_OUTPUT"
elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then
echo "version=${REF_NAME#v}" >> "$GITHUB_OUTPUT"
echo "is_release=true" >> "$GITHUB_OUTPUT"

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

The workflow_dispatch input expects a version “without v prefix”, but the version step uses the input verbatim. If a user supplies v1.0.0 by mistake, dotnet pack -p:Version=... will receive an invalid NuGet version and the release job will try to create tag vv1.0.0. Consider normalizing/validating the input (e.g., strip an optional leading v and fail fast if the remaining string isn’t a valid NuGet version).

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +28
# Cross-branch reality (2026-04):
# Packages 1–3 live on branch `claude/focused-gauss-3c1f8d` (semconv PR).
# Package 4 lives on branch `claude/goofy-cohen-8f4c45` (analyzer PR).
# Until BOTH PRs merge to main, a tag push or workflow_dispatch on a feature
# branch will only publish the subset of packages present in that ref. The
# publish matrix is fail-fast:false by design — partial success is the
# expected outcome during this bootstrap window. Once both PRs merge, every
# tag push produces all 4 nupkgs.

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

The header comment hard-codes temporary branch names and a point-in-time note (“Cross-branch reality (2026-04) …”). This will become stale immediately after merge and can mislead future maintainers. Consider replacing it with a durable reference to the prerequisite PRs/packages (or removing the branch-specific details entirely).

Suggested change
# Cross-branch reality (2026-04):
# Packages 1–3 live on branch `claude/focused-gauss-3c1f8d` (semconv PR).
# Package 4 lives on branch `claude/goofy-cohen-8f4c45` (analyzer PR).
# Until BOTH PRs merge to main, a tag push or workflow_dispatch on a feature
# branch will only publish the subset of packages present in that ref. The
# publish matrix is fail-fast:false by design — partial success is the
# expected outcome during this bootstrap window. Once both PRs merge, every
# tag push produces all 4 nupkgs.
# This workflow publishes whichever package projects are present in the
# checked-out ref. If a tag or manual run targets a ref that does not yet
# contain all four package projects, only the available subset can be packed
# and published. The publish matrix is configured with fail-fast: false so
# that each package publish attempt proceeds independently. Refs that contain
# all four package projects will publish all four nupkgs.

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/nuget-publish.yml:
- Around line 37-42: Normalize and validate the manual version input from
workflow_dispatch by stripping an optional leading "v" from inputs.version and
verifying the remaining string is a valid semver before using it to construct
tags/releases; update the workflow steps that read inputs.version (the
workflow_dispatch input block and the steps referenced around the regions
corresponding to lines 73-75 and 183-187) to first set a sanitized variable
(e.g., "version" or "normalized_version") by removing a leading 'v' if present
and then fail early with a clear message if the sanitized value is not a valid
semantic version, and use that sanitized variable everywhere you currently use
inputs.version.
- Around line 167-172: The release job is skipped if any matrix publish leg
fails because "needs: publish" requires all legs to succeed; create an explicit
fan-in aggregator job (e.g., "publish-fan-in") that has needs: publish and runs
regardless of leg outcomes (use if: always() or run with a step that uses
job.status), compute a boolean output like
publish-fan-in.outputs.partial_or_full_success (set via a step that checks
needs.publish[*].result or job.status to detect any_success or all_failed), and
then change the release job to depend on publish-fan-in (replace needs: publish
with needs: [version, publish-fan-in]) and use the aggregator output in the
release if condition instead of relying on needs.publish; keep the existing
version check (needs.version.outputs.is_release) combined with the aggregator
output so release runs for intended partial publishes while preserving
fail-fast: false on the matrix.
- Line 64: Replace every mutable action reference in the workflow (e.g.,
actions/checkout@v6 and actions/setup-dotnet@v5) with their corresponding
immutable 40-character commit SHAs; locate each "uses:" entry that currently
uses a tag and swap the tag (e.g., `@v6`, `@v5`) for the correct full commit SHA for
that action, ensuring consistency for all occurrences of actions/checkout and
actions/setup-dotnet in the file so the workflow is pinned to fixed commits.
- Around line 86-108: The build job currently unconditionally runs dotnet build
for four projects
(packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj,
packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj,
packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj,
packages/Qyl.OpenTelemetry.SemanticConventions.Analyzers/Qyl.OpenTelemetry.SemanticConventions.Analyzers.csproj)
which causes failure when one or more paths are missing; modify the workflow so
the build step either reuses the existing package matrix (matrix-driven job) or
wraps each dotnet build invocation in a file-existence check (e.g., test for the
.csproj path before running dotnet build), ensuring the build job succeeds and
allows the publish matrix job to run.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: da15c51a-bb48-4a95-9be8-2abffce05532

📥 Commits

Reviewing files that changed from the base of the PR and between ea49455 and 9db2ea1.

📒 Files selected for processing (1)
  • .github/workflows/nuget-publish.yml
📜 Review details
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Dependency Audit
  • GitHub Check: Frontend (React)
  • GitHub Check: Backend (.NET)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (csharp)
🧰 Additional context used
📓 Path-based instructions (1)
.github/**

⚙️ CodeRabbit configuration file

GitHub Actions workflows. Review for: action version pinning (use SHA not tags), proper secret handling (no secrets in logs), unnecessary workflow triggers, and job dependency correctness. Flag missing concurrency groups on push-triggered workflows.

Files:

  • .github/workflows/nuget-publish.yml

Comment on lines +37 to +42
workflow_dispatch:
inputs:
version:
description: 'Version to publish (without v prefix, e.g. 1.0.0)'
required: true
type: string

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Normalize workflow_dispatch versions before using them.

The manual path forwards inputs.version verbatim, while .github/workflows/release.yml:14-18 tells operators to enter versions like v1.0.0. If someone follows that convention here, this workflow emits version=v1.0.0 and then creates a vv1.0.0 GitHub release tag. Strip an optional leading v and validate the remainder before setting the output.

Suggested fix
       - id: ver
         env:
           EVENT_NAME: ${{ github.event_name }}
           DISPATCH_VERSION: ${{ github.event.inputs.version }}
           REF_NAME: ${{ github.ref_name }}
         run: |
           if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
-            echo "version=${DISPATCH_VERSION}" >> "$GITHUB_OUTPUT"
+            VERSION="${DISPATCH_VERSION#v}"
+            test -n "$VERSION"
+            echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
             echo "is_release=true" >> "$GITHUB_OUTPUT"
           elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then

Also applies to: 73-75, 183-187

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/nuget-publish.yml around lines 37 - 42, Normalize and
validate the manual version input from workflow_dispatch by stripping an
optional leading "v" from inputs.version and verifying the remaining string is a
valid semver before using it to construct tags/releases; update the workflow
steps that read inputs.version (the workflow_dispatch input block and the steps
referenced around the regions corresponding to lines 73-75 and 183-187) to first
set a sanitized variable (e.g., "version" or "normalized_version") by removing a
leading 'v' if present and then fail early with a clear message if the sanitized
value is not a valid semantic version, and use that sanitized variable
everywhere you currently use inputs.version.

version: ${{ steps.ver.outputs.version }}
is_release: ${{ steps.ver.outputs.is_release }}
steps:
- uses: actions/checkout@v6

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '^\s*-\s*uses:\s*[^@]+@(?![0-9a-f]{40}\b)\S+' .github/workflows/nuget-publish.yml

Repository: Alexander-Nachtmann/qyl

Length of output: 299


Pin all GitHub Actions to immutable commit SHAs.

Every uses: statement in this workflow references a mutable tag (@v5, @v6) instead of a full commit SHA. This exposes the workflow to silent behavioral changes from upstream action updates without visibility in pull requests. Replace all mutable tags with their corresponding 40-character commit SHAs:

  • Line 64: actions/checkout@v6
  • Line 97: actions/checkout@v6
  • Line 98: actions/setup-dotnet@v5
  • Line 140: actions/checkout@v6
  • Line 143: actions/setup-dotnet@v5
  • Line 177: actions/checkout@v6
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/nuget-publish.yml at line 64, Replace every mutable action
reference in the workflow (e.g., actions/checkout@v6 and
actions/setup-dotnet@v5) with their corresponding immutable 40-character commit
SHAs; locate each "uses:" entry that currently uses a tag and swap the tag
(e.g., `@v6`, `@v5`) for the correct full commit SHA for that action, ensuring
consistency for all occurrences of actions/checkout and actions/setup-dotnet in
the file so the workflow is pinned to fixed commits.

Comment on lines +86 to +108
# Compile on all three OSes to catch platform-specific issues before we push.
# Scoped to the 4 publishable csprojs — qyl.slnx contains unrelated projects
# (collector, dashboard, loom) with WIP CI state that isn't our concern here.
build:
needs: version
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: Build Qyl.SemanticConventions
run: dotnet build packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Incubating
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj -c Release -p:Version=${{ needs.version.outputs.version }}
- name: Build Qyl.OpenTelemetry.SemanticConventions.Analyzers
run: dotnet build packages/Qyl.OpenTelemetry.SemanticConventions.Analyzers/Qyl.OpenTelemetry.SemanticConventions.Analyzers.csproj -c Release -p:Version=${{ needs.version.outputs.version }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The bootstrap strategy is blocked by the unconditional four-project build.

Lines 101-108 build all four .csproj files before publish starts. On the branch states described in Lines 21-28, at least one of those paths is absent, so build fails and no package reaches the matrix publish step. Reuse the package matrix for build, or guard each build with a file-existence check.

As per coding guidelines, .github/**: GitHub Actions workflows. Review for: job dependency correctness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/nuget-publish.yml around lines 86 - 108, The build job
currently unconditionally runs dotnet build for four projects
(packages/Qyl.SemanticConventions/Qyl.SemanticConventions.csproj,
packages/Qyl.OpenTelemetry.SemanticConventions/Qyl.OpenTelemetry.SemanticConventions.csproj,
packages/Qyl.OpenTelemetry.SemanticConventions.Incubating/Qyl.OpenTelemetry.SemanticConventions.Incubating.csproj,
packages/Qyl.OpenTelemetry.SemanticConventions.Analyzers/Qyl.OpenTelemetry.SemanticConventions.Analyzers.csproj)
which causes failure when one or more paths are missing; modify the workflow so
the build step either reuses the existing package matrix (matrix-driven job) or
wraps each dotnet build invocation in a file-existence check (e.g., test for the
.csproj path before running dotnet build), ensuring the build job succeeds and
allows the publish matrix job to run.

Comment on lines +167 to +172
# Separate job so matrix legs don't race on `gh release create`. Runs only
# after every publish leg finishes (success or partial) — `needs: publish`
# waits for all matrix combinations.
release:
needs: [version, publish]
if: needs.version.outputs.is_release == 'true'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Partial publish currently suppresses release creation.

fail-fast: false only keeps the matrix running; it does not make publish succeed. If any publish leg fails, needs: publish causes release to be skipped, so the workflow cannot deliver the “partial success during bootstrap” behavior documented above. If partial publish is intentional, move release behind an explicit fan-in that uses always() plus a computed success signal.

As per coding guidelines, .github/**: GitHub Actions workflows. Review for: job dependency correctness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/nuget-publish.yml around lines 167 - 172, The release job
is skipped if any matrix publish leg fails because "needs: publish" requires all
legs to succeed; create an explicit fan-in aggregator job (e.g.,
"publish-fan-in") that has needs: publish and runs regardless of leg outcomes
(use if: always() or run with a step that uses job.status), compute a boolean
output like publish-fan-in.outputs.partial_or_full_success (set via a step that
checks needs.publish[*].result or job.status to detect any_success or
all_failed), and then change the release job to depend on publish-fan-in
(replace needs: publish with needs: [version, publish-fan-in]) and use the
aggregator output in the release if condition instead of relying on
needs.publish; keep the existing version check
(needs.version.outputs.is_release) combined with the aggregator output so
release runs for intended partial publishes while preserving fail-fast: false on
the matrix.

@ANcpLua
ANcpLua merged commit 3dedb8e into main Apr 22, 2026
37 checks passed
@ANcpLua
ANcpLua deleted the nuget-publish-workflow branch April 22, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants