Skip to content

Add dependency version validation pipeline#702

Closed
YunchuWang wants to merge 1 commit intomainfrom
wangbill/dep-version-validation
Closed

Add dependency version validation pipeline#702
YunchuWang wants to merge 1 commit intomainfrom
wangbill/dep-version-validation

Conversation

@YunchuWang
Copy link
Copy Markdown
Member

Summary

Adds a GitHub Actions workflow (dep-version-validation.yml) that validates dependency version bumps in Directory.Packages.props do not break the DurableTask SDK NuGet packages when consumed by Azure Functions.

What this pipeline does

  1. Packs all SDK NuGet packages from source into a local NuGet feed
  2. Builds a .NET isolated Function App using PackageReference (not ProjectReference) to the local packages, with packageSourceMapping to ensure packages come from the local feed
  3. Starts Azurite (Azure Storage emulator) in Docker
  4. Runs the Function App in Docker and triggers a HelloCities orchestration
  5. Validates orchestration output contains expected greetings
  6. Verifies loaded SDK assembly versions match the locally built version

Trigger paths

  • \Directory.Packages.props\
  • \Directory.Build.props\ / \Directory.Build.targets\
  • \�ng/targets/Release.props\
  • \ est/SmokeTests/DepValidation/**\

Motivation

The existing \�zure-functions-smoke-tests.yml\ uses \ProjectReference\ for the local SDK packages. This is great for validating code changes but doesn't catch NuGet-level issues (wrong version ranges, missing transitive dependencies, assembly version conflicts) that affect real users who consume these packages via NuGet.

This pipeline mirrors the SDK-PR-Validation pipeline pattern used in AAPT-DTMB.

Add a GitHub Actions workflow that validates dependency version bumps
in Directory.Packages.props do not break the DurableTask SDK NuGet
packages when consumed by Azure Functions.

The pipeline:
- Packs all SDK packages from source into a local NuGet feed
- Builds a .NET isolated Function App using PackageReferences (not
  ProjectReferences) to the local packages
- Starts Azurite and runs the Function App in Docker
- Triggers a HelloCities orchestration and validates output
- Verifies loaded SDK assembly versions match the locally built version

This mirrors the SDK-PR-Validation pipeline pattern used in AAPT-DTMB.
Copilot AI review requested due to automatic review settings April 8, 2026 19:15
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new GitHub Actions workflow and a dedicated Azure Functions (.NET isolated) smoke-test app to validate that dependency-version bumps don’t break the locally packed DurableTask SDK NuGet packages when consumed via PackageReference.

Changes:

  • Added dep-version-validation.yml workflow to pack SDK NuGets, build/publish a Functions app against a local feed, run Azurite + the app in Docker, and validate runtime behavior and loaded assembly versions.
  • Added test/SmokeTests/DepValidation Function App project (host/program/functions) configured to restore DurableTask packages from a local feed via package source mapping.
  • Added Dockerfile + local NuGet feed ignore to support containerized execution in CI.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test/SmokeTests/DepValidation/local.settings.json Adds local dev settings for the smoke-test Function App.
test/SmokeTests/DepValidation/host.json Adds Functions host configuration for the smoke-test app.
test/SmokeTests/DepValidation/Program.cs Minimal .NET isolated worker host startup.
test/SmokeTests/DepValidation/NuGet.config Forces DurableTask package resolution from the local feed using package source mapping.
test/SmokeTests/DepValidation/HelloCitiesOrchestration.cs Adds orchestration + an HTTP endpoint to report loaded SDK assembly versions.
test/SmokeTests/DepValidation/Dockerfile Packages the published Function App into an Azure Functions base image.
test/SmokeTests/DepValidation/DepValidationApp.csproj Defines dependencies (Functions worker + DurableTask SDK from local feed).
test/SmokeTests/DepValidation/.gitignore Ignores locally generated local-packages/ feed.
.github/workflows/dep-version-validation.yml Implements the end-to-end dependency version validation pipeline in CI.

Comment on lines +62 to +83
- name: Parse SDK version
id: version
run: |
set -e
PROPS_FILE="eng/targets/Release.props"
VERSION_PREFIX=$(grep -oP '<VersionPrefix>\K[^<]+' "$PROPS_FILE")
VERSION_SUFFIX=$(grep -oP '<VersionSuffix>\K[^<]+' "$PROPS_FILE" || true)

# Bump patch version to distinguish local build from published
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION_PREFIX"
LOCAL_PATCH=$((PATCH + 1))
LOCAL_VERSION="${MAJOR}.${MINOR}.${LOCAL_PATCH}"
if [ -n "$VERSION_SUFFIX" ]; then
LOCAL_VERSION="${LOCAL_VERSION}-${VERSION_SUFFIX}"
fi

# Update the version in source so assembly versions are consistent
sed -i "s|<VersionPrefix>${VERSION_PREFIX}</VersionPrefix>|<VersionPrefix>${MAJOR}.${MINOR}.${LOCAL_PATCH}</VersionPrefix>|" "$PROPS_FILE"

echo "Published version: ${VERSION_PREFIX}"
echo "Local version: ${LOCAL_VERSION}"
echo "sdk_version=${LOCAL_VERSION}" >> "$GITHUB_OUTPUT"
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

The version parsing/mutation is brittle: grep -P and the exact-string sed replacement will break if Release.props formatting changes (whitespace, attributes, multi-line XML), and mutating tracked files in-place makes failures harder to diagnose. Prefer deriving the version via MSBuild and passing it as properties (e.g., set VersionPrefix/VersionSuffix/Version via dotnet build/pack -p:...) rather than editing eng/targets/Release.props.

Copilot uses AI. Check for mistakes.
Comment on lines +190 to +198
- name: Start Azurite
run: |
npm install -g azurite
docker network create smoketest-network 2>/dev/null || true
docker run -d \
--name azurite-smoketest \
--network smoketest-network \
-p 10000:10000 -p 10001:10001 -p 10002:10002 \
mcr.microsoft.com/azure-storage/azurite
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

npm install -g azurite is unused (Azurite is started via the Docker image), adds time and introduces an extra external supply-chain dependency. Remove the npm install step and rely solely on the container.

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +173
# ---- Verify SDK packages resolved from local-packages ----
- name: Verify SDK packages from local source
run: |
set -e
echo "Verifying SDK packages were restored from local-packages..."
ASSETS_FILE="test/SmokeTests/DepValidation/obj/project.assets.json"
for pkg in "Microsoft.DurableTask.Abstractions" "Microsoft.DurableTask.Client.Grpc" "Microsoft.DurableTask.Worker.Grpc"; do
if grep -qi "$pkg" "$ASSETS_FILE"; then
echo " OK: $pkg found in project.assets.json"
else
echo " FAIL: $pkg NOT found"
exit 1
fi
done
echo "PASS: SDK packages verified in build output."
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

This check does not actually verify the packages were restored from the local feed—it only verifies the package IDs appear in project.assets.json, which would also be true if they were restored from nuget.org. To make this a real guardrail, run restore explicitly with the intended NuGet.config (e.g., dotnet restore ... --configfile test/SmokeTests/DepValidation/NuGet.config) and/or rely on packageSourceMapping by making the restore fail if local-packages is missing (e.g., temporarily disabling nuget.org for the Microsoft.DurableTask.* pattern during this verification step).

Copilot uses AI. Check for mistakes.
--name azurite-smoketest \
--network smoketest-network \
-p 10000:10000 -p 10001:10001 -p 10002:10002 \
mcr.microsoft.com/azure-storage/azurite
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

The Azurite image is unpinned (defaults to latest), which can introduce CI flakiness when the image updates. Pin to a specific tag (or ideally an image digest) to make the workflow deterministic.

Suggested change
mcr.microsoft.com/azure-storage/azurite
mcr.microsoft.com/azure-storage/azurite:3.33.0

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +61
string[] sdkAssemblyPrefixes = new[]
{
"Microsoft.DurableTask.Abstractions",
"Microsoft.DurableTask.Client",
"Microsoft.DurableTask.Client.Grpc",
"Microsoft.DurableTask.Worker",
"Microsoft.DurableTask.Worker.Grpc",
"Microsoft.DurableTask.Grpc",
};

SortedDictionary<string, string> loadedVersions = new();
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
AssemblyName name = asm.GetName();
foreach (string prefix in sdkAssemblyPrefixes)
{
if (string.Equals(name.Name, prefix, StringComparison.OrdinalIgnoreCase))
{
string? infoVersion = asm
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;

// Strip source-link commit hash suffix (e.g. "1.23.3+abc123")
if (infoVersion != null && infoVersion.Contains('+'))
{
infoVersion = infoVersion[..infoVersion.IndexOf('+')];
}

loadedVersions[name.Name!] = infoVersion ?? name.Version?.ToString() ?? "unknown";
}
}
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

The version endpoint only reports assemblies that are already loaded into the AppDomain. If one of the expected DurableTask assemblies hasn’t been loaded yet, it will be silently omitted and the workflow’s version check won’t detect it. Consider explicitly loading/forcing references to the expected assemblies (e.g., via typeof(...) on a type from each package) and/or returning a result that includes all expected assembly names (failing/marking missing ones) so the pipeline can assert completeness, not just version equality for whatever happened to be loaded.

Copilot uses AI. Check for mistakes.
<!-- Azure Functions host packages (from nuget.org) -->
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.51.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" OutputItemType="Analyzer" />
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

For the Functions Worker SDK analyzer reference, it’s typical to set PrivateAssets=\"all\" (and, if needed, IncludeAssets) to ensure it never flows to consumers via transitive dependencies. This project isn’t packable, but adding PrivateAssets=\"all\" makes the intent explicit and prevents accidental propagation if project settings change later.

Suggested change
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" OutputItemType="Analyzer" PrivateAssets="all" />

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +60
foreach (string prefix in sdkAssemblyPrefixes)
{
if (string.Equals(name.Name, prefix, StringComparison.OrdinalIgnoreCase))
{
string? infoVersion = asm
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;

// Strip source-link commit hash suffix (e.g. "1.23.3+abc123")
if (infoVersion != null && infoVersion.Contains('+'))
{
infoVersion = infoVersion[..infoVersion.IndexOf('+')];
}

loadedVersions[name.Name!] = infoVersion ?? name.Version?.ToString() ?? "unknown";
}
}
@YunchuWang
Copy link
Copy Markdown
Member Author

Closing — durabletask-dotnet packages are transitive dependencies consumed through azure-functions-durable-extension and AAPT-DTMB. The dep validation is more valuable in those downstream repos. This repo already has azure-functions-smoke-tests.yml for code-level validation.

@YunchuWang YunchuWang closed this Apr 8, 2026
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