Skip to content

Commit 482a7cc

Browse files
montfortclaude
andauthored
fix(cli): update no longer copies dist-manifest.yml or dist-templates into adopter projects (cli-3.5.3) (#64)
devtrail update / update-framework used to walk every file in the extracted release ZIP and copy each one whose path didn't already exist in the target — which silently deposited the framework's internal dist-manifest.yml and dist-templates/ directory at the root of every updated project. devtrail init was already correct: it only extracts paths declared in manifest.files. update_framework.rs now applies the same whitelist via matches_manifest(), so only files declared in the release manifest are copied. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f25e7ee commit 482a7cc

10 files changed

Lines changed: 85 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ and this project uses [independent versioning](README.md#versioning) for Framewo
77

88
---
99

10+
## CLI 3.5.3 — `devtrail update` no longer leaks package internals into adopter projects
11+
12+
### Fixed (CLI)
13+
- `devtrail update` (and `devtrail update-framework`) used to copy the framework's internal `dist-manifest.yml` and `dist-templates/` directory into the root of the adopter project. Both are package-internal artifacts: the manifest is the catalogue the CLI reads from the release ZIP, and `dist-templates/` is the source of agent-directive injections that are read into memory and merged via marker blocks — neither is meant to land on disk in the target project. `devtrail init` already filtered correctly via `manifest.files`; only the update path was inconsistent. Update now applies the same whitelist, so only files declared in the release manifest are copied. Existing projects affected by the bug can clean up by deleting `dist-manifest.yml` and `dist-templates/` from their project root and running `devtrail update-framework` again to regenerate `.devtrail/.checksums.json` without orphan entries.
14+
15+
---
16+
1017
## CLI 3.5.2 — Remove Undocumented Vim-Style Aliases (`l`, `h`)
1118

1219
### Changed (CLI)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ DevTrail uses independent version tags for each component:
207207
| Component | Tag prefix | Example | Includes |
208208
|-----------|-----------|---------|----------|
209209
| Framework | `fw-` | `fw-4.3.0` | Templates (12 types), governance, directives |
210-
| CLI | `cli-` | `cli-3.5.2` | The `devtrail` binary |
210+
| CLI | `cli-` | `cli-3.5.3` | The `devtrail` binary |
211211

212212
Check installed versions with `devtrail status` or `devtrail about`.
213213

cli/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "devtrail-cli"
3-
version = "3.5.2"
3+
version = "3.5.3"
44
edition = "2021"
55
description = "CLI tool for DevTrail - Documentation Governance for AI-Assisted Development"
66
license = "MIT"

cli/src/commands/update_framework.rs

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn run() -> Result<()> {
7373

7474
// Update framework files
7575
utils::info("Updating framework files...");
76-
let stats = update_files(&target, &source_root, &current_checksums)?;
76+
let stats = update_files(&target, &source_root, &manifest, &current_checksums)?;
7777

7878
// Update directive injections
7979
utils::info("Updating AI agent directives...");
@@ -105,6 +105,7 @@ struct UpdateStats {
105105
fn update_files(
106106
target: &Path,
107107
source_root: &Path,
108+
manifest: &DistManifest,
108109
checksums: &Checksums,
109110
) -> Result<UpdateStats> {
110111
let mut stats = UpdateStats {
@@ -121,20 +122,19 @@ fn update_files(
121122
.strip_prefix(source_root)
122123
.unwrap_or(&source_path)
123124
.display()
124-
.to_string();
125-
126-
// Skip user-generated documents
127-
if utils::is_user_document(&source_path) {
128-
continue;
129-
}
130-
131-
// Skip checksums file
132-
if relative == ".devtrail/.checksums.json" {
125+
.to_string()
126+
.replace('\\', "/");
127+
128+
// Only touch files declared by the release manifest. The release ZIP also
129+
// ships internal artifacts (`dist-manifest.yml`, `dist-templates/`) that
130+
// the CLI consumes from the temp dir but must never copy into the
131+
// adopter's project. Mirrors `init.rs::extract_matching_files`.
132+
if !matches_manifest(&relative, &manifest.files) {
133133
continue;
134134
}
135135

136-
// Skip dist-manifest.yml (we save it separately)
137-
if relative == ".devtrail/dist-manifest.yml" {
136+
// Skip user-generated documents
137+
if utils::is_user_document(&source_path) {
138138
continue;
139139
}
140140

@@ -325,6 +325,18 @@ fn find_source_root(extract_dir: &Path) -> Result<PathBuf> {
325325
bail!("Could not find dist-manifest.yml in extracted archive");
326326
}
327327

328+
/// Match a relative path (POSIX-style) against the manifest's `files` whitelist.
329+
/// Patterns ending in `/` match any path under that directory; otherwise exact match.
330+
fn matches_manifest(relative: &str, files: &[String]) -> bool {
331+
files.iter().any(|pat| {
332+
if pat.ends_with('/') {
333+
relative.starts_with(pat.as_str())
334+
} else {
335+
relative == pat
336+
}
337+
})
338+
}
339+
328340
fn walkdir(dir: PathBuf) -> Result<Vec<PathBuf>> {
329341
let mut files = Vec::new();
330342
if !dir.is_dir() {
@@ -343,3 +355,49 @@ fn walkdir(dir: PathBuf) -> Result<Vec<PathBuf>> {
343355

344356
Ok(files)
345357
}
358+
359+
#[cfg(test)]
360+
mod tests {
361+
use super::matches_manifest;
362+
363+
fn manifest_files() -> Vec<String> {
364+
// Matches `dist/dist-manifest.yml` (fw-4.3.0).
365+
vec![
366+
".devtrail/".to_string(),
367+
"DEVTRAIL.md".to_string(),
368+
".claude/skills/".to_string(),
369+
".gemini/skills/".to_string(),
370+
".agent/workflows/".to_string(),
371+
".github/workflows/docs-validation.yml".to_string(),
372+
]
373+
}
374+
375+
#[test]
376+
fn package_artifacts_are_rejected() {
377+
let files = manifest_files();
378+
// Regression for the bug where `devtrail update` deposited these in the
379+
// adopter project. Both live at the ZIP root, neither is in `manifest.files`.
380+
assert!(!matches_manifest("dist-manifest.yml", &files));
381+
assert!(!matches_manifest("dist-templates/directives/CLAUDE.md", &files));
382+
}
383+
384+
#[test]
385+
fn declared_files_and_directories_match() {
386+
let files = manifest_files();
387+
assert!(matches_manifest("DEVTRAIL.md", &files));
388+
assert!(matches_manifest(".devtrail/00-governance/AGENT-RULES.md", &files));
389+
assert!(matches_manifest(".claude/skills/devtrail-new/SKILL.md", &files));
390+
assert!(matches_manifest(
391+
".github/workflows/docs-validation.yml",
392+
&files
393+
));
394+
}
395+
396+
#[test]
397+
fn undeclared_paths_are_rejected() {
398+
let files = manifest_files();
399+
assert!(!matches_manifest("README.md", &files));
400+
assert!(!matches_manifest(".github/workflows/release-cli.yml", &files));
401+
assert!(!matches_manifest(".claude/agents/foo.md", &files));
402+
}
403+
}

docs/adopters/CLI-REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ DevTrail uses **independent version tags** for each component:
4949
| Component | Tag prefix | Example | What it includes |
5050
|-----------|-----------|---------|------------------|
5151
| Framework | `fw-` | `fw-4.3.0` | Templates (12 types), governance docs, directives |
52-
| CLI | `cli-` | `cli-3.5.2` | The `devtrail` binary |
52+
| CLI | `cli-` | `cli-3.5.3` | The `devtrail` binary |
5353

5454
Framework and CLI are released independently. A framework update does not require a CLI update, and vice versa.
5555

docs/i18n/es/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ DevTrail usa tags de versión independientes para cada componente:
150150
| Componente | Prefijo de tag | Ejemplo | Incluye |
151151
|------------|---------------|---------|---------|
152152
| Framework | `fw-` | `fw-4.3.0` | Plantillas (12 tipos), gobernanza, directivas |
153-
| CLI | `cli-` | `cli-3.5.2` | El binario `devtrail` |
153+
| CLI | `cli-` | `cli-3.5.3` | El binario `devtrail` |
154154

155155
Verifica las versiones instaladas con `devtrail status` o `devtrail about`.
156156

docs/i18n/es/adopters/CLI-REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ DevTrail usa **tags de versión independientes** para cada componente:
4949
| Componente | Prefijo de tag | Ejemplo | Qué incluye |
5050
|------------|---------------|---------|-------------|
5151
| Framework | `fw-` | `fw-4.3.0` | Plantillas (12 tipos), docs de gobernanza, directivas |
52-
| CLI | `cli-` | `cli-3.5.2` | El binario `devtrail` |
52+
| CLI | `cli-` | `cli-3.5.3` | El binario `devtrail` |
5353

5454
Framework y CLI se publican de forma independiente. Una actualización del framework no requiere actualización del CLI, y viceversa.
5555

docs/i18n/zh-CN/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ DevTrail 为每个组件使用独立的版本标签:
150150
| 组件 | 标签前缀 | 示例 | 包含内容 |
151151
|------|----------|------|----------|
152152
| Framework | `fw-` | `fw-4.3.0` | 模板(12 种类型)、治理文档、指令 |
153-
| CLI | `cli-` | `cli-3.5.2` | `devtrail` 二进制文件 |
153+
| CLI | `cli-` | `cli-3.5.3` | `devtrail` 二进制文件 |
154154

155155
使用 `devtrail status``devtrail about` 查看已安装的版本。
156156

docs/i18n/zh-CN/adopters/CLI-REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ DevTrail 为每个组件使用**独立的版本标签**:
4949
| 组件 | 标签前缀 | 示例 | 包含内容 |
5050
|------|----------|------|----------|
5151
| Framework | `fw-` | `fw-4.3.0` | 模板(12 种类型)、治理文档、指令 |
52-
| CLI | `cli-` | `cli-3.5.2` | `devtrail` 二进制文件 |
52+
| CLI | `cli-` | `cli-3.5.3` | `devtrail` 二进制文件 |
5353

5454
Framework 和 CLI 独立发布。Framework 更新不需要 CLI 更新,反之亦然。
5555

0 commit comments

Comments
 (0)