Skip to content

Release v1.3.6#84

Merged
Aliothmoon merged 2 commits into
mainfrom
dev
Mar 16, 2026
Merged

Release v1.3.6#84
Aliothmoon merged 2 commits into
mainfrom
dev

Conversation

@Aliothmoon

@Aliothmoon Aliothmoon commented Mar 16, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

在补丁生成过程中添加目录级别的差异追踪,并确保归档文件中对新增/删除的文件夹包含显式的目录条目。

新功能:

  • 在版本之间追踪新增和删除的目录,并将其包含在生成的补丁元数据中。
  • 将目录变更信息传播到增量补丁包中,适用于 ziptar.gz 格式。

增强:

  • 在补丁和归档元数据中将路径统一规范为使用正斜杠,以实现跨平台一致性。
  • 确保 ziptar.gz 归档在增量补丁中为新添加的目录包含显式的目录条目。

测试:

  • 添加覆盖全面的单元测试和集成测试,用于目录差异计算、补丁生成以及跨 ziptar.gz 格式的端到端完整流程,包括无变更、全部新增、全部删除、重命名以及仅根目录文件等边界情况。
Original summary in English

Summary by Sourcery

Add directory-level diff tracking to patch generation and ensure archives contain explicit directory entries for added/removed folders.

New Features:

  • Track added and deleted directories between versions and include them in generated patch metadata.
  • Propagate directory change information into incremental patch packages for both zip and tar.gz formats.

Enhancements:

  • Normalize paths to forward slashes in patch and archive metadata for cross-platform consistency.
  • Ensure zip and tar.gz archives include explicit directory entries for newly added directories in incremental patches.

Tests:

  • Add comprehensive unit and integration tests for directory diff calculation, patch generation, and full end-to-end pipelines across zip and tar.gz formats, including edge cases like no changes, all-new, all-deleted, renames, and root-only files.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些总体反馈:

  • 在 CalculateDirDiff 中,addedDirs/deletedDirs 切片是通过 map 遍历构建的,因此它们的顺序是非确定性的;建议在返回前对它们进行排序,这样 changes.json 和归档内容在多次运行之间就能保持稳定且可重现。
  • 当前的 extractDirs 会针对每个文件向上遍历所有父目录;如果要在大型目录树上运行,你可以通过缓存已访问的目录,或者根据文件数量预先分配 map 来做一些微优化,以减少重复工作和内存分配。
供 AI 代理使用的提示
请根据这次代码评审中的评论进行修改:

## 总体评论
- 在 CalculateDirDiff 中,addedDirs/deletedDirs 切片是通过 map 遍历构建的,因此它们的顺序是非确定性的;建议在返回前对它们进行排序,这样 changes.json 和归档内容在多次运行之间就能保持稳定且可重现。
- 当前的 extractDirs 会针对每个文件向上遍历所有父目录;如果要在大型目录树上运行,你可以通过缓存已访问的目录,或者根据文件数量预先分配 map 来做一些微优化,以减少重复工作和内存分配。

## 逐条评论

### 评论 1
<location path="internal/pkg/patcher/patcher.go" line_range="328-330" />
<code_context>
 		}
 	}

+	// create added directories in temp root so they appear in the archive
+	for _, d := range addedDirs {
+		dirPath := filepath.Join(root, d)
+		if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
+			return fmt.Errorf("failed to create added dir: %w", err)
+		}
</code_context>
<issue_to_address>
**suggestion:** 当 MkdirAll 失败时,在错误信息中包含目录路径

当前的错误信息没有指出是哪个目录创建失败。在包装错误时包含路径(例如 `fmt.Errorf("failed to create added dir %q: %w", dirPath, err)`)会让定位权限或路径问题变得容易得多。

```suggestion
		if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
			return fmt.Errorf("failed to create added dir %q: %w", dirPath, err)
		}
```
</issue_to_address>

Sourcery 对开源项目免费 —— 如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The addedDirs/deletedDirs slices are built from map iteration in CalculateDirDiff, so their order is nondeterministic; consider sorting them before returning so changes.json and archive contents are stable and reproducible across runs.
  • extractDirs currently walks up every parent directory for each file; if this runs over large trees, you could micro-optimize by caching visited directories or preallocating the map based on the number of files to reduce repeated work and allocations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The addedDirs/deletedDirs slices are built from map iteration in CalculateDirDiff, so their order is nondeterministic; consider sorting them before returning so changes.json and archive contents are stable and reproducible across runs.
- extractDirs currently walks up every parent directory for each file; if this runs over large trees, you could micro-optimize by caching visited directories or preallocating the map based on the number of files to reduce repeated work and allocations.

## Individual Comments

### Comment 1
<location path="internal/pkg/patcher/patcher.go" line_range="328-330" />
<code_context>
 		}
 	}

+	// create added directories in temp root so they appear in the archive
+	for _, d := range addedDirs {
+		dirPath := filepath.Join(root, d)
+		if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
+			return fmt.Errorf("failed to create added dir: %w", err)
+		}
</code_context>
<issue_to_address>
**suggestion:** Include directory path in the error message when MkdirAll fails

The error message doesn’t identify which directory failed. Including the path in the wrapped error (e.g. `fmt.Errorf("failed to create added dir %q: %w", dirPath, err)`) will make it much easier to diagnose permission or path issues.

```suggestion
		if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
			return fmt.Errorf("failed to create added dir %q: %w", dirPath, err)
		}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +328 to +330
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create added dir: %w", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 当 MkdirAll 失败时,在错误信息中包含目录路径

当前的错误信息没有指出是哪个目录创建失败。在包装错误时包含路径(例如 fmt.Errorf("failed to create added dir %q: %w", dirPath, err))会让定位权限或路径问题变得容易得多。

Suggested change
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create added dir: %w", err)
}
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create added dir %q: %w", dirPath, err)
}
Original comment in English

suggestion: Include directory path in the error message when MkdirAll fails

The error message doesn’t identify which directory failed. Including the path in the wrapped error (e.g. fmt.Errorf("failed to create added dir %q: %w", dirPath, err)) will make it much easier to diagnose permission or path issues.

Suggested change
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create added dir: %w", err)
}
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create added dir %q: %w", dirPath, err)
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances incremental patch generation by tracking directory-level changes and ensuring archives include explicit directory entries, with accompanying end-to-end tests.

Changes:

  • Add directory diff calculation (CalculateDirDiff) and include added_dir / deleted_dir in changes.json.
  • Update patch generation to create added directories so they are emitted into the resulting archive.
  • Normalize archive entry paths and emit directory entries in ZIP/TAR.GZ compression; add comprehensive patcher tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
internal/pkg/patcher/patcher_test.go New unit/integration-style tests covering diff, dir diff, and full patch pipeline for zip/tgz.
internal/pkg/patcher/patcher.go Adds dir diff support and propagates added/deleted dirs into patch generation + changes.json.
internal/pkg/archiver/archiver.go Normalizes entry paths to / and explicitly writes directory entries for zip/tar.gz.
internal/logic/version.go Wires directory diff into incremental package creation flow.
Comments suppressed due to low confidence (1)

internal/pkg/patcher/patcher.go:271

  • appendChangesRecord creates changes.json with os.Create but then ignores that handle and writes via os.WriteFile. Besides being redundant, this means the final file permissions come from the initial os.Create (umask/0666) rather than the intended 0644 passed to WriteFile. Remove the unused os.Create and write once (or write to the created file handle) to avoid incorrect perms and double opens.
func appendChangesRecord(root string, changes []Change, addedDirs, deletedDirs []string) error {
	path := filepath.Join(root, "changes.json")
	data := getChangesInfo(changes, addedDirs, deletedDirs)

	f, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("failed to create changes.json file: %w", err)
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	buf, err := sonic.Marshal(data)
	if err != nil {
		return fmt.Errorf("failed to marshal changes to JSON: %w", err)
	}

	if err := os.WriteFile(path, buf, 0644); err != nil {
		return fmt.Errorf("failed to write JSON to file: %w", err)
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 154 to +167
relPath, err := filepath.Rel(srcDir, path)
if err != nil {
return err
}
// normalize to forward slashes for standard zip entry names
relPath = filepath.ToSlash(relPath)

if info.IsDir() {
if relPath == "." {
return nil
}
_, err := writer.Create(relPath + "/")
return err
}
Comment on lines 220 to +236
relPath, err := filepath.Rel(srcDir, path)
if err != nil {
return err
}
// normalize to forward slashes for standard tar entry names
relPath = filepath.ToSlash(relPath)

if info.IsDir() {
return nil
if relPath == "." {
return nil
}
header, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}
header.Name = relPath + "/"
return tarWriter.WriteHeader(header)
Comment on lines +83 to +97
func CalculateDirDiff(newHashes, oldHashes map[string]string) (addedDirs, deletedDirs []string) {
newDirs := extractDirs(newHashes)
oldDirs := extractDirs(oldHashes)

for d := range newDirs {
if _, exists := oldDirs[d]; !exists {
addedDirs = append(addedDirs, d)
}
}
for d := range oldDirs {
if _, exists := newDirs[d]; !exists {
deletedDirs = append(deletedDirs, d)
}
}
return
@Aliothmoon Aliothmoon merged commit d2f98b0 into main Mar 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants