chore: import upstream snapshot with attribution
Publish SDK (PyPI) / publish (push) Has been cancelled
Publish SDK (npm) / publish (@aionui/officecli-sdk) (push) Has been cancelled
SDK smoke / smoke (windows-latest) (push) Has been cancelled
Publish SDK (npm) / publish (@officecli/officecli-sdk) (push) Has been cancelled
Publish SDK (npm) / publish (@officecli/sdk) (push) Has been cancelled
Publish SDK (npm) / publish (officecli-sdk) (push) Has been cancelled
SDK smoke / smoke (macos-latest) (push) Has been cancelled
SDK smoke / smoke (ubuntu-latest) (push) Has been cancelled
Skill parity / diff (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:09:29 +08:00
commit 8cb1f9f479
1189 changed files with 386007 additions and 0 deletions
+235
View File
@@ -0,0 +1,235 @@
name: Build
on:
workflow_dispatch:
push:
tags:
- 'v*'
permissions:
contents: read
jobs:
build:
strategy:
matrix:
include:
- rid: osx-arm64
name: officecli-mac-arm64
os: macos-latest
- rid: osx-x64
name: officecli-mac-x64
os: macos-latest
- rid: linux-x64
name: officecli-linux-x64
os: ubuntu-latest
- rid: linux-arm64
name: officecli-linux-arm64
os: ubuntu-latest
- rid: linux-musl-x64
name: officecli-linux-alpine-x64
os: ubuntu-latest
- rid: linux-musl-arm64
name: officecli-linux-alpine-arm64
os: ubuntu-latest
- rid: win-x64
name: officecli-win-x64.exe
os: windows-latest
- rid: win-arm64
name: officecli-win-arm64.exe
os: windows-latest
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- name: Publish
run: dotnet publish src/officecli/officecli.csproj -c Release -r ${{ matrix.rid }} -o publish --nologo
- name: Rename output
run: |
if [ -f publish/officecli.exe ]; then
mv publish/officecli.exe publish/${{ matrix.name }}
else
mv publish/officecli publish/${{ matrix.name }}
fi
- name: Setup macOS code signing
if: startsWith(matrix.rid, 'osx-')
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
CERT_PATH="$RUNNER_TEMP/build_certificate.p12"
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode > "$CERT_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERT_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple:,codesign: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychain -d user -s "$KEYCHAIN_PATH" login.keychain-db
rm -f "$CERT_PATH"
- name: Codesign (macOS)
if: startsWith(matrix.rid, 'osx-')
run: |
IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -n 1 | sed -E 's/.*"(.+)"/\1/')
echo "Signing with: $IDENTITY"
# --options runtime (Hardened Runtime, required by notarization) denies the
# self-contained CoreCLR's MAP_JIT executable memory by default, so the runtime
# fails to start with "Failed to create CoreCLR, HRESULT: 0x80070008" on every
# command. build/officecli.entitlements re-permits exactly allow-jit (the plist
# holds no XML comments — codesign's AMFI parser rejects them).
codesign --force --options runtime --entitlements build/officecli.entitlements --timestamp --sign "$IDENTITY" publish/${{ matrix.name }}
codesign --verify --strict --verbose=2 publish/${{ matrix.name }}
# Fail the build if allow-jit did not actually embed (e.g. a malformed plist
# makes codesign silently drop entitlements) — otherwise the bug ships again.
codesign -d --entitlements - --xml publish/${{ matrix.name }} | grep -q allow-jit
- name: Notarize (macOS)
if: startsWith(matrix.rid, 'osx-')
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: |
# A bare Mach-O binary cannot be stapled; notarytool requires a
# zip/pkg/dmg container. Submit a zip — the notarization ticket is
# recorded on Apple's servers and Gatekeeper validates online.
ditto -c -k --keepParent publish/${{ matrix.name }} "$RUNNER_TEMP/notarize.zip"
xcrun notarytool submit "$RUNNER_TEMP/notarize.zip" \
--apple-id "$APPLE_ID" \
--team-id "$TEAM_ID" \
--password "$APP_SPECIFIC_PASSWORD" \
--wait
rm -f "$RUNNER_TEMP/notarize.zip"
- name: Smoke test - create document
if: >-
(matrix.rid == 'osx-arm64' && runner.arch == 'ARM64') ||
(matrix.rid == 'osx-x64' && runner.arch == 'X64') ||
(matrix.rid == 'linux-x64' && runner.os == 'Linux') ||
(matrix.rid == 'win-x64' && runner.os == 'Windows')
env:
# Disable Git Bash (MSYS) POSIX-to-Windows path conversion on
# windows-latest, which otherwise mangles `/body` into
# `C:/Program Files/Git/body` before it reaches the CLI.
MSYS_NO_PATHCONV: '1'
MSYS2_ARG_CONV_EXCL: '*'
run: |
chmod +x publish/${{ matrix.name }}
publish/${{ matrix.name }} create test_smoke.docx
publish/${{ matrix.name }} add test_smoke.docx /body --type paragraph --prop text="Hello from CI"
publish/${{ matrix.name }} get test_smoke.docx '/body/p[1]'
publish/${{ matrix.name }} close test_smoke.docx
rm -f test_smoke.docx
- name: Smoke test - .NET 8-only runtime (linux-x64)
if: matrix.rid == 'linux-x64' && runner.os == 'Linux'
run: |
chmod +x publish/${{ matrix.name }}
mkdir -p smoke_net8
docker run --rm \
-v "$PWD/publish:/app:ro" \
-v "$PWD/smoke_net8:/work" \
-w /work \
mcr.microsoft.com/dotnet/runtime:8.0 \
bash -c "set -e; \
/app/${{ matrix.name }} create issue115.docx; \
/app/${{ matrix.name }} add issue115.docx /body --type paragraph --prop text='net8 smoke'; \
/app/${{ matrix.name }} close issue115.docx; \
/app/${{ matrix.name }} view issue115.docx text --json"
- name: Smoke test - install
if: >-
(matrix.rid == 'osx-arm64' && runner.arch == 'ARM64') ||
(matrix.rid == 'osx-x64' && runner.arch == 'X64') ||
(matrix.rid == 'linux-x64' && runner.os == 'Linux') ||
(matrix.rid == 'win-x64' && runner.os == 'Windows')
env:
MSYS_NO_PATHCONV: '1'
MSYS2_ARG_CONV_EXCL: '*'
run: |
publish/${{ matrix.name }} install
if [ "$RUNNER_OS" == "Windows" ]; then
test -f "$LOCALAPPDATA/OfficeCLI/officecli.exe" || { echo "FAIL: officecli.exe not found in %LOCALAPPDATA%\\OfficeCLI"; exit 1; }
"$LOCALAPPDATA/OfficeCLI/officecli.exe" --version
else
test -f "$HOME/.local/bin/officecli" || { echo "FAIL: officecli not found in ~/.local/bin"; exit 1; }
"$HOME/.local/bin/officecli" --version
fi
- name: Smoke test - install.sh / install.ps1
# Exercises the shell installers themselves (separate from the CLI's
# own `install` subcommand tested above). Downloads from
# d.officecli.ai with github fallback — validates the production
# mirror is reachable AND the in-repo script is syntactically and
# logically correct on each platform.
if: >-
(matrix.rid == 'osx-arm64' && runner.arch == 'ARM64') ||
(matrix.rid == 'osx-x64' && runner.arch == 'X64') ||
(matrix.rid == 'linux-x64' && runner.os == 'Linux') ||
(matrix.rid == 'win-x64' && runner.os == 'Windows')
shell: bash
env:
MSYS_NO_PATHCONV: '1'
MSYS2_ARG_CONV_EXCL: '*'
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -NoProfile -ExecutionPolicy Bypass -File ./install.ps1
test -f "$LOCALAPPDATA/OfficeCLI/officecli.exe" || { echo "FAIL: officecli.exe not installed"; exit 1; }
"$LOCALAPPDATA/OfficeCLI/officecli.exe" --version
else
bash install.sh
test -f "$HOME/.local/bin/officecli" || { echo "FAIL: officecli not installed at ~/.local/bin"; exit 1; }
"$HOME/.local/bin/officecli" --version
fi
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.name }}
path: publish/${{ matrix.name }}
release:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Flatten artifacts and generate checksums
run: |
mkdir -p flat
find artifacts -type f -exec mv {} flat/ \;
rm -rf artifacts
mv flat artifacts
cd artifacts
sha256sum officecli-* > SHA256SUMS
echo "=== SHA256SUMS ==="
cat SHA256SUMS
- name: Create Draft Release
uses: softprops/action-gh-release@v3
with:
files: artifacts/**/*
generate_release_notes: true
draft: true
+95
View File
@@ -0,0 +1,95 @@
name: Publish npm
# Fires when a GitHub Release is PUBLISHED (not on the draft created by
# build.yml). By then the release assets — the platform binaries and
# SHA256SUMS the npm postinstall downloads — are publicly live, so a user who
# `npm install`s immediately can fetch them.
#
# Auth: npm Trusted Publishing (OIDC). No NPM_TOKEN / secret required — the
# `id-token: write` permission below lets the runner mint a short-lived token.
# Configure the trusted publisher for BOTH packages on npmjs.com:
# org/user: iOfficeAI repo: OfficeCLI workflow: publish-npm.yml action: npm publish
on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version to publish (X.Y.Z, or X.Y.Z-pre for a test build). Required for manual runs.'
required: false
dist_tag:
description: 'npm dist-tag (default: latest). Use e.g. "test" for a prerelease that must not move latest.'
required: false
default: 'latest'
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC token for npm trusted publishing
contents: read
strategy:
fail-fast: false
matrix:
package:
- '@officecli/officecli'
- '@aionui/officecli'
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Upgrade npm (trusted publishing + provenance need >= 11.5.1)
# Pinned to 11.x: npm 12.0.0 ships a broken provenance path
# ("Cannot find module 'sigstore'" in libnpmpublish). Unpin once fixed.
run: npm install -g npm@11
- name: Resolve version
id: ver
run: |
if [ "${{ github.event_name }}" = "release" ]; then
V="${{ github.event.release.tag_name }}"
else
V="${{ github.event.inputs.version }}"
fi
V="${V#v}"
if ! printf '%s' "$V" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$'; then
echo "Invalid or missing version: '$V'"
exit 1
fi
echo "version=$V" >> "$GITHUB_OUTPUT"
echo "Publishing ${{ matrix.package }}@$V"
- name: Set package name, version, and match the README to this package
id: pkg
working-directory: npm
run: |
npm pkg set name='${{ matrix.package }}'
npm pkg set version='${{ steps.ver.outputs.version }}'
# Only the package name differs per scope; links stay officecli.ai.
# Rewrite the install/npx commands to this package's name. No-op for
# @officecli/officecli.
sed -i "s|@officecli/officecli|${{ matrix.package }}|g" README.md
# A brand-new package can't be created by trusted publishing (OIDC) —
# skip (green) until its one-time manual first publish creates it.
if npm view '${{ matrix.package }}' version >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "::notice::${{ matrix.package }} does not exist on npm yet — first publish must be manual; skipping."
fi
- name: Publish to npm
if: steps.pkg.outputs.exists == 'true'
working-directory: npm
run: |
TAG='${{ github.event.inputs.dist_tag }}'
[ -z "$TAG" ] && TAG='latest'
npm publish --provenance --access public --tag "$TAG"
+63
View File
@@ -0,0 +1,63 @@
name: Publish SDK (PyPI)
# Publishes officecli-sdk (sdk/python) to PyPI on its OWN cadence — same model as
# publish-sdk.yml for npm. Triggers on a push touching sdk/python/** (or manual
# dispatch); a guard skips when that version is already on PyPI, so only a
# version bump in pyproject.toml actually publishes. A CLI release does NOT
# republish the SDK.
#
# Auth: PyPI Trusted Publishing (OIDC), no token / no API key. Configure the
# trusted publisher for the officecli-sdk project on pypi.org:
# owner: iOfficeAI repo: OfficeCLI workflow: publish-pypi.yml
# (officecli-sdk already exists on PyPI, so the publisher can be added directly.)
on:
push:
branches: [main]
paths:
- 'sdk/python/**'
workflow_dispatch:
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC token for PyPI trusted publishing
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Resolve version and check if already on PyPI
id: ver
working-directory: sdk/python
run: |
NAME=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['name'])")
VERSION=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
echo "Publishing candidate: $NAME==$VERSION"
CODE=$(curl -s -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/$NAME/$VERSION/json")
if [ "$CODE" = "200" ]; then
echo "already=true" >> "$GITHUB_OUTPUT"
echo "$NAME==$VERSION is already on PyPI — nothing to publish (bump the version to release)."
else
echo "already=false" >> "$GITHUB_OUTPUT"
fi
- name: Build sdist + wheel
if: steps.ver.outputs.already == 'false'
working-directory: sdk/python
run: |
python -m pip install --upgrade build
python -m build
- name: Publish to PyPI
if: steps.ver.outputs.already == 'false'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: sdk/python/dist
+82
View File
@@ -0,0 +1,82 @@
name: Publish SDK (npm)
# Publishes @officecli/sdk (sdk/node) on its OWN cadence — decoupled from the
# CLI release. The SDK depends on @officecli/officecli via a semver range
# (^1.0.0), so a CLI version bump flows to users automatically (fresh installs
# resolve the newest CLI; existing installs auto-update the bundled binary)
# WITHOUT republishing the SDK. The SDK is republished only when its own source
# changes — i.e. when sdk/node/package.json's version is bumped.
#
# Trigger: a push touching sdk/node/** (or manual dispatch). A guard skips the
# publish when that version is already on npm, so only a version bump actually
# publishes — pushes that don't change the version are no-ops.
#
# The same SDK is published under four names (one canonical + mirrors/aliases).
# The version is shared (sdk/node/package.json); the name is swapped per leg.
#
# Auth: npm Trusted Publishing (OIDC), no token. Configure a trusted publisher
# for EACH of the four packages on npmjs.com:
# org/user: iOfficeAI repo: OfficeCLI workflow: publish-sdk.yml action: npm publish
on:
push:
branches: [main]
paths:
- 'sdk/node/**'
workflow_dispatch:
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
strategy:
fail-fast: false
matrix:
package:
- '@officecli/sdk'
- '@officecli/officecli-sdk'
- '@aionui/officecli-sdk'
- 'officecli-sdk'
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Upgrade npm (trusted publishing + provenance need >= 11.5.1)
run: npm install -g npm@latest
- name: Set package name and check if already published
id: ver
working-directory: sdk/node
run: |
npm pkg set name='${{ matrix.package }}'
# Only the package name differs per scope; links stay officecli.ai.
# Rewrite install/require examples to this package's name.
sed -i "s|@officecli/sdk|${{ matrix.package }}|g" README.md
VERSION=$(node -p "require('./package.json').version")
echo "Publishing candidate: ${{ matrix.package }}@$VERSION"
if ! npm view "${{ matrix.package }}" version >/dev/null 2>&1; then
# A brand-new package can't be created by trusted publishing (OIDC) —
# it has no package to attach the publisher config to, so the PUT 404s.
# Skip (green) until the one-time MANUAL first publish creates it.
echo "already=true" >> "$GITHUB_OUTPUT"
echo "::notice::${{ matrix.package }} does not exist on npm yet — first publish must be manual; skipping."
elif npm view "${{ matrix.package }}@$VERSION" version >/dev/null 2>&1; then
echo "already=true" >> "$GITHUB_OUTPUT"
echo "${{ matrix.package }}@$VERSION is already on npm — nothing to publish (bump the version to release)."
else
echo "already=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish to npm
if: steps.ver.outputs.already == 'false'
working-directory: sdk/node
run: npm publish --provenance --access public
+50
View File
@@ -0,0 +1,50 @@
name: SDK smoke
# End-to-end smoke for both SDKs on all three OSes. The runners have no officecli
# on PATH, so create() exercises the FULL auto-install path — install.sh on
# unix, install.ps1 (irm | iex via PowerShell) on Windows — then a real pipe
# round-trip. This is the only place the Windows installer path is exercised, and
# it runs against the SDK source directly (no bundled dependency installed), so
# the install.sh/install.ps1 fallback is what gets tested.
on:
push:
branches: [main]
paths:
- 'sdk/**'
- '.github/workflows/sdk-smoke.yml'
pull_request:
paths:
- 'sdk/**'
- '.github/workflows/sdk-smoke.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
smoke:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: '20'
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Node SDK auto-install + round-trip
run: node sdk/node/smoke.js
- name: Python SDK auto-install + round-trip
run: python sdk/python/smoke.py
+41
View File
@@ -0,0 +1,41 @@
name: Skill parity
# Root SKILL.md is the file the public URL serves
# (https://d.officecli.ai/SKILL.md, raw GitHub URL) and is what
# `curl ... | bash` consumers fetch. skills/officecli/SKILL.md is a
# symlink to it — the spec-conforming location that `gh skill install`
# and `npx skills add` discover, and what the binary embeds.
#
# On macOS / Linux the symlink resolves transparently and this diff
# always passes. The check exists for the Windows case: git on Windows
# without core.symlinks=true checks out symlinks as plain text files
# containing the link target path (e.g. literal "../../SKILL.md"), so
# diff catches that corruption before it ships in a release build.
on:
push:
branches: [main]
pull_request:
paths:
- 'SKILL.md'
- 'skills/officecli/SKILL.md'
- '.github/workflows/skill-parity.yml'
permissions:
contents: read
jobs:
diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Diff root SKILL.md vs skills/officecli/SKILL.md
run: |
if ! diff -q SKILL.md skills/officecli/SKILL.md; then
echo "::error::SKILL.md and skills/officecli/SKILL.md are out of sync."
echo "The two files must be byte-identical. The binary embeds skills/officecli/SKILL.md;"
echo "the public URL (d.officecli.ai/SKILL.md, raw GitHub) serves the root copy."
echo "Fix: cp SKILL.md skills/officecli/SKILL.md (or the reverse) and commit."
exit 1
fi
echo "OK: SKILL.md == skills/officecli/SKILL.md"