This repository packages the unmodified iOfficeAI/OfficeCLI
submodule for applications that run the CLI as a child process. The package ID is
OfficeCLI; binaries and their assembly versions retain the upstream identity.
It is a regular PackageReference, not a dotnet tool package.
The package contains lib/net11.0/officecli.dll, embedded upstream resources,
debug symbols in a separate .snupkg, and small SDK-generated apphosts for
win-x64, linux-x64, linux-arm64, osx-x64 and osx-arm64. It declares the
upstream project's PackageReferences instead of bundling third-party DLLs or a
second .NET runtime. The large upstream project is compiled only once per pack;
a tiny separate project generates platform apphosts that load officecli.dll.
The tiny project's managed stub is never packaged.
Upstream build outputs are redirected to this wrapper's artifacts directory;
the source submodule does not need packaging changes or a custom gitignore.
buildTransitive/OfficeCLI.targets selects the apphost from RuntimeIdentifier
(or the SDK host RID for RID-less builds), copies it into executable consumers,
and preserves Unix executable permissions. Class libraries do not get launchers.
The consumer must target .NET 11 or newer and generate its dependency and runtime
configuration files. Single-file and NativeAOT consumers are rejected.
OfficeCLI and the consumer share one output directory and dependency graph.
The targets copy the consumer's SDK-generated .deps.json and .runtimeconfig.json
to the OfficeCLI sidecar names. The dependency manifest intentionally keeps the
consumer's root library and includes the OfficeCLI package; the apphost loads
officecli.dll as its entry assembly. A publish uses the final dependency manifest
after trimming, not the build manifest. This preserves NuGet dependency overrides
and selects either the installed runtime for framework-dependent builds or the
adjacent runtime for self-contained publishes. Both processes inherit the same
runtime configuration. Moving the launcher by itself is unsupported.
The CLI assembly is rooted for trimming because the consumer launches it without static C# calls. Updating the consumer's other packages can still affect binary compatibility with upstream dependencies; run the document/resident smoke after such updates. An independent subdirectory containing another runtime or duplicate managed dependencies would defeat the intended sharing.
<PackageReference Include="OfficeCLI" Version="[999.YYYYMMDD.RUN_NUMBER]" />Launch the adjacent officecli.exe on Windows or officecli on Unix using
ProcessStartInfo. It must remain a real apphost: upstream uses
Environment.ProcessPath to spawn resident/watch processes. Set these variables
on the child process so embedded copies are managed by your application's release:
OFFICECLI_NO_AUTO_INSTALL=1
OFFICECLI_SKIP_UPDATE=1
These disable automatic behavior; explicit install/update commands are not
blocked by the packaging layer. Do not expose those commands as maintenance of
the embedded copy. macOS consumers that put managed output into a Bundle must
copy the apphost and both sidecars to the same managed directory, apply chmod +x,
and sign the launcher using their own application identity and JIT entitlements.
The Apple SDK disables dependency manifest generation by default, so macOS
consumers must explicitly enable GenerateDependencyFile. For custom Bundle
copy targets, use OfficeCliDepsFile and OfficeCliRuntimeConfig: the package
sets these to the current build files or the final post-trimming publish files.
The runtime JSON comes from ProjectRuntimeConfigFilePath: the Apple SDK keeps
that generated file but removes it from the publish list when using runtimeconfig.bin.
The package itself does not provide Developer ID signing or notarization.
Install .NET SDK 11.0.100 (or the version pinned in global.json) and PowerShell 7.
Initialize the source checkout with git submodule update --init.
pwsh -File scripts/pack.ps1
pwsh -File scripts/smoke.ps1 -RuntimeIdentifier win-x64Use your platform's supported RID for smoke. Packages appear under
artifacts/packages; command logs and exact upstream provenance are in
artifacts/logs and artifacts/source.json. 999.0.0 is a local draft only.
The smoke restores into a separate cache, exercises a dependency version override,
then runs RID-less framework-dependent build output and a trimmed self-contained
publish. It creates Word, Excel and PowerPoint documents and edits/reads a Word
document through upstream's resident child process.
The osx-arm64 CI job pins workload set 11.0.100.1 with Xcode 27.2 and publishes a
non-trimmed, self-contained net11.0-macos consumer as an Apple .app, matching
Everywhere's LinkMode=None policy. The fixture
uses hardened runtime and the same JIT entitlements and signing order as Everywhere:
native libraries, the OfficeCLI apphost, then the main app. CI uses ad-hoc signing;
Developer ID signing, notarization and installer packaging remain release responsibilities.
The smoke copies the signed app to a fresh directory, then verifies signatures,
starts the main executable, and runs all document/resident tests from both the
original and relocated Contents/MonoBundle directories.
It also checks that OfficeCLI uses the final dependency manifest and the
consumer's runtime configuration, with one shared copy of each tested dependency.
This exercises Apple's runtime configuration conversion and app layout, which a
regular net11.0 publish with an osx RID does not cover.
The Apple fixture owns a normal AppKit initialization entry point. A console-only
entry point with full trimming removed Microsoft.macOS.dll and made the native
runtime bridge abort before Main. The unchanged package passed the full-trimming
diagnostic after restoring an AppKit entry point; the supported Apple smoke uses
LinkMode=None to match Everywhere.
When changing LinkMode during local investigation, use fresh tests/MacConsumer/bin
and obj outputs. Reusing a Full-mode bundle for None retained 48 trimmed framework
assemblies alongside the full platform assembly and caused a different startup
failure. Ordinary repeated builds with the same policy remain incremental.
To run it locally with Xcode 27.2, first install the matching workload:
dotnet workload install macos --version 11.0.100.1Then add -AppleBundle to the smoke command. Pinning the .NET SDK alone does not
pin workload manifests. Update the workload set and Xcode together when Everywhere
changes its Apple toolchain. The other four RID jobs retain the ordinary .NET smoke.
- Create the
Sylinko/OfficeCLIGitHub repository and push this wrapper, including the upstream submodule's pinned commit. The submodule points to the upstream repository, not to a fork with packaging patches. - In
Sylinko/nuget-feed, registerOfficeCLIwith source repositorySylinko/OfficeCLIand allow the original package ID. Merge that registration before running publication. - Configure the wrapper repository's Actions secret
SYLINKO_NUGET_FEED_TOKEN. Follow the feed token configuration: the fine-grained token needs access toSylinko/nuget-feed, Metadata read, Contents read/write and Pull requests read/write. The producer's release is uploaded with its automaticGITHUB_TOKENandcontents: writepermission. - Run Publish NuGet manually on
main. It uses the workflow's original UTC creation date and CI run number to produce999.YYYYMMDD.RUN_NUMBER. The wrapper version is separate from upstreamVersion/AssemblyVersion. The package is built once, tested on all five supported RIDs, then published throughSylinko/nuget-feed@v1. This creates a GitHub Release and a feed PR. - Review/merge the feed PR, let the feed deploy, and pin the actual package version in Everywhere's central package management. Publication retries follow the shared action's current behavior; do not overwrite an already consumed version.
To update upstream, fetch a reviewed release tag/commit in 3rd/OfficeCLI, check
it out, commit the new submodule pointer, and run publication again. Read
artifacts/source.json to identify the upstream version and commit in each run.
There is no scheduled update or automatic unreviewed upgrade.
Upstream OfficeCLI is Apache-2.0. Every package contains the original LICENSE
and THIRD-PARTY-NOTICES.txt. Wrapper scripts and integration files are distributed
under the same license; the repository's LICENSE is copied from upstream.