Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: 2

updates:
- package-ecosystem: "cargo"
directory: "/tools/wta"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 5
groups:
wta-rust-minor-and-patch:
applies-to: "version-updates"
patterns:
- "*"
exclude-patterns:
- "agent-client-protocol*"
update-types:
- "minor"
- "patch"

# WTA statically links its Rust dependencies. Before merging an update,
# regenerate and commit tools/wta/cgmanifest.json and the WTA block in
# NOTICE.md with build/scripts/Generate-WtaThirdPartyNotices.ps1.
8 changes: 4 additions & 4 deletions .github/instructions/rust-wta.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ and product behavior are defined in `AGENTS.md` and `tools/wta/AGENTS.md`.

## Toolchain and dependencies

- Keep the CI `ms-prod-1.93` pin in `tools/wta/rust-toolchain.toml` unless a
toolchain update is the explicit task. Repo-root local commands use the
installed active toolchain, so do not rely on language or library features
newer than Rust 1.93.
- Keep the public `1.93` pin in `tools/wta/rust-toolchain.toml` unless a
toolchain update is the explicit task. GitHub automation resolves that pin
with rustup, while Azure DevOps installs the matching `ms-prod-1.93`
toolchain. Do not let the versions drift.
- New dependencies must build with the repo's static-CRT Windows MSVC
configuration.
- Use the explicit Windows target from the repo-level build instructions; do
Expand Down
10 changes: 7 additions & 3 deletions .github/skills/add-acp-agent-support/references/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ Add or update tests for:
- invalid or empty delegate executable rejection;
- policy filtering or settings serialization when those paths changed.

Run the WTA suite from the repository root:
Run the WTA suite from its crate directory so Cargo uses the pinned toolchain:

```powershell
cargo test --manifest-path tools\wta\Cargo.toml
Push-Location tools\wta
cargo test
Pop-Location
```

Do not treat a successful build as a substitute for tests; WTA test-only code
Expand All @@ -59,7 +61,9 @@ Resolve and stop only the specific live WTA process IDs before rebuilding:
```powershell
Get-Process wta -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.Id -Force }
cargo build --target x86_64-pc-windows-msvc --manifest-path tools\wta\Cargo.toml
Push-Location tools\wta
cargo build --target x86_64-pc-windows-msvc
Pop-Location
```

Always use the explicit target for a package-validation cycle because
Expand Down
109 changes: 109 additions & 0 deletions .github/skills/wta-rust-dependency-update/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: wta-rust-dependency-update
description: 'Regenerate, review, test, and verify WTA Rust dependency updates and third-party attribution. Use when handling Dependabot PRs for /tools/wta, editing tools/wta/Cargo.toml or Cargo.lock, changing dependency features, finishing or reviewing a WTA dependency PR, or fixing stale NOTICE.md or cgmanifest CI failures.'
---

# WTA Rust Dependency Update

Keep WTA Cargo dependency changes synchronized with the generated Component
Governance manifest and third-party notices required for the statically linked
`wta.exe`.

## When to Use This Skill

- Complete or review a Dependabot PR for `/tools/wta`.
- Add, remove, or upgrade a dependency in `tools/wta/Cargo.toml`.
- Update `tools/wta/Cargo.lock` in a way that changes runtime dependencies.
- Change dependency features that add or remove transitive runtime crates.
- Fix the **Verify WTA third-party notices** CI failure.

Do not run this workflow for Rust source-only changes that leave the dependency
graph unchanged.

## Prerequisites

- Run commands from the repository root.
- Use PowerShell 7 or later (`pwsh`), not Windows PowerShell 5.1.
- Read `tools/wta/AGENTS.md` and the WTA Rust instructions before changing
Cargo files.
- Preserve unrelated worktree changes; never reset or overwrite them.

## Workflow

1. Inspect the PR or worktree diff and confirm which Cargo manifest, lockfile,
or feature change triggered the dependency update.
2. Choose the safe mode:
- For a review-only request, do not run the mutating generator. Inspect the
existing diff and continue with the offline `-Verify` command in step 4.
- When completing or fixing the update, regenerate both attribution
artifacts:

```powershell
pwsh -File .\build\scripts\Generate-WtaThirdPartyNotices.ps1
```

3. Review the dependency and generated changes together:

```powershell
$baseRef = gh pr view --json baseRefName --jq .baseRefName 2>$null
if ($LASTEXITCODE -eq 0) {
git fetch origin $baseRef
$compareRef = git merge-base HEAD "origin/$baseRef"
} else {
$compareRef = 'HEAD'
}
git diff --check $compareRef --
git diff $compareRef -- tools/wta/Cargo.toml tools/wta/Cargo.lock `
tools/wta/cgmanifest.json NOTICE.md
```

4. Run the same offline consistency check used by CI:

```powershell
pwsh -File .\build\scripts\Generate-WtaThirdPartyNotices.ps1 -Verify
```

5. Test and finish:
- Run the WTA test suite with the pinned toolchain:

```powershell
Get-Process wta -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.Id -Force }
Push-Location tools/wta
cargo test --locked
Pop-Location
```

- If generation produced changes, keep `NOTICE.md` and
`tools/wta/cgmanifest.json` in the same PR as the Cargo update.
- For a Dependabot branch, add a follow-up commit; do not rewrite the bot
commit unless the user explicitly requests it.
- If generation produced no changes, do not create an empty attribution
commit; report that `-Verify` passed.
- Commit or push only when requested. Include all generated attribution
changes together and describe them as generated output.

## Completion Gate

- In update/completion mode, the normal generator exits successfully.
- Generated changes contain only dependency attribution expected from the
Cargo update.
- `git diff --check` passes.
- `Generate-WtaThirdPartyNotices.ps1 -Verify` reports the same runtime crate
count in `cgmanifest.json` and `NOTICE.md`.
- `cargo test --locked` passes from `tools/wta`.
- No dependency PR is declared ready while generated artifacts are stale.

## Gotchas

- **Do not edit generated attribution text by hand.** Fix the Cargo metadata or
generator and rerun it.
- **Do not run only `-Verify` after a dependency change.** Verification detects
stale artifacts but does not regenerate them.
- **Do not assume every lockfile change needs an attribution diff.** Dev-only,
build-only, or platform-excluded dependency changes may leave runtime output
unchanged.
- **Do not omit `NOTICE.md` when `cgmanifest.json` changes, or vice versa.**
They are generated from the same runtime dependency graph.
- **Review major updates separately.** In particular,
`agent-client-protocol*` updates require focused ACP compatibility review.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you

Testing is a key component in the development workflow. The C++ Terminal and Console code uses TAEF (the Test Authoring and Execution Framework); the Rust WTA crate uses standard `cargo test`.

If your changes affect existing test cases, or you're working on brand new features and also the accompanying test cases, see [TAEF](./doc/TAEF.md) for more information about how to validate the C++ side locally, and run `cargo test --manifest-path tools/wta/Cargo.toml` for the Rust side.
If your changes affect existing test cases, or you're working on brand new features and also the accompanying test cases, see [TAEF](./doc/TAEF.md) for more information about how to validate the C++ side locally. For the Rust side, run `cargo test` from `tools/wta` so Cargo uses the pinned toolchain.

### Code Review

Expand Down
2 changes: 1 addition & 1 deletion NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ Applies to 2 crate(s) (directly or via composite identifiers): moxcms v0.8.1, px
_Canonical text reproduced from upstream `SPDX:BSD-3-Clause`:_

```
Copyright (c) <year> <owner>.
Copyright (c) <year> <owner>.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Expand Down
80 changes: 68 additions & 12 deletions build/scripts/Generate-WtaThirdPartyNotices.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,22 @@
left byte-identical.

.NOTES
Requires `cargo` on PATH. The repo's rust-toolchain.toml pins a
custom channel for CI builds; for metadata-only invocations bypass
it by setting `$env:RUSTUP_TOOLCHAIN = 'stable'` before invoking
the script.
Requires `cargo` on PATH. Cargo runs from tools/wta so rustup discovers
the repository's pinned public toolchain. CI can explicitly override it
with RUSTUP_TOOLCHAIN.

.PARAMETER Verify
Verify that cgmanifest.json and the generated NOTICE block contain exactly
the runtime Cargo dependency versions without rewriting either artifact or
contacting external license sources.

.EXAMPLE
PS> $env:RUSTUP_TOOLCHAIN = 'stable'
PS> .\build\scripts\Generate-WtaThirdPartyNotices.ps1
#>
[CmdletBinding()]
param()
param(
[switch]$Verify
)

$ErrorActionPreference = 'Stop'

Expand All @@ -71,11 +76,25 @@ $targetTriple = 'x86_64-pc-windows-msvc'
# needed in this script).
# ---------------------------------------------------------------------------
Write-Host "Running cargo metadata for $wtaRoot\Cargo.toml (--filter-platform $targetTriple)" -ForegroundColor Cyan
$cargoJson = & cargo metadata --format-version 1 `
--filter-platform $targetTriple `
--manifest-path (Join-Path $wtaRoot 'Cargo.toml')
if ($LASTEXITCODE -ne 0) {
throw "cargo metadata failed with exit code $LASTEXITCODE. Is cargo on PATH? Try `$env:RUSTUP_TOOLCHAIN='stable'."
$cargoArgs = @(
'metadata'
'--format-version', '1'
'--filter-platform', $targetTriple
'--manifest-path', (Join-Path $wtaRoot 'Cargo.toml')
)
if ($Verify) {
$cargoArgs += @('--locked', '--offline')
}

Push-Location $wtaRoot
try {
$cargoJson = & cargo @cargoArgs
if ($LASTEXITCODE -ne 0) {
throw "cargo metadata failed with exit code $LASTEXITCODE. Is cargo on PATH?"
}
}
finally {
Pop-Location
}
$meta = $cargoJson | ConvertFrom-Json -Depth 100

Expand Down Expand Up @@ -130,6 +149,42 @@ foreach ($pkgId in $seen) {
$attributed = $attributed | Sort-Object name, version
Write-Host "Crates to attribute: $($attributed.Count)" -ForegroundColor Cyan

if ($Verify) {
$expected = @($attributed | ForEach-Object { "$($_.name)@$($_.version)" } | Sort-Object)

$cgManifest = Get-Content -Raw $cgManifestPath | ConvertFrom-Json -Depth 20
$actualCg = @(
$cgManifest.Registrations |
ForEach-Object { "$($_.component.cargo.name)@$($_.component.cargo.version)" } |
Sort-Object
)

$notice = Get-Content -Raw $noticePath
$beginMarker = '<!-- BEGIN wta-rust-deps'
$endMarker = '<!-- END wta-rust-deps -->'
$beginIndex = $notice.IndexOf($beginMarker, [StringComparison]::Ordinal)
$endIndex = $notice.IndexOf($endMarker, [StringComparison]::Ordinal)
if ($beginIndex -lt 0 -or $endIndex -le $beginIndex) {
throw 'NOTICE.md does not contain a valid WTA Rust dependency block.'
}

$noticeBlock = $notice.Substring($beginIndex, $endIndex - $beginIndex)
$actualNotice = @(
[regex]::Matches($noticeBlock, '(?m)^- \*\*(?<name>[^*]+)\*\* v(?<version>\S+) --') |
ForEach-Object { "$($_.Groups['name'].Value)@$($_.Groups['version'].Value)" } |
Sort-Object
)

$cgDiff = @(Compare-Object $expected $actualCg)
$noticeDiff = @(Compare-Object $expected $actualNotice)
if ($cgDiff.Count -gt 0 -or $noticeDiff.Count -gt 0) {
throw 'WTA third-party artifacts are stale. Run build/scripts/Generate-WtaThirdPartyNotices.ps1 and commit NOTICE.md and tools/wta/cgmanifest.json.'
}

Write-Host "Verified $($expected.Count) WTA Rust dependencies in cgmanifest.json and NOTICE.md." -ForegroundColor Green
return
}

# ---------------------------------------------------------------------------
# 4. SPDX normalization + atomic-token decomposition.
# - `X/Y` -> `X OR Y` (legacy cargo notation)
Expand Down Expand Up @@ -475,7 +530,8 @@ foreach ($atom in ($atomToCrates.Keys | Sort-Object)) {
$out.Add("_Canonical text reproduced from upstream ``$($src.Name)``:_")
$out.Add('')
$out.Add('```')
$out.Add($src.Text.TrimEnd())
$normalizedText = (($src.Text -split "`r?`n") | ForEach-Object { $_.TrimEnd() }) -join "`n"
$out.Add($normalizedText.TrimEnd())
$out.Add('```')
} else {
$out.Add('```')
Expand Down
43 changes: 25 additions & 18 deletions build/scripts/New-WtaLocalInstaller.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,25 @@ function Find-CargoPath {
}

function Get-InstalledRustTargets {
param(
[Parameter(Mandatory = $true)]
[string]$ToolchainRoot
)

$rustupPath = Join-Path $env:USERPROFILE '.cargo\bin\rustup.exe'
if (-not (Test-Path $rustupPath -PathType Leaf)) {
return @()
}

$targets = & $rustupPath target list --installed
if ($LASTEXITCODE -ne 0) {
throw 'rustup target list --installed failed.'
Push-Location $ToolchainRoot
try {
$targets = & $rustupPath target list --installed
if ($LASTEXITCODE -ne 0) {
throw 'rustup target list --installed failed.'
}
}
finally {
Pop-Location
}

return @($targets | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
Expand All @@ -112,19 +123,14 @@ function Invoke-RustBuild {
[string]$RustTarget,

[Parameter(Mandatory = $true)]
[string]$RepoRoot
[string]$ToolchainRoot
)

# Cargo's config discovery walks up from the current working directory,
# not from the manifest path. Pin CWD to the repo root so Cargo finds the
# repo-root .cargo/config.toml that supplies `+crt-static` — even when
# this script is launched from outside the repo.
#
# Important: do NOT push into the manifest's directory. tools/wta/ has its
# own rust-toolchain.toml, so letting rustup discover that file from CWD
# can change toolchain resolution compared to the repo-root configuration
# this script relies on for local builds.
Push-Location $RepoRoot
# not from the manifest path. Build both Rust binaries from tools/wta so
# rustup discovers the pinned public toolchain and Cargo walks up to the
# repo-root .cargo/config.toml that supplies `+crt-static`.
Push-Location $ToolchainRoot
try {
& $CargoPath build --manifest-path $ManifestPath --release --target $RustTarget
if ($LASTEXITCODE -ne 0) {
Expand Down Expand Up @@ -452,10 +458,11 @@ if ($BuildTerminal -and $installerVersion -ne $expectedManifestIdentity.Version)

$cargoPath = Find-CargoPath
$rustTarget = Get-RustTarget -PlatformName $Platform
$installedTargets = Get-InstalledRustTargets
$wtaRoot = Join-Path $repoRoot 'tools\wta'
$installedTargets = Get-InstalledRustTargets -ToolchainRoot $wtaRoot

if ($installedTargets.Count -gt 0 -and $installedTargets -notcontains $rustTarget) {
throw "Rust target $rustTarget is not installed. Install it with rustup target add $rustTarget."
throw "Rust target $rustTarget is not installed for the WTA toolchain. Run rustup target add $rustTarget from tools/wta."
}

$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
Expand Down Expand Up @@ -498,8 +505,8 @@ if ($SkipWtaBuild) {
$resolvedWtaExePath = Resolve-AbsolutePath -Path $WtaExePath
} else {
Write-Status "Building wta.exe for $rustTarget with a static CRT ..."
$manifestPath = Join-Path $repoRoot 'tools\wta\Cargo.toml'
Invoke-RustBuild -CargoPath $cargoPath -ManifestPath $manifestPath -RustTarget $rustTarget -RepoRoot $repoRoot
$manifestPath = Join-Path $wtaRoot 'Cargo.toml'
Invoke-RustBuild -CargoPath $cargoPath -ManifestPath $manifestPath -RustTarget $rustTarget -ToolchainRoot $wtaRoot
$resolvedWtaExePath = Join-Path $repoRoot ("tools\wta\target\{0}\release\wta.exe" -f $rustTarget)
}

Expand Down Expand Up @@ -553,7 +560,7 @@ Copy-Item -Path $installerCmd -Destination (Join-Path $installerSourceRoot 'inst
Copy-Item -Path $payloadZip -Destination (Join-Path $installerSourceRoot 'payload.zip') -Force

Write-Status "Building installer bootstrap for $rustTarget ..."
Invoke-RustBuild -CargoPath $cargoPath -ManifestPath $installerBootstrapManifest -RustTarget $rustTarget -RepoRoot $repoRoot
Invoke-RustBuild -CargoPath $cargoPath -ManifestPath $installerBootstrapManifest -RustTarget $rustTarget -ToolchainRoot $wtaRoot
$bootstrapExePath = Join-Path $repoRoot ("installer\bootstrap\target\{0}\release\intelligent-terminal-installer-bootstrap.exe" -f $rustTarget)
if (-not (Test-Path $bootstrapExePath -PathType Leaf)) {
throw "Installer bootstrap not found: $bootstrapExePath"
Expand Down
Loading
Loading