diff --git a/.pipelines/CosmosDB-Shell-Official.yml b/.pipelines/CosmosDB-Shell-Official.yml
index 3432ffc3..42dfa064 100644
--- a/.pipelines/CosmosDB-Shell-Official.yml
+++ b/.pipelines/CosmosDB-Shell-Official.yml
@@ -31,6 +31,10 @@ variables:
BuildConfiguration: Release
WindowsContainerImage: "onebranch.azurecr.io/windows/ltsc2022/vse2022:latest" # Docker image which is used to build the project https://aka.ms/obpipelines/containers
+ # Azure Linux is RPM-native and resolves packages from packages.microsoft.com.
+ # The build containers cannot reach the public Ubuntu archives, so an
+ # Ubuntu image cannot install the RPM tooling the rpm job needs.
+ LinuxContainerImage: "mcr.microsoft.com/onebranch/azurelinux/build:3.0"
resources:
repositories:
@@ -822,6 +826,140 @@ extends:
publishVstsFeed: "CosmosDB/CosmosDBShell"
allowPackageConflicts: true
+ - job: rpm
+ displayName: Build lightweight RPM packages
+ pool:
+ type: linux
+ variables:
+ ob_outputDirectory: "$(Build.SourcesDirectory)/out"
+ ob_artifactBaseName: cosmos_shell_lite_rpm
+ ob_git_fetchDepth: -1
+ steps:
+ - task: UseDotNet@2
+ inputs:
+ packageType: "sdk"
+ useGlobalJson: true
+ performMultiLevelLookup: true
+
+ - script: |
+ set -euo pipefail
+ dotnet tool restore --configfile "$(Build.SourcesDirectory)/.pipelines/nuget.config"
+ package_version="$(dotnet tool run nbgv get-version -v NuGetPackageVersion)"
+ package_version="${package_version%%+*}"
+ if [[ ! "$package_version" =~ ^([0-9]+\.[0-9]+\.[0-9]+)(-(.+))?$ ]]; then
+ echo "Unsupported RPM package version: $package_version" >&2
+ exit 1
+ fi
+
+ echo "##vso[task.setvariable variable=CosmosDBShell_RpmVersion]${BASH_REMATCH[1]}"
+ if [[ -n "${BASH_REMATCH[3]:-}" ]]; then
+ rpm_release="0.${BASH_REMATCH[3]//[^[:alnum:].]/.}"
+ else
+ rpm_release="1"
+ fi
+ echo "##vso[task.setvariable variable=CosmosDBShell_RpmRelease]$rpm_release"
+ displayName: Compute RPM version
+
+ - script: |
+ set -euo pipefail
+ if ! command -v rpmbuild >/dev/null 2>&1; then
+ if ! command -v tdnf >/dev/null 2>&1; then
+ echo "rpmbuild is unavailable and tdnf is not installed; run this job in the configured Azure Linux container." >&2
+ exit 1
+ fi
+ tdnf install -y rpm-build
+ fi
+
+ rpm_root="$(Build.SourcesDirectory)/.rpmbuild"
+ output_dir="$(Build.SourcesDirectory)/out/rpm"
+ mkdir -p "$rpm_root"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} "$output_dir"
+ cp "$(Build.SourcesDirectory)/packaging/rpm/cosmosdbshell-lite.spec" "$rpm_root/SPECS/"
+ cp "$(Build.SourcesDirectory)/LICENSE.md" "$(Build.SourcesDirectory)/NOTICE.html" "$rpm_root/SOURCES/"
+
+ for entry in "linux-x64:x86_64" "linux-arm64:aarch64"; do
+ rid="${entry%%:*}"
+ rpm_arch="${entry##*:}"
+ publish_dir="$(Build.SourcesDirectory)/.rpm-publish/$rid"
+
+ rm -rf "$publish_dir"
+ dotnet publish "$(Build.SourcesDirectory)/CosmosDBShell/CosmosDBShell.csproj" \
+ --configuration "$(BuildConfiguration)" \
+ --runtime "$rid" \
+ --self-contained false \
+ -p:PublishSingleFile=false \
+ -p:PackAsTool=false \
+ -p:CosmosDBShellExcludeMsalRuntime=true \
+ -p:CosmosDBShellExcludeMcpAndLsp=true \
+ --output "$publish_dir" \
+ --configfile "$(Build.SourcesDirectory)/.pipelines/nuget.config"
+
+ # Ships for linux-x64 only and doubles the package size. The VS Code
+ # broker credential falls back when it is absent, as it already does
+ # on linux-arm64, where this library does not exist at all.
+ rm -f "$publish_dir/libmsalruntime.so"
+
+ for required in CosmosDBShell CosmosDBShell.dll; do
+ if [[ ! -f "$publish_dir/$required" ]]; then
+ echo "Expected $required in $publish_dir." >&2
+ exit 1
+ fi
+ done
+
+ set +e
+ "$publish_dir/CosmosDBShell" --output json --lsp >lsp.stdout 2>lsp.stderr
+ lsp_exit=$?
+ set -e
+ if [[ $lsp_exit -ne 2 || -s lsp.stdout ]]; then
+ echo "Lightweight --lsp smoke test did not return a machine-mode usage error." >&2
+ exit 1
+ fi
+ grep -F '"status":"error"' lsp.stderr
+ grep -F '"error":"LSP support is not included in this build."' lsp.stderr
+ rm -f lsp.stdout lsp.stderr
+
+ tar -czf "$rpm_root/SOURCES/cosmosdbshell-lite-payload.tar.gz" -C "$publish_dir" .
+ rpmbuild -bb "$rpm_root/SPECS/cosmosdbshell-lite.spec" \
+ --target "$rpm_arch" \
+ --define "_topdir $rpm_root" \
+ --define "package_version $(CosmosDBShell_RpmVersion)" \
+ --define "package_release $(CosmosDBShell_RpmRelease)"
+ done
+
+ find "$rpm_root/RPMS" -type f -name 'cosmosdbshell-lite-*.rpm' -exec cp {} "$output_dir/" \;
+ displayName: Build lightweight framework-dependent RPMs
+
+ - script: |
+ set -euo pipefail
+ output_dir="$(Build.SourcesDirectory)/out/rpm"
+ mapfile -t packages < <(find "$output_dir" -maxdepth 1 -type f -name 'cosmosdbshell-lite-*.rpm' | sort)
+ if [[ ${#packages[@]} -ne 2 ]]; then
+ echo "Expected two RPM packages; found ${#packages[@]}." >&2
+ exit 1
+ fi
+
+ for package in "${packages[@]}"; do
+ size="$(stat -c %s "$package")"
+ if (( size > 25000000 )); then
+ echo "$(basename "$package") is $size bytes and exceeds the 25 MB Cloud Shell limit." >&2
+ exit 1
+ fi
+ rpm -qp --queryformat '%{NAME}\n' "$package" | grep -Fx 'cosmosdbshell-lite'
+ rpm -qpR "$package" | grep -Fx 'dotnet-runtime-10.0 >= 10.0'
+ rpm -qlp "$package" | grep -Fx '/usr/bin/cosmosdbshell'
+ rpm -qlp "$package" | grep -Fx '/usr/libexec/cosmosdbshell/CosmosDBShell'
+ rpm -qlp "$package" | grep -Fx '/usr/libexec/cosmosdbshell/CosmosDBShell.dll'
+ if rpm -qlp "$package" | grep -q 'libmsalruntime\.so'; then
+ echo "$(basename "$package") still carries libmsalruntime.so." >&2
+ exit 1
+ fi
+ if rpm -qlp "$package" | grep -Eq '/(ModelContextProtocol|OmniSharp|Azure\.Identity\.Broker|Microsoft\.Identity\.Client\.Broker).*\.dll$'; then
+ echo "$(basename "$package") unexpectedly contains an excluded feature assembly." >&2
+ exit 1
+ fi
+ echo "Validated $(basename "$package") [$size bytes]"
+ done
+ displayName: Validate lightweight RPM packages
+
- job: CodeQLAnalyze
displayName: CodeQL (C#)
pool:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d25a26a4..b0025d7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## Unreleased
+### Build & pipeline
+
+- The official pipeline now produces compressed `cosmosdbshell-lite` RPM packages for `x86_64` and `aarch64` from lightweight framework-dependent Linux builds. The packages retain the interactive shell, scripting, ARM management, direct/gateway connectivity, and OpenTelemetry export while excluding MCP, LSP, and brokered Visual Studio Code authentication. They require the .NET 10 runtime and are validated against the Azure Cloud Shell 25 MB package-size limit.
### Improvements
- Cosmos DB data-plane commands now consistently expose their aggregate observed request charge in structured output and connection-scoped `info` telemetry, including metadata/configuration operations, scripts, change feed reads, paginated operations, handled probes, and charged failures. Azure Resource Manager control-plane operations remain uncharged.
diff --git a/CosmosDBShell.Tests/CommandTests/ConnectCommandTests.cs b/CosmosDBShell.Tests/CommandTests/ConnectCommandTests.cs
index 717c7847..87059dc1 100644
--- a/CosmosDBShell.Tests/CommandTests/ConnectCommandTests.cs
+++ b/CosmosDBShell.Tests/CommandTests/ConnectCommandTests.cs
@@ -16,6 +16,16 @@ namespace CosmosShell.Tests.CommandTests;
[Collection(CosmosShell.Tests.Shell.ThemeStateTestCollection.Name)]
public class ConnectCommandTests
{
+ [Fact]
+ public void VSCodeCredential_SupportMatchesBuildCapability()
+ {
+#if COSMOSDBSHELL_NO_MSAL_RUNTIME
+ Assert.False(ShellInterpreter.IsVSCodeCredentialSupported);
+#else
+ Assert.True(ShellInterpreter.IsVSCodeCredentialSupported);
+#endif
+ }
+
[Fact]
public void CreateClientOptions_Emulator_UsesShortRequestTimeout()
{
diff --git a/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.Highlighter.cs b/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.Highlighter.cs
index 58c551fe..53ba1405 100644
--- a/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.Highlighter.cs
+++ b/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.Highlighter.cs
@@ -7,7 +7,6 @@ namespace Azure.Data.Cosmos.Shell.Core;
using System.Linq;
using System.Text;
using Azure.Data.Cosmos.Shell.Parser;
-using Microsoft.AspNetCore.Http.Metadata;
using RadLine;
using Spectre.Console;
using Spectre.Console.Rendering;
diff --git a/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs b/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs
index b46d1927..d5507bfe 100644
--- a/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs
+++ b/CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs
@@ -130,6 +130,18 @@ internal ShellInterpreter(string? configPath = null)
///
public bool Echo { get; set; } = true;
+ internal static bool IsVSCodeCredentialSupported
+ {
+ get
+ {
+#if COSMOSDBSHELL_NO_MSAL_RUNTIME
+ return false;
+#else
+ return true;
+#endif
+ }
+ }
+
internal static CancellationTokenSource UserCancellationTokenSource
{
get
@@ -1174,27 +1186,36 @@ internal async Task ConnectAsync(string connectionString, string? loginHint = nu
// Step 2: VisualStudioCodeCredential (when launched from VS Code extension)
if (client == null && credentialMethod == CredentialMethod.VSCode)
{
- WriteLine(MessageService.GetString("shell-connect-vscode-credential-auth"));
-
- var vscOptions = new VisualStudioCodeCredentialOptions();
- if (!string.IsNullOrWhiteSpace(tenantId))
+ if (!IsVSCodeCredentialSupported)
{
- vscOptions.TenantId = tenantId;
+ WriteLine(MessageService.GetString("shell-connect-vscode-credential-msal-runtime-missing"));
}
-
- if (authorityHostUri != null)
+#if !COSMOSDBSHELL_NO_MSAL_RUNTIME
+ else
{
- vscOptions.AuthorityHost = authorityHostUri;
- }
+ WriteLine(MessageService.GetString("shell-connect-vscode-credential-auth"));
- var vscCredential = new VisualStudioCodeCredential(vscOptions);
- if (await this.TryConnectWithTokenCredentialAsync(tokenEndpoint, vscCredential, options, subscriptionId, resourceGroupName, authorityHostUri, allowCredentialFallback: true, token))
- {
- return;
- }
+ var vscOptions = new VisualStudioCodeCredentialOptions();
+ if (!string.IsNullOrWhiteSpace(tenantId))
+ {
+ vscOptions.TenantId = tenantId;
+ }
+
+ if (authorityHostUri != null)
+ {
+ vscOptions.AuthorityHost = authorityHostUri;
+ }
- // VS Code credential unavailable or expired; continue the credential chain.
- WriteLine(MessageService.GetString("shell-connect-vscode-credential-fallback"));
+ var vscCredential = new VisualStudioCodeCredential(vscOptions);
+ if (await this.TryConnectWithTokenCredentialAsync(tokenEndpoint, vscCredential, options, subscriptionId, resourceGroupName, authorityHostUri, allowCredentialFallback: true, token))
+ {
+ return;
+ }
+
+ // VS Code credential unavailable or expired; continue the credential chain.
+ WriteLine(MessageService.GetString("shell-connect-vscode-credential-fallback"));
+ }
+#endif
}
// Step 3: Static token from COSMOSDB_SHELL_TOKEN environment variable
diff --git a/CosmosDBShell/CosmosDBShell.csproj b/CosmosDBShell/CosmosDBShell.csproj
index 50d17844..a5ebb388 100644
--- a/CosmosDBShell/CosmosDBShell.csproj
+++ b/CosmosDBShell/CosmosDBShell.csproj
@@ -74,9 +74,9 @@
-
-
-
+
+
+
@@ -86,16 +86,17 @@
-
+
-
-
-
-
+
+
+
+
+
@@ -108,6 +109,13 @@
+
+
+
+
+
+
+
diff --git a/CosmosDBShell/Program.cs b/CosmosDBShell/Program.cs
index 4c11ce16..868a0b4d 100644
--- a/CosmosDBShell/Program.cs
+++ b/CosmosDBShell/Program.cs
@@ -7,11 +7,15 @@
using System.Reflection;
using Azure.Data.Cosmos.Shell.Commands;
using Azure.Data.Cosmos.Shell.Core;
+#if !COSMOSDBSHELL_NO_MCP_LSP
using Azure.Data.Cosmos.Shell.Lsp;
using Azure.Data.Cosmos.Shell.Mcp;
+#endif
using Azure.Data.Cosmos.Shell.Util;
using Microsoft.Azure.Cosmos;
+#if !COSMOSDBSHELL_NO_MCP_LSP
using Microsoft.Extensions.Hosting;
+#endif
using Spectre.Console;
internal class Program
@@ -25,6 +29,7 @@ public static async Task Main(string[] args)
// accidentally trigger LSP mode.
args = NormalizeArguments(args);
+#if !COSMOSDBSHELL_NO_MCP_LSP
// Handle LSP mode early, before any other code can write to stdout.
// The LSP protocol requires exclusive access to stdin/stdout. Only
// inspect the prefix before -c / -k so that a command tail of literally
@@ -37,8 +42,9 @@ public static async Task Main(string[] args)
await server.WaitForExit;
return;
}
+#endif
- IHost? host = null;
+ IDisposable? host = null;
TracingBootstrap? tracing = null;
try
{
@@ -181,14 +187,6 @@ public static async Task Main(string[] args)
}
}
- if (o.StartLspServer)
- {
- // Already handled above, but keep for completeness
- var server = await LspServer.CreateLanguageServerAsync();
- await server.WaitForExit;
- return;
- }
-
if (!string.IsNullOrWhiteSpace(o.ExecuteAndQuit) && !string.IsNullOrWhiteSpace(o.ExecuteAndContinue))
{
Environment.ExitCode = ShellExitCode.UsageError;
@@ -240,6 +238,20 @@ void WriteStartupError(string message)
AnsiConsole.WriteLine(message);
}
+ if (o.StartLspServer || o.LspStdio)
+ {
+#if COSMOSDBSHELL_NO_MCP_LSP
+ WriteStartupError(MessageService.GetString("error-lsp-not-included"));
+ Environment.ExitCode = ShellExitCode.UsageError;
+ return;
+#else
+ // Already handled above, but keep for completeness
+ var server = await LspServer.CreateLanguageServerAsync();
+ await server.WaitForExit;
+ return;
+#endif
+ }
+
if (o.Container != null && o.Database == null)
{
WriteStartupError(MessageService.GetString("error-startup-container-requires-database"));
@@ -399,6 +411,11 @@ await ShellInterpreter.Instance.ConnectAsync(
if (o.McpPort is int mcpPort)
{
+#if COSMOSDBSHELL_NO_MCP_LSP
+ WriteStartupError(MessageService.GetString("error-mcp-not-included"));
+ Environment.ExitCode = ShellExitCode.UsageError;
+ return;
+#else
if (mcpPort <= 0)
{
WriteStartupError(MessageService.GetString("mcp-error-invalid-port"));
@@ -417,7 +434,7 @@ await ShellInterpreter.Instance.ConnectAsync(
return;
}
- if (host != null)
+ if (host is IHost mcpHost)
{
ShellInterpreter.Instance.McpPort = mcpPort;
hostTask = Task.Run(async () =>
@@ -425,8 +442,8 @@ await ShellInterpreter.Instance.ConnectAsync(
try
{
var token = CancellationToken.None;
- await host.StartAsync(token);
- await host.WaitForShutdownAsync(token);
+ await mcpHost.StartAsync(token);
+ await mcpHost.WaitForShutdownAsync(token);
}
catch (Exception ex)
{
@@ -435,6 +452,7 @@ await ShellInterpreter.Instance.ConnectAsync(
}
});
}
+#endif
}
if (Console.IsInputRedirected)
@@ -516,12 +534,14 @@ private static void WriteVersionHeading()
ShellInterpreter.WriteLine(heading);
}
- private static async Task StopHostAsync(IHost? host, Task? hostTask)
+ private static async Task StopHostAsync(IDisposable? host, Task? hostTask)
{
- if (host != null)
+#if !COSMOSDBSHELL_NO_MCP_LSP
+ if (host is IHost mcpHost)
{
- await host.StopAsync();
+ await mcpHost.StopAsync();
}
+#endif
if (hostTask != null)
{
diff --git a/CosmosDBShell/lang/en.ftl b/CosmosDBShell/lang/en.ftl
index 76af81c7..4d10eae9 100644
--- a/CosmosDBShell/lang/en.ftl
+++ b/CosmosDBShell/lang/en.ftl
@@ -17,6 +17,7 @@ shell-connect-static-token-auth = Connecting with externally provided access tok
shell-connect-static-token-expiry = Expires in { $timespan } (expiration: { $expiration }).
shell-connect-vscode-credential-auth = Connecting with Visual Studio Code credential...
shell-connect-vscode-credential-fallback = Visual Studio Code credential unavailable, falling back...
+shell-connect-vscode-credential-msal-runtime-missing = WARNING: Visual Studio Code credential is unavailable because this build excludes the native MSAL runtime. Falling back to other credentials; use --azure-cli to select the Azure CLI identity explicitly.
shell-connect-devicecode-fallback = Browser authentication failed, falling back to device code authentication...
shell-connect-arm-discovery-failed = Using Cosmos DB data plane.
shell-connect-arm-discovery-ambiguous = Multiple ARM Cosmos DB accounts match the connected endpoint. Reconnect with --subscription and --resource-group, or use --connect-subscription and --connect-resource-group at startup, to specify which account to use. Using Cosmos DB data plane for now.
@@ -31,6 +32,8 @@ yes_char = Y
no_char = N
error = Error:
+error-lsp-not-included = LSP support is not included in this build.
+error-mcp-not-included = MCP support is not included in this build.
error-connection_failed = Failed to connect to the Cosmos DB account.
error-emulator_connection_failed =
Could not reach the Cosmos DB emulator at { $endpoint }.
diff --git a/Directory.Build.props b/Directory.Build.props
index 8dcb9c5e..d2dc1ddf 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,4 +15,10 @@
+
+ $(DefineConstants);COSMOSDBSHELL_NO_MSAL_RUNTIME
+
+
+ $(DefineConstants);COSMOSDBSHELL_NO_MCP_LSP
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index db1ee944..1f6cd656 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,6 +13,7 @@
+
diff --git a/README.md b/README.md
index 2b9a5f7a..8405ab59 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,45 @@ Run the tests:
dotnet test CosmosDBShell.sln
```
+## Install from Lightweight RPM Artifacts
+
+The official pipeline produces framework-dependent `cosmosdbshell-lite` RPMs
+for internal Azure Cloud Shell deployments and controlled evaluation environments:
+
+- `cosmosdbshell-lite--.x86_64.rpm`
+- `cosmosdbshell-lite--.aarch64.rpm`
+
+These artifacts are intended for internal Azure Cloud Shell deployment and
+controlled environments. They are not currently a supported general-purpose
+Linux distribution channel; installation on other RPM-based distributions is
+for evaluation in environments that provide the required runtime.
+
+The RPM requires the .NET 10 runtime package (`dotnet-runtime-10.0`), but does
+not require the .NET SDK. On Azure Linux, install the package that matches the
+host architecture with `tdnf`:
+
+```bash
+sudo tdnf install ./cosmosdbshell-lite--..rpm
+cosmosdbshell
+```
+
+On other RPM-based distributions, use `dnf`:
+
+```bash
+sudo dnf install ./cosmosdbshell-lite--..rpm
+cosmosdbshell
+```
+
+The RPM retains the interactive shell, scripting, ARM management, and both
+direct and gateway Cosmos DB connectivity. To reduce its deployment footprint,
+it excludes MCP, LSP, and the native and managed broker components used by
+Visual Studio Code credential authentication. Invoking `--mcp`, `--lsp`, or
+`--stdio` reports that the feature is unavailable. OpenTelemetry tracing and
+OTLP export remain available through `--otel`.
+Selecting Visual Studio Code credential authentication prints a warning and
+falls back to other credentials. Use `--azure-cli` to select the signed-in Azure
+CLI identity explicitly.
+
## Architecture
| Folder | Purpose |
diff --git a/packaging/rpm/cosmosdbshell-lite.spec b/packaging/rpm/cosmosdbshell-lite.spec
new file mode 100644
index 00000000..fc6d1407
--- /dev/null
+++ b/packaging/rpm/cosmosdbshell-lite.spec
@@ -0,0 +1,48 @@
+Name: cosmosdbshell-lite
+Version: %{package_version}
+Release: %{package_release}%{?dist}
+Summary: Lightweight interactive shell for Azure Cosmos DB
+License: MIT
+URL: https://github.com/Azure/CosmosDBShell
+Source0: cosmosdbshell-lite-payload.tar.gz
+Source1: LICENSE.md
+Source2: NOTICE.html
+Requires: dotnet-runtime-10.0 >= 10.0
+# The payload is prebuilt and only needs the .NET runtime, so skip the ELF scan
+# that would otherwise derive dependencies from the build host.
+AutoReqProv: no
+
+%global _binary_payload w19.zstdio
+%{!?_licensedir: %global _licensedir %{_datadir}/licenses}
+
+# Prebuilt binaries are shipped as published; stripping them breaks the .NET host.
+%global debug_package %{nil}
+%global __os_install_post %{nil}
+
+%description
+Azure Cosmos DB Shell is a command-line tool for interactive navigation,
+queries, and scripting with Azure Cosmos DB. This lightweight build excludes
+MCP, LSP, and brokered Visual Studio Code authentication.
+
+%prep
+
+%build
+
+%install
+mkdir -p %{buildroot}%{_libexecdir}/cosmosdbshell
+tar -xzf %{SOURCE0} -C %{buildroot}%{_libexecdir}/cosmosdbshell
+chmod 0755 %{buildroot}%{_libexecdir}/cosmosdbshell/CosmosDBShell
+install -D -m 0644 %{SOURCE1} %{buildroot}%{_licensedir}/%{name}/LICENSE.md
+install -D -m 0644 %{SOURCE2} %{buildroot}%{_licensedir}/%{name}/NOTICE.html
+mkdir -p %{buildroot}%{_bindir}
+ln -s %{_libexecdir}/cosmosdbshell/CosmosDBShell %{buildroot}%{_bindir}/cosmosdbshell
+
+%files
+%{_bindir}/cosmosdbshell
+%{_libexecdir}/cosmosdbshell
+%license %{_licensedir}/%{name}/LICENSE.md
+%license %{_licensedir}/%{name}/NOTICE.html
+
+%changelog
+* Thu Aug 27 2026 Microsoft Corporation - %{package_version}-%{package_release}
+- Build from the framework-dependent .NET 10 publish output.
\ No newline at end of file