This commit is contained in:
zyj
2026-03-03 18:20:18 +08:00
commit a9f9330744
25 changed files with 5004 additions and 0 deletions

71
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go-version: ['1.22']
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Download dependencies
run: go mod download
- name: Run tests
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...
- name: Upload coverage
if: matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v3
with:
file: coverage.out
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go-version: ['1.22']
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Build
run: go build -v ./cmd/devpack/

48
.gitignore vendored Normal file
View File

@@ -0,0 +1,48 @@
# Binaries
devpack
devpack.exe
*.exe
*.dll
*.so
*.dylib
# Build output
/dist/
/build/
/bin/
# Test binary
*.test
# Go workspace
go.work
go.work.sum
# IDE
.idea/
*.swp
*.swo
*~
.vscode/settings.json
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Pack files (don't commit test packs)
*.devpack
# Temp
tmp/
temp/
# Coverage
coverage.out
coverage.html
# Environment
.env
.env.local

49
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,49 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug DevPack",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": [],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
},
{
"name": "Debug Scan",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["scan", "--verbose"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
},
{
"name": "Debug Capture",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["capture", "--name", "test-env"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
},
{
"name": "Debug Restore",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["restore", "test-env", "--dry-run"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
}
]
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 DevPack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

95
Makefile Normal file
View File

@@ -0,0 +1,95 @@
.PHONY: build build-all test test-unit test-coverage lint fmt vet clean run dev help
# Variables
BINARY_NAME=devpack
MAIN_PATH=./cmd/devpack
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
LDFLAGS=-ldflags "-s -w -X github.com/user/devpack/pkg/version.Version=$(VERSION) -X github.com/user/devpack/pkg/version.Commit=$(COMMIT) -X github.com/user/devpack/pkg/version.Date=$(DATE)"
# Default target
all: lint test build
## help: Show this help message
help:
@echo "DevPack Makefile"
@echo ""
@echo "Usage:"
@sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /'
## build: Build for current platform
build:
go build $(LDFLAGS) -o $(BINARY_NAME) $(MAIN_PATH)
## build-windows: Build for Windows (amd64)
build-windows:
GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(BINARY_NAME).exe $(MAIN_PATH)
## build-darwin: Build for macOS (amd64 + arm64)
build-darwin:
GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(BINARY_NAME)-darwin-amd64 $(MAIN_PATH)
GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o $(BINARY_NAME)-darwin-arm64 $(MAIN_PATH)
## build-linux: Build for Linux (amd64 + arm64)
build-linux:
GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BINARY_NAME)-linux-amd64 $(MAIN_PATH)
GOOS=linux GOARCH=arm64 go build $(LDFLAGS) -o $(BINARY_NAME)-linux-arm64 $(MAIN_PATH)
## build-all: Build for all platforms
build-all: build-windows build-darwin build-linux
## test: Run all tests
test:
go test -race -count=1 ./...
## test-unit: Run unit tests only
test-unit:
go test -race -count=1 -short ./...
## test-coverage: Run tests with coverage report
test-coverage:
go test -race -coverprofile=coverage.out -covermode=atomic ./...
go tool cover -html=coverage.out -o coverage.html
@echo "Coverage report: coverage.html"
## lint: Run linter
lint:
golangci-lint run ./...
## fmt: Format code
fmt:
go fmt ./...
goimports -w .
## vet: Run go vet
vet:
go vet ./...
## run: Build and run
run: build
./$(BINARY_NAME)
## dev: Run in development mode
dev:
go run $(MAIN_PATH)
## clean: Clean build artifacts
clean:
rm -f $(BINARY_NAME) $(BINARY_NAME).exe
rm -f $(BINARY_NAME)-*
rm -f coverage.out coverage.html
rm -rf dist/
## deps: Download dependencies
deps:
go mod download
go mod tidy
## snapshot: Create GoReleaser snapshot
snapshot:
goreleaser release --snapshot --clean
## release: Create release with GoReleaser
release:
goreleaser release --clean

273
README.md Normal file
View File

@@ -0,0 +1,273 @@
<p align="center">
<img src="docs/assets/logo-placeholder.png" alt="DevPack Logo" width="200"/>
</p>
<h1 align="center">DevPack</h1>
<p align="center">
<strong>📦 开发环境打包迁移工具 — 一键打包,随处开发</strong>
</p>
<p align="center">
<a href="#功能特性">功能特性</a> •
<a href="#快速开始">快速开始</a> •
<a href="#使用示例">使用示例</a> •
<a href="docs/USER-GUIDE.md">用户手册</a> •
<a href="docs/DEVELOPMENT.md">开发指南</a> •
<a href="docs/CONTRIBUTING.md">贡献指南</a>
</p>
---
## 🎯 项目简介
**DevPack** 是一个用 Go 编写的跨平台开发环境打包迁移工具。它能够扫描、捕获并打包你当前电脑上的完整开发环境包括编程语言运行时、包管理器、IDE 配置、Shell 设置、环境变量等),生成一个可移植的 `.devpack` 文件,在另一台同系统的电脑上一键还原,让你在几分钟内恢复完整的开发环境。
### 为什么需要 DevPack
每个开发者都经历过这样的痛苦:
- 🖥️ **换新电脑** — 花一整天甚至几天重新搭建开发环境
- 🔧 **装了什么?** — 记不清装了哪些工具、配了哪些参数
- 👥 **团队协作** — 新成员入职需要漫长的环境配置过程
- 📋 **环境不一致** — "在我电脑上能跑"的经典问题
DevPack 就像给你的开发环境拍一张"快照",随时随地还原。
### DevPack vs Docker
| 特性 | DevPack | Docker |
|------|---------|--------|
| 目标 | 打包**宿主机开发环境** | 打包**应用运行环境** |
| 使用场景 | 电脑迁移、环境备份 | 应用部署、环境隔离 |
| 捕获内容 | IDE设置、Shell配置、工具链 | 应用代码、依赖、系统库 |
| 运行方式 | 直接还原到宿主机 | 运行在容器中 |
| 性能开销 | 无 | 有(虚拟化层) |
| 学习成本 | 低 | 中等 |
> 💡 DevPack 不是 Docker 的替代品而是补充。Docker 解决"应用如何部署"DevPack 解决"开发者如何迁移"。
---
## ✨ 功能特性
### 🔍 智能扫描
- 自动检测已安装的编程语言、框架、工具
- 识别包管理器及已安装的包Scoop、Chocolatey、Winget、Homebrew、APT 等)
- 捕获 IDE/编辑器配置VS Code 扩展与设置、JetBrains 设置等)
- 收集 Shell 配置PowerShell Profile、.bashrc、.zshrc 等)
### 📦 一键打包
- 生成可移植的 `.devpack` 打包文件
- 支持选择性打包(只打包你需要的部分)
- 敏感数据加密存储SSH 密钥、Token 等)
- 增量打包,节省空间
### 🚀 快速还原
- 自动依赖解析与安装顺序管理
- 冲突检测与智能合并
- 干运行模式(预览将做的更改)
- 回滚机制(还原失败可撤销)
### 🧩 插件化架构
- 内置丰富的采集器插件
- 支持自定义采集器开发
- 社区插件生态
### 📊 环境管理
- 命名配置文件Profile管理
- 环境差异对比
- 环境版本历史
---
## 📋 系统要求
| 操作系统 | 最低版本 | 架构 |
|---------|---------|------|
| Windows | 10 1903+ | x64, ARM64 |
| macOS | 11 Big Sur+ | x64, ARM64 (Apple Silicon) |
| Linux | Ubuntu 20.04+ / CentOS 8+ | x64, ARM64 |
---
## 🚀 快速开始
### 安装
**使用 Go 安装:**
```bash
go install github.com/user/devpack@latest
```
**Windows (Scoop)**
```powershell
scoop bucket add devpack https://github.com/user/devpack-bucket
scoop install devpack
```
**macOS (Homebrew)**
```bash
brew tap user/devpack
brew install devpack
```
**从 Release 下载:**
前往 [Releases](https://github.com/user/devpack/releases) 页面下载对应平台的二进制文件。
### 快速上手
```bash
# 1. 初始化(创建默认配置文件)
devpack init
# 2. 扫描当前环境
devpack scan
# 3. 打包开发环境
devpack capture --name "my-dev-env"
# 4. 导出为可移植文件
devpack export my-dev-env -o my-dev-env.devpack
# 5. 在新电脑上还原
devpack import my-dev-env.devpack
devpack restore my-dev-env
```
---
## 💻 使用示例
### 场景一:打包完整开发环境
```bash
# 扫描并预览将要打包的内容
devpack scan --verbose
# 创建完整环境快照
devpack capture --name "fullstack-2026" --all
# 导出
devpack export fullstack-2026 -o fullstack-2026.devpack
```
### 场景二:只打包特定工具链
```bash
# 只打包 Go + VS Code 相关配置
devpack capture --name "golang-env" \
--collectors runtime,editor \
--filter "runtime:go,editor:vscode"
```
### 场景三:团队环境标准化
```bash
# 团队 Lead 导出标准开发环境
devpack capture --name "team-standard" --profile team.yaml
devpack export team-standard -o team-standard.devpack
# 新成员导入并还原
devpack import team-standard.devpack
devpack restore team-standard --dry-run # 先预览
devpack restore team-standard # 确认后还原
```
### 场景四:对比两台电脑的环境差异
```bash
# 在电脑 A 上导出
devpack capture --name "pc-a"
devpack export pc-a -o pc-a.devpack
# 在电脑 B 上对比
devpack import pc-a.devpack
devpack diff pc-a --current
```
---
## 🏗️ 项目结构
```
DevPack/
├── cmd/ # CLI 入口与命令定义
│ └── devpack/
│ ├── main.go
│ └── commands/ # 各子命令实现
├── internal/ # 内部包(不对外暴露)
│ ├── collector/ # 采集器核心
│ │ ├── registry.go # 采集器注册中心
│ │ ├── runtime/ # 运行时采集器Go, Node, Python...
│ │ ├── editor/ # 编辑器采集器VS Code, JetBrains...
│ │ ├── shell/ # Shell 采集器
│ │ ├── package/ # 包管理器采集器
│ │ ├── git/ # Git 配置采集器
│ │ ├── env/ # 环境变量采集器
│ │ ├── font/ # 字体采集器
│ │ └── ssh/ # SSH/GPG 密钥采集器
│ ├── pack/ # 打包/解包引擎
│ ├── restore/ # 还原引擎
│ ├── profile/ # 配置文件管理
│ ├── diff/ # 环境差异对比
│ ├── crypto/ # 加密模块
│ ├── platform/ # 平台适配层
│ │ ├── windows/
│ │ ├── darwin/
│ │ └── linux/
│ └── config/ # 应用配置
├── pkg/ # 可对外使用的公共包
│ ├── manifest/ # Pack 清单格式定义
│ └── version/ # 版本信息
├── plugins/ # 外部插件目录
├── docs/ # 项目文档
│ ├── PRD.md # 产品需求文档
│ ├── ARCHITECTURE.md # 架构设计文档
│ ├── DEVELOPMENT.md # 开发指南
│ ├── API.md # API 设计文档
│ ├── ROADMAP.md # 开发路线图
│ ├── USER-GUIDE.md # 用户手册
│ └── CONTRIBUTING.md # 贡献指南
├── test/ # 集成测试
├── scripts/ # 构建与工具脚本
├── .github/ # GitHub Actions CI/CD
├── go.mod
├── go.sum
├── Makefile
├── LICENSE
└── README.md
```
---
## 📖 文档
| 文档 | 说明 |
|------|------|
| [产品需求文档 (PRD)](docs/PRD.md) | 产品定位、用户画像、功能需求 |
| [架构设计文档](docs/ARCHITECTURE.md) | 系统架构、核心模块、数据流 |
| [开发指南](docs/DEVELOPMENT.md) | 开发环境搭建、编码规范、测试 |
| [API 设计文档](docs/API.md) | CLI 命令、内部 API、Pack 格式规范 |
| [开发路线图](docs/ROADMAP.md) | 版本规划与里程碑 |
| [用户手册](docs/USER-GUIDE.md) | 安装、使用、常见问题 |
| [贡献指南](docs/CONTRIBUTING.md) | 如何参与项目开发 |
---
## 🤝 贡献
欢迎贡献代码、报告 Bug 或提出新功能建议!请查看 [贡献指南](docs/CONTRIBUTING.md) 了解详情。
---
## 📄 许可证
本项目采用 [MIT License](LICENSE) 开源。
---
<p align="center">
<sub>Built with ❤️ by DevPack Team</sub>
</p>

898
docs/API.md Normal file
View File

@@ -0,0 +1,898 @@
# DevPack API 设计文档
> 版本v1.0.0-draft
> 更新日期2026-03-03
> 作者DevPack Team
---
## 1. CLI 命令参考
### 1.1 命令总览
```
devpack [command] [subcommand] [flags]
Available Commands:
init 初始化 DevPack 配置
scan 扫描当前开发环境
capture 捕获环境并生成 Pack
restore 从 Pack 还原开发环境
diff 对比两个环境的差异
export 导出 Pack 为文件
import 导入 Pack 文件
list 列出 Pack 和 Profile
profile 管理配置文件
verify 验证 Pack 文件完整性
version 显示版本信息
help 帮助信息
Global Flags:
-v, --verbose 详细输出
-q, --quiet 静默模式
--log-level 日志级别 (trace|debug|info|warn|error)
--log-file 日志文件路径
--no-color 禁用彩色输出
--config 配置文件路径(默认 ~/.devpack/config.yaml
-h, --help 帮助信息
```
---
### 1.2 devpack init
初始化 DevPack创建配置目录和默认配置文件。
```
devpack init [flags]
Flags:
--profile <name> 创建并使用指定名称的 Profile
--template <tpl> 使用预设模板 (minimal|standard|full)
--force 覆盖已有配置
```
**示例:**
```bash
# 默认初始化
devpack init
# 使用模板初始化
devpack init --template standard
# 创建指定 Profile
devpack init --profile golang-dev
```
**行为:**
1. 创建 `~/.devpack/` 目录
2. 生成 `~/.devpack/config.yaml` 默认配置
3. 创建 `~/.devpack/profiles/` 目录
4. 创建 `~/.devpack/packs/` 目录
5. 创建 `~/.devpack/logs/` 目录
**输出示例:**
```
✓ Created ~/.devpack/config.yaml
✓ Created ~/.devpack/profiles/default.yaml
✓ DevPack initialized successfully!
Run 'devpack scan' to scan your current environment.
```
---
### 1.3 devpack scan
扫描当前系统的开发环境。
```
devpack scan [flags]
Flags:
-c, --collectors <list> 指定采集器(逗号分隔)
--category <cat> 按分类扫描 (runtime|package|editor|shell|git|env)
--profile <name> 使用指定 Profile 的扫描配置
-o, --output <format> 输出格式 (table|json|yaml) [默认: table]
--save <path> 保存扫描结果到文件
--detailed 显示详细信息
```
**示例:**
```bash
# 扫描所有
devpack scan
# 只扫描运行时
devpack scan --category runtime
# 扫描特定采集器
devpack scan -c go,node,vscode
# JSON 格式输出
devpack scan -o json
# 保存结果
devpack scan --save scan-result.json
```
**输出示例:**
```
🔍 Scanning development environment...
╔══════════════╤════════════════════╤═══════════╤═══════════════════╗
║ Category │ Name │ Version │ Details ║
╠══════════════╪════════════════════╪═══════════╪═══════════════════╣
║ Runtime │ Go │ 1.22.1 │ GOPATH: ~/go ║
║ Runtime │ Node.js │ 20.11.0 │ npm: 10.2.4 ║
║ Runtime │ Python │ 3.12.2 │ pip: 24.0 ║
║ Runtime │ Rust │ 1.76.0 │ rustup: 1.27.0 ║
╠══════════════╪════════════════════╪═══════════╪═══════════════════╣
║ Package Mgr │ Scoop │ - │ 42 packages ║
║ Package Mgr │ Winget │ - │ 15 packages ║
╠══════════════╪════════════════════╪═══════════╪═══════════════════╣
║ Editor │ VS Code │ 1.87.0 │ 35 extensions ║
║ Editor │ Neovim │ 0.9.5 │ 18 plugins ║
╠══════════════╪════════════════════╪═══════════╪═══════════════════╣
║ Shell │ PowerShell │ 7.4.1 │ 5 modules ║
║ Shell │ Windows Terminal │ 1.19 │ 3 profiles ║
╠══════════════╪════════════════════╪═══════════╪═══════════════════╣
║ Git │ Git │ 2.43.0 │ 12 aliases ║
╠══════════════╪════════════════════╪═══════════╪═══════════════════╣
║ Environment │ Environment Vars │ - │ 8 dev-related ║
╚══════════════╧════════════════════╧═══════════╧═══════════════════╝
Found 12 items across 6 categories.
Estimated pack size: ~12 MB
```
---
### 1.4 devpack capture
捕获当前环境并创建 Pack。
```
devpack capture [flags]
Flags:
-n, --name <name> Pack 名称(必需)
-d, --description <desc> Pack 描述
-p, --profile <name> 使用指定 Profile
-c, --collectors <list> 指定采集器(逗号分隔)
--filter <expr> 过滤表达式
--all 捕获所有可用采集器
--encrypt 加密敏感数据
--password <pwd> 加密密码(不建议命令行传入)
--exclude <patterns> 排除模式(逗号分隔)
--tag <tags> 标签(逗号分隔)
-y, --yes 跳过确认提示
```
**示例:**
```bash
# 使用默认 Profile 捕获
devpack capture --name "my-env-2026"
# 捕获所有内容
devpack capture --name "full-env" --all
# 使用指定 Profile
devpack capture --name "golang-env" --profile golang-dev
# 选择性捕获
devpack capture --name "vscode-only" -c vscode
# 加密捕获
devpack capture --name "secure-env" --all --encrypt
# 带过滤器
devpack capture --name "web-dev" \
-c go,node,vscode \
--filter "runtime:go,runtime:node,editor:vscode"
```
**输出示例:**
```
📦 Capturing environment "my-env-2026"...
Collecting:
✓ Go Runtime (1.22.1, 3 tools) [2.1 MB]
✓ Node.js Runtime (20.11.0, 12 global pkgs) [1.5 MB]
✓ VS Code (35 extensions, settings) [4.2 MB]
✓ PowerShell (profile, 5 modules) [0.3 MB]
✓ Scoop (42 packages) [0.1 MB]
✓ Git (config, 12 aliases) [0.01 MB]
✓ Environment Variables (8 variables) [0.001 MB]
⚠ SSH Keys skipped (use --encrypt)
Pack created: my-env-2026
Items: 7 collectors, 115 items
Size: 8.2 MB (compressed)
Pack ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Run 'devpack export my-env-2026 -o my-env-2026.devpack' to export.
```
---
### 1.5 devpack restore
从 Pack 还原开发环境。
```
devpack restore <pack-name> [flags]
Flags:
--dry-run 预览更改(不实际执行)
--conflict <strategy> 冲突处理策略 (skip|overwrite|merge|prompt|newest)
--no-rollback 不创建还原点
-c, --collectors <list> 只还原指定采集器
--exclude <list> 排除指定采集器
--password <pwd> 解密密码
--parallel <n> 并行还原数(默认: 4
-y, --yes 跳过确认提示
--force 强制还原(忽略平台不匹配)
```
**示例:**
```bash
# 干运行预览
devpack restore my-env-2026 --dry-run
# 正常还原
devpack restore my-env-2026
# 跳过冲突
devpack restore my-env-2026 --conflict skip
# 只还原编辑器配置
devpack restore my-env-2026 -c vscode
# 还原加密 Pack
devpack restore secure-env --password
```
**干运行输出示例:**
```
🔍 Dry Run — Restore Plan for "my-env-2026"
Source: DESKTOP-ABC123 (Windows 11, amd64)
Target: LAPTOP-XYZ789 (Windows 11, amd64) ✓ Compatible
Plan:
┌────────────────────┬────────────┬──────────────────────────────┐
│ Item │ Action │ Details │
├────────────────────┼────────────┼──────────────────────────────┤
│ Go 1.22.1 │ INSTALL │ Download from golang.org │
│ Node.js 20.11.0 │ SKIP │ Already installed (20.11.0) │
│ Python 3.12.2 │ UPGRADE │ Current: 3.11.7 → 3.12.2 │
│ VS Code Extensions │ INSTALL 12 │ 23 already present │
│ VS Code Settings │ MERGE │ Conflict in 3 settings │
│ PowerShell Profile │ OVERWRITE │ Backup: profile.bak │
│ Scoop Packages │ INSTALL 8 │ 34 already present │
│ Git Config │ MERGE │ Add 5 aliases │
│ Env Variables │ SET 3 │ GOPATH, GOROOT, NODE_HOME │
└────────────────────┴────────────┴──────────────────────────────┘
Install: 3 items | Upgrade: 1 | Merge: 2 | Skip: 1 | Overwrite: 1
⚠ This is a dry run. No changes were made.
Run without --dry-run to apply these changes.
```
---
### 1.6 devpack diff
对比两个环境的差异。
```
devpack diff <pack-name> [flags]
Flags:
--current 与当前环境对比
--with <pack-name> 与另一个 Pack 对比
-c, --collectors <list> 只对比指定采集器
-o, --output <format> 输出格式 (table|json|yaml)
```
**示例:**
```bash
# 与当前环境对比
devpack diff my-env-2026 --current
# 两个 Pack 对比
devpack diff env-v1 --with env-v2
```
---
### 1.7 devpack export / import
导出和导入 Pack 文件。
```
# 导出
devpack export <pack-name> [flags]
Flags:
-o, --output <path> 输出文件路径(默认: <pack-name>.devpack
# 导入
devpack import <file-path> [flags]
Flags:
-n, --name <name> 导入后的名称(默认使用 Pack 原名)
--verify 导入前验证文件完整性
```
---
### 1.8 devpack list
列出本地的 Pack 和 Profile。
```
devpack list [subcommand] [flags]
Subcommands:
packs 列出所有 Pack
profiles 列出所有 Profile
collectors 列出所有可用采集器
Flags:
-o, --output <format> 输出格式 (table|json|yaml)
--detailed 显示详细信息
```
---
### 1.9 devpack profile
管理 Profile 配置文件。
```
devpack profile [subcommand] [flags]
Subcommands:
create <name> 创建新 Profile
edit <name> 编辑 Profile
delete <name> 删除 Profile
show <name> 显示 Profile 内容
list 列出所有 Profile
use <name> 设置默认 Profile
```
---
### 1.10 devpack verify
验证 Pack 文件的完整性。
```
devpack verify <pack-name-or-file> [flags]
Flags:
--checksum 验证校验和
--structure 验证目录结构
--deep 深度验证(检查所有采集器数据)
```
---
## 2. 内部 API
### 2.1 Collector 接口
```go
package collector
import (
"context"
"time"
)
// Collector 所有采集器的统一接口
type Collector interface {
// 基本信息
Name() string
DisplayName() string
Description() string
Category() Category
// 可用性检查
IsAvailable(ctx context.Context) bool
// 核心操作
Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error)
Capture(ctx context.Context, targetDir string, opts CaptureOptions) error
Restore(ctx context.Context, sourceDir string, opts RestoreOptions) error
Verify(ctx context.Context) (*VerifyResult, error)
}
// ScanOptions 扫描选项
type ScanOptions struct {
Detailed bool // 是否返回详细信息
Filters map[string]string // 过滤条件
Timeout time.Duration // 超时时间
}
// CaptureOptions 捕获选项
type CaptureOptions struct {
IncludePatterns []string // 包含模式
ExcludePatterns []string // 排除模式
Encrypt bool // 是否加密
}
// RestoreOptions 还原选项
type RestoreOptions struct {
ConflictStrategy ConflictStrategy // 冲突策略
DryRun bool // 干运行
Force bool // 强制
}
// ScanResult 扫描结果
type ScanResult struct {
Collector string `json:"collector"`
Category Category `json:"category"`
Items []ScanItem `json:"items"`
Timestamp time.Time `json:"timestamp"`
Platform PlatformInfo `json:"platform"`
Errors []string `json:"errors,omitempty"`
}
// ScanItem 扫描项
type ScanItem struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
Path string `json:"path,omitempty"`
Type string `json:"type,omitempty"` // binary, config, package
Properties map[string]string `json:"properties,omitempty"`
Size int64 `json:"size,omitempty"`
Sensitive bool `json:"sensitive,omitempty"` // 是否包含敏感数据
Children []ScanItem `json:"children,omitempty"`
}
// VerifyResult 验证结果
type VerifyResult struct {
Success bool `json:"success"`
Items []VerifyItem `json:"items"`
}
type VerifyItem struct {
Name string `json:"name"`
Status string `json:"status"` // ok, missing, version_mismatch, error
Message string `json:"message,omitempty"`
}
```
### 2.2 Pack Engine API
```go
package pack
import (
"context"
"io"
"github.com/user/devpack/pkg/manifest"
)
// Packer 打包器
type Packer struct {
// ...
}
// NewPacker 创建打包器
func NewPacker(opts PackerOptions) *Packer
// PackerOptions 打包器选项
type PackerOptions struct {
Name string
Description string
Author string
Compression CompressionType // gzip, zstd, none
Encryption bool
Password string
}
// Pack 执行打包
func (p *Packer) Pack(ctx context.Context, sourceDir string, output io.Writer) (*manifest.Manifest, error)
// Unpacker 解包器
type Unpacker struct {
// ...
}
// NewUnpacker 创建解包器
func NewUnpacker() *Unpacker
// Unpack 执行解包
func (u *Unpacker) Unpack(ctx context.Context, input io.Reader, targetDir string) (*manifest.Manifest, error)
// Verify 验证 Pack 文件
func (u *Unpacker) Verify(ctx context.Context, input io.Reader) (*VerifyResult, error)
```
### 2.3 Restore Engine API
```go
package restore
import (
"context"
"github.com/user/devpack/internal/collector"
"github.com/user/devpack/pkg/manifest"
)
// Engine 还原引擎
type Engine struct {
registry *collector.Registry
platform platform.Platform
// ...
}
// NewEngine 创建还原引擎
func NewEngine(registry *collector.Registry, platform platform.Platform) *Engine
// Plan 生成还原计划
func (e *Engine) Plan(ctx context.Context, m *manifest.Manifest, opts PlanOptions) (*RestorePlan, error)
// Execute 执行还原计划
func (e *Engine) Execute(ctx context.Context, plan *RestorePlan) (*RestoreReport, error)
// Rollback 回滚还原
func (e *Engine) Rollback(ctx context.Context, restorePointID string) error
// RestorePlan 还原计划
type RestorePlan struct {
PackName string `json:"pack_name"`
Items []RestorePlanItem `json:"items"`
Conflicts []ConflictItem `json:"conflicts"`
TotalSteps int `json:"total_steps"`
}
// RestorePlanItem 还原计划项
type RestorePlanItem struct {
Collector string `json:"collector"`
ItemName string `json:"item_name"`
Action RestoreAction `json:"action"` // install, upgrade, merge, skip, overwrite
Current string `json:"current,omitempty"`
Target string `json:"target"`
Details string `json:"details,omitempty"`
Order int `json:"order"` // 执行顺序
}
// RestoreAction 还原动作
type RestoreAction string
const (
ActionInstall RestoreAction = "install"
ActionUpgrade RestoreAction = "upgrade"
ActionMerge RestoreAction = "merge"
ActionSkip RestoreAction = "skip"
ActionOverwrite RestoreAction = "overwrite"
)
// RestoreReport 还原报告
type RestoreReport struct {
Success int `json:"success"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
Items []ReportItem `json:"items"`
Duration time.Duration `json:"duration"`
RestorePointID string `json:"restore_point_id,omitempty"`
}
```
---
## 3. Pack 文件格式规范
### 3.1 文件结构
```
<name>.devpack # tar.gz 归档
├── manifest.json # Pack 清单(必需)
├── checksum.sha256 # 校验和文件(必需)
├── collectors/ # 采集器数据目录
│ ├── <category>/ # 分类目录
│ │ └── <collector-name>/ # 采集器目录
│ │ ├── metadata.json # 采集器元数据
│ │ └── data/ # 采集器数据
│ │ └── ... # 具体数据文件
│ └── ...
└── encrypted/ # 加密数据目录(可选)
└── <collector-name>/
└── data.enc # 加密数据
```
### 3.2 manifest.json 完整规范
```json
{
"version": "0.1.0",
"format_ver": "1",
"pack_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "my-dev-env",
"description": "My full development environment",
"author": "developer@example.com",
"created_at": "2026-03-03T10:30:00Z",
"tags": ["golang", "fullstack"],
"source": {
"hostname": "DESKTOP-ABC123",
"os": "windows",
"os_version": "10.0.22631",
"arch": "amd64",
"username": "developer",
"devpack_version": "0.1.0"
},
"collectors": [
{
"name": "go",
"category": "runtime",
"display_name": "Go Runtime",
"item_count": 4,
"data_path": "collectors/runtime/go",
"data_size": 2148576,
"checksum": "sha256:abc123..."
},
{
"name": "vscode",
"category": "editor",
"display_name": "Visual Studio Code",
"item_count": 36,
"data_path": "collectors/editor/vscode",
"data_size": 4412928,
"checksum": "sha256:def456..."
}
],
"encryption": {
"algorithm": "AES-256-GCM",
"kdf": "argon2id",
"kdf_params": {
"time": 1,
"memory": 65536,
"threads": 4
},
"encrypted_collectors": ["ssh"]
},
"checksum": "sha256:789abc..."
}
```
### 3.3 metadata.json 规范
每个采集器目录下的元数据文件:
```json
{
"collector": "go",
"category": "runtime",
"captured_at": "2026-03-03T10:30:00Z",
"platform": {
"os": "windows",
"arch": "amd64"
},
"items": [
{
"name": "go",
"version": "1.22.1",
"type": "binary",
"install_method": "direct",
"install_url": "https://go.dev/dl/go1.22.1.windows-amd64.msi",
"properties": {
"GOPATH": "C:\\Users\\dev\\go",
"GOROOT": "C:\\Program Files\\Go",
"GOPROXY": "https://goproxy.cn,direct"
}
},
{
"name": "gopls",
"version": "0.15.1",
"type": "tool",
"install_method": "go_install",
"install_command": "go install golang.org/x/tools/gopls@v0.15.1"
},
{
"name": "golangci-lint",
"version": "1.56.2",
"type": "tool",
"install_method": "go_install",
"install_command": "go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.2"
}
],
"files": [
{
"source": "data/go-env.json",
"description": "Go environment variables"
}
]
}
```
---
## 4. 配置文件规范
### 4.1 全局配置 (~/.devpack/config.yaml)
```yaml
# DevPack 全局配置
version: "1"
# 存储设置
storage:
packs_dir: "~/.devpack/packs" # Pack 存储目录
profiles_dir: "~/.devpack/profiles" # Profile 目录
logs_dir: "~/.devpack/logs" # 日志目录
temp_dir: "" # 临时目录(空则使用系统默认)
# 默认行为
defaults:
profile: "default" # 默认 Profile
compression: "gzip" # 压缩方式 (gzip|zstd|none)
conflict_strategy: "prompt" # 冲突策略
create_restore_point: true # 还原前创建还原点
parallel_restore: 4 # 并行还原数
# 日志设置
logging:
level: "info" # 日志级别
format: "text" # 日志格式 (text|json)
file: "~/.devpack/logs/devpack.log" # 日志文件
# 网络设置(用于下载安装器)
network:
timeout: 300 # 下载超时(秒)
proxy: "" # HTTP 代理
retries: 3 # 重试次数
# UI 设置
ui:
color: true # 彩色输出
progress_bar: true # 进度条
interactive: true # 交互式提示
# 安全设置
security:
default_encrypt: false # 默认是否加密
scan_for_secrets: true # 扫描敏感数据并警告
exclude_patterns: # 全局排除模式
- "*_KEY"
- "*_SECRET"
- "*_TOKEN"
- "*_PASSWORD"
```
### 4.2 Profile 配置文件规范
```yaml
# Profile 配置文件
name: "default"
description: "Default development environment profile"
version: "1"
collectors:
# 运行时采集器配置
runtime:
enabled: true
include: [] # 空列表 = 全部,非空 = 只包含列出的
exclude: [] # 排除列表
options: {} # 采集器特定选项
# 包管理器采集器配置
package:
enabled: true
include: []
exclude: []
options:
scoop:
include_buckets: true
exclude_packages: []
chocolatey:
exclude_packages: []
# 编辑器采集器配置
editor:
enabled: true
include: ["vscode"]
options:
vscode:
capture_extensions: true
capture_settings: true
capture_keybindings: true
capture_snippets: true
exclude_extensions: []
# Shell 采集器配置
shell:
enabled: true
include: []
options: {}
# Git 采集器配置
git:
enabled: true
options:
capture_config: true
capture_aliases: true
capture_hooks: false
# 环境变量采集器配置
env:
enabled: true
options:
include_patterns: []
exclude_patterns:
- "*_KEY"
- "*_SECRET"
# SSH/GPG 采集器配置
ssh:
enabled: false # 默认禁用
options:
encrypt: true # 必须加密
# 字体采集器配置
font:
enabled: false
options:
include_fonts: [] # 空 = 自动检测开发字体
# 还原选项
restore:
conflict_strategy: "prompt"
create_restore_point: true
dry_run_first: false
# 自定义钩子(高级)
hooks:
pre_capture: "" # 捕获前执行的命令
post_capture: "" # 捕获后执行的命令
pre_restore: "" # 还原前执行的命令
post_restore: "" # 还原后执行的命令
```
---
## 5. 退出码
| 退出码 | 含义 |
|--------|------|
| 0 | 成功 |
| 1 | 一般性错误 |
| 2 | 命令行参数错误 |
| 3 | 配置错误 |
| 10 | Pack 文件损坏或无效 |
| 11 | 平台不兼容 |
| 12 | 版本不兼容 |
| 20 | 还原失败 |
| 21 | 还原部分成功 |
| 22 | 冲突(需要用户介入) |
| 30 | 权限不足 |
| 40 | 网络错误 |
| 50 | 加密/解密错误 |
---
## 6. 环境变量
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `DEVPACK_HOME` | DevPack 主目录 | `~/.devpack` |
| `DEVPACK_LOG_LEVEL` | 日志级别 | `info` |
| `DEVPACK_LOG_FILE` | 日志文件路径 | `~/.devpack/logs/devpack.log` |
| `DEVPACK_CONFIG` | 配置文件路径 | `~/.devpack/config.yaml` |
| `DEVPACK_NO_COLOR` | 禁用彩色输出 | `false` |
| `DEVPACK_PROFILE` | 默认 Profile | `default` |
| `DEVPACK_HTTP_PROXY` | HTTP 代理 | - |

751
docs/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,751 @@
# DevPack 架构设计文档
> 版本v1.0.0-draft
> 更新日期2026-03-03
> 作者DevPack Team
---
## 1. 架构概览
### 1.1 设计原则
| 原则 | 说明 |
|------|------|
| **单一二进制** | 编译为无依赖的单一可执行文件 |
| **插件化** | 采集器通过接口抽象,支持扩展 |
| **安全优先** | 敏感数据加密,操作可回滚 |
| **平台适配** | 通过平台适配层处理 OS 差异 |
| **幂等性** | 同一 Pack 多次还原结果一致 |
| **最小侵入** | 只修改用户级配置,不修改系统级设置(除非明确授权) |
### 1.2 高层架构图
```
┌──────────────────────────────────────────────────────────────────┐
│ CLI Layer (Cobra) │
│ ┌──────┐ ┌───────┐ ┌─────────┐ ┌────────┐ ┌──────┐ ┌────────┐ │
│ │ init │ │ scan │ │ capture │ │restore │ │ diff │ │export/ │ │
│ │ │ │ │ │ │ │ │ │ │ │import │ │
│ └──┬───┘ └───┬───┘ └────┬────┘ └───┬────┘ └──┬───┘ └───┬────┘ │
└─────┼─────────┼──────────┼──────────┼─────────┼─────────┼──────┘
│ │ │ │ │ │
┌─────┴─────────┴──────────┴──────────┴─────────┴─────────┴──────┐
│ Core Engine Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ Collector │ │ Pack Engine │ │ Restore Engine │ │
│ │ Registry │ │ │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └────────────┬─────────────┘ │
│ │ │ │ │
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌────────────┴─────────────┐ │
│ │ Profile │ │ Crypto │ │ Dependency Resolver │ │
│ │ Manager │ │ Module │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────────┘
┌─────────────────────────┴───────────────────────────────────────┐
│ Platform Abstraction Layer │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Windows │ │ macOS │ │ Linux │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────┴───────────────────────────────────────┐
│ Collector Plugins │
│ ┌─────────┐ ┌─────────┐ ┌────────┐ ┌───────┐ ┌─────┐ ┌─────┐ │
│ │ Runtime │ │ Package │ │ Editor │ │ Shell │ │ Git │ │ Env │ │
│ └─────────┘ └─────────┘ └────────┘ └───────┘ └─────┘ └─────┘ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Font │ │ SSH/GPG │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 2. 核心模块设计
### 2.1 采集器系统 (Collector System)
采集器是 DevPack 的核心,负责扫描和收集环境信息。所有采集器实现统一的接口。
#### 2.1.1 Collector 接口
```go
// Collector 定义了所有采集器必须实现的接口
type Collector interface {
// Name 返回采集器名称(唯一标识)
Name() string
// DisplayName 返回采集器的显示名称
DisplayName() string
// Description 返回采集器的描述信息
Description() string
// Category 返回采集器所属分类
Category() Category
// IsAvailable 检测当前系统是否支持此采集器
IsAvailable(ctx context.Context) bool
// Scan 扫描当前环境,返回扫描结果
Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error)
// Capture 捕获环境数据,写入到指定目录
Capture(ctx context.Context, targetDir string, opts CaptureOptions) error
// Restore 从指定目录还原环境
Restore(ctx context.Context, sourceDir string, opts RestoreOptions) error
// Verify 验证还原后的环境是否正确
Verify(ctx context.Context) (*VerifyResult, error)
}
// Category 采集器分类
type Category string
const (
CategoryRuntime Category = "runtime" // 编程语言运行时
CategoryPackage Category = "package" // 包管理器
CategoryEditor Category = "editor" // 编辑器/IDE
CategoryShell Category = "shell" // Shell 配置
CategoryGit Category = "git" // Git 配置
CategoryEnv Category = "env" // 环境变量
CategoryFont Category = "font" // 字体
CategorySSH Category = "ssh" // SSH/GPG 密钥
CategoryCustom Category = "custom" // 自定义
)
// ScanResult 扫描结果
type ScanResult struct {
Collector string `json:"collector"`
Category Category `json:"category"`
Items []ScanItem `json:"items"`
Timestamp time.Time `json:"timestamp"`
Platform PlatformInfo `json:"platform"`
}
// ScanItem 单个扫描项
type ScanItem struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
Path string `json:"path,omitempty"`
Properties map[string]string `json:"properties,omitempty"`
Size int64 `json:"size,omitempty"`
Children []ScanItem `json:"children,omitempty"`
}
```
#### 2.1.2 采集器注册中心
```go
// Registry 采集器注册中心
type Registry struct {
collectors map[string]Collector
mu sync.RWMutex
}
// Register 注册一个采集器
func (r *Registry) Register(c Collector) error
// Get 获取指定名称的采集器
func (r *Registry) Get(name string) (Collector, bool)
// List 列出所有已注册的采集器
func (r *Registry) List() []Collector
// ListByCategory 按分类列出采集器
func (r *Registry) ListByCategory(cat Category) []Collector
// ScanAll 使用所有可用的采集器扫描
func (r *Registry) ScanAll(ctx context.Context, opts ScanOptions) ([]*ScanResult, error)
```
#### 2.1.3 内置采集器实现示例
以 Go 运行时采集器为例:
```go
type GoCollector struct{}
func (c *GoCollector) Name() string { return "go" }
func (c *GoCollector) DisplayName() string { return "Go Runtime" }
func (c *GoCollector) Category() Category { return CategoryRuntime }
func (c *GoCollector) Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error) {
result := &ScanResult{
Collector: c.Name(),
Category: c.Category(),
}
// 检测 Go 版本
version, err := exec.CommandContext(ctx, "go", "version").Output()
// ... 解析版本信息
// 获取 go env 信息
envJSON, err := exec.CommandContext(ctx, "go", "env", "-json").Output()
// ... 解析环境变量
// 获取全局安装的工具
// ... 扫描 GOPATH/bin
return result, nil
}
```
### 2.2 打包引擎 (Pack Engine)
#### 2.2.1 Pack 文件格式
`.devpack` 文件本质上是一个经过组织的 tar.gz 归档文件:
```
my-env.devpack (tar.gz)
├── manifest.json # Pack 清单(元数据)
├── checksum.sha256 # 文件校验和
├── collectors/ # 各采集器数据
│ ├── runtime/
│ │ ├── go/
│ │ │ ├── metadata.json # Go 环境元数据
│ │ │ └── data/ # Go 相关数据文件
│ │ ├── node/
│ │ │ ├── metadata.json
│ │ │ └── data/
│ │ └── python/
│ │ ├── metadata.json
│ │ └── data/
│ ├── editor/
│ │ └── vscode/
│ │ ├── metadata.json
│ │ ├── extensions.json # 扩展列表
│ │ └── data/ # 设置文件
│ │ ├── settings.json
│ │ ├── keybindings.json
│ │ └── snippets/
│ ├── shell/
│ │ ├── powershell/
│ │ │ ├── metadata.json
│ │ │ └── data/
│ │ └── bash/
│ │ ├── metadata.json
│ │ └── data/
│ ├── package/
│ │ └── scoop/
│ │ ├── metadata.json
│ │ └── data/
│ ├── git/
│ │ ├── metadata.json
│ │ └── data/
│ └── env/
│ ├── metadata.json
│ └── data/
└── encrypted/ # 加密数据(可选)
└── ssh/
└── data.enc # 加密的 SSH 密钥
```
#### 2.2.2 Manifest 结构
```go
// Manifest Pack 清单
type Manifest struct {
// 元信息
Version string `json:"version"` // DevPack 版本
FormatVer string `json:"format_ver"` // Pack 格式版本
Name string `json:"name"` // Pack 名称
Description string `json:"description"` // 描述
Author string `json:"author"` // 作者
CreatedAt time.Time `json:"created_at"` // 创建时间
PackID string `json:"pack_id"` // 唯一 ID (UUID)
// 源环境信息
Source SourceInfo `json:"source"`
// 采集器数据索引
Collectors []CollectorEntry `json:"collectors"`
// 加密信息
Encryption *EncryptionInfo `json:"encryption,omitempty"`
// 校验信息
Checksum string `json:"checksum"` // 整包校验和
}
// SourceInfo 源环境信息
type SourceInfo struct {
Hostname string `json:"hostname"`
OS string `json:"os"` // windows, darwin, linux
OSVersion string `json:"os_version"`
Arch string `json:"arch"` // amd64, arm64
Username string `json:"username"`
}
// CollectorEntry 采集器条目
type CollectorEntry struct {
Name string `json:"name"`
Category Category `json:"category"`
ItemCount int `json:"item_count"`
DataPath string `json:"data_path"`
DataSize int64 `json:"data_size"`
Checksum string `json:"checksum"`
}
```
#### 2.2.3 打包流程
```
用户执行 capture 命令
┌─────────────────┐
│ 加载 Profile │ ─── 确定需要运行哪些采集器
└────────┬────────┘
┌─────────────────┐
│ 运行采集器 │ ─── 并行扫描 → 收集数据 → 写入临时目录
└────────┬────────┘
┌─────────────────┐
│ 生成 Manifest │ ─── 创建清单文件、计算校验和
└────────┬────────┘
┌─────────────────┐
│ 加密敏感数据 │ ─── 对标记为敏感的数据进行加密(可选)
└────────┬────────┘
┌─────────────────┐
│ 压缩归档 │ ─── tar.gz 打包
└────────┬────────┘
┌─────────────────┐
│ 输出 .devpack │ ─── 写入最终文件
└─────────────────┘
```
### 2.3 还原引擎 (Restore Engine)
#### 2.3.1 还原流程
```
用户执行 restore 命令
┌─────────────────┐
│ 校验 Pack 文件 │ ─── 检查完整性、版本兼容性
└────────┬────────┘
┌─────────────────┐
│ 解析 Manifest │ ─── 读取清单,确定还原内容
└────────┬────────┘
┌─────────────────┐
│ 平台兼容检查 │ ─── 确认目标平台与源平台匹配
└────────┬────────┘
┌─────────────────┐
│ 冲突检测 │ ─── 检测与现有环境的冲突
└────────┬────────┘
┌─────────────────────┐
│ 创建还原点(可选) │ ─── 备份当前环境用于回滚
└────────┬────────────┘
┌─────────────────┐
│ 依赖排序 │ ─── 按依赖关系排序安装顺序
└────────┬────────┘
┌─────────────────┐ ┌──────────────┐
│ 执行还原 │ ──▶ │ 逐项安装/配置 │
└────────┬────────┘ └──────────────┘
┌─────────────────┐
│ 验证还原结果 │ ─── 运行各采集器的验证逻辑
└────────┬────────┘
┌─────────────────┐
│ 生成还原报告 │ ─── 成功/失败/跳过的项目清单
└─────────────────┘
```
#### 2.3.2 冲突处理策略
```go
// ConflictStrategy 冲突处理策略
type ConflictStrategy int
const (
ConflictSkip ConflictStrategy = iota // 跳过(保留现有)
ConflictOverwrite // 覆盖(使用 Pack 中的)
ConflictMerge // 合并
ConflictPrompt // 询问用户
ConflictNewest // 使用较新版本
)
// ConflictItem 冲突项
type ConflictItem struct {
Collector string
ItemName string
CurrentVer string
PackVer string
Type ConflictType // VersionMismatch, AlreadyExists, DependencyConflict
}
```
### 2.4 配置文件管理 (Profile Manager)
#### 2.4.1 Profile 格式
使用 YAML 格式定义 Profile
```yaml
# ~/.devpack/profiles/golang-dev.yaml
name: golang-dev
description: "Go 全栈开发环境"
version: "1.0"
# 采集器配置
collectors:
runtime:
enabled: true
include:
- go
- node
exclude: []
options:
go:
capture_gopath_bin: true # 捕获 GOPATH/bin 下的工具
capture_go_env: true # 捕获 go env 配置
editor:
enabled: true
include:
- vscode
options:
vscode:
capture_extensions: true
capture_settings: true
capture_keybindings: true
capture_snippets: true
# 排除的扩展
exclude_extensions:
- "ms-vsliveshare.vsliveshare"
shell:
enabled: true
include:
- powershell
- bash
package:
enabled: true
include:
- scoop
git:
enabled: true
options:
capture_aliases: true
capture_hooks: false
env:
enabled: true
options:
# 只捕获匹配的环境变量
include_patterns:
- "GOPATH"
- "GOROOT"
- "PATH"
- "NODE_*"
exclude_patterns:
- "*_KEY"
- "*_SECRET"
- "*_TOKEN"
ssh:
enabled: false
# 还原选项
restore:
conflict_strategy: prompt # skip, overwrite, merge, prompt, newest
create_restore_point: true
dry_run_first: false
```
### 2.5 平台适配层 (Platform Abstraction Layer)
```go
// Platform 平台适配接口
type Platform interface {
// OS 返回操作系统标识
OS() string
// Arch 返回架构标识
Arch() string
// HomeDir 返回用户主目录
HomeDir() string
// ConfigDir 返回配置文件目录
ConfigDir() string
// DataDir 返回数据目录
DataDir() string
// GetEnvVar 获取环境变量
GetEnvVar(key string) string
// SetEnvVar 设置用户级环境变量
SetEnvVar(key, value string) error
// AddToPath 添加目录到 PATH
AddToPath(dir string) error
// IsAdmin 是否以管理员身份运行
IsAdmin() bool
// PackageManagers 返回可用的包管理器
PackageManagers() []string
// DefaultShell 返回默认 Shell
DefaultShell() string
// InstallFont 安装字体文件
InstallFont(fontPath string) error
// RunAsAdmin 以管理员权限运行命令
RunAsAdmin(cmd string, args ...string) error
}
```
各平台实现:
```go
// Windows 平台实现
type WindowsPlatform struct{}
func (p *WindowsPlatform) OS() string { return "windows" }
func (p *WindowsPlatform) HomeDir() string { return os.Getenv("USERPROFILE") }
func (p *WindowsPlatform) ConfigDir() string { return os.Getenv("APPDATA") }
func (p *WindowsPlatform) DefaultShell() string { return "powershell" }
func (p *WindowsPlatform) PackageManagers() []string {
var pms []string
if _, err := exec.LookPath("scoop"); err == nil { pms = append(pms, "scoop") }
if _, err := exec.LookPath("choco"); err == nil { pms = append(pms, "chocolatey") }
if _, err := exec.LookPath("winget"); err == nil { pms = append(pms, "winget") }
return pms
}
// macOS 平台实现
type DarwinPlatform struct{}
// Linux 平台实现
type LinuxPlatform struct{}
```
### 2.6 加密模块 (Crypto Module)
```go
// Encryptor 加密器接口
type Encryptor interface {
// Encrypt 使用密码加密数据
Encrypt(data []byte, password string) ([]byte, error)
// Decrypt 使用密码解密数据
Decrypt(encrypted []byte, password string) ([]byte, error)
}
// AESGCMEncryptor AES-256-GCM 加密实现
type AESGCMEncryptor struct{}
// 密钥派生使用 Argon2id
func deriveKey(password string, salt []byte) []byte {
return argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
}
```
---
## 3. 数据流
### 3.1 扫描 → 打包数据流
```
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐
│ 用户系统 │ ──▶ │ Collectors │ ──▶ │ ScanResult │ ──▶ │ 临时目录 │
│ (实际环境) │ │ (扫描采集) │ │ (结构化数据)│ │ (文件数据)│
└────────────┘ └────────────┘ └────────────┘ └────┬─────┘
┌────────────┐ ┌────────────┐ │
│ .devpack │ ◀── │ Pack 引擎 │ ◀────────┘
│ (归档文件) │ │ (压缩打包) │
└────────────┘ └────────────┘
```
### 3.2 导入 → 还原数据流
```
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐
│ .devpack │ ──▶ │ 解压校验 │ ──▶ │ Manifest │ ──▶ │ 还原计划 │
│ (归档文件) │ │ │ │ 解析 │ │ 生成 │
└────────────┘ └────────────┘ └────────────┘ └────┬─────┘
┌────────────┐ ┌────────────┐ │
│ 目标系统 │ ◀── │ Restore │ ◀────────┘
│ (已还原) │ │ Engine │
└────────────┘ └────────────┘
```
---
## 4. 技术选型
### 4.1 核心依赖
| 依赖 | 用途 | 选型理由 |
|------|------|---------|
| [cobra](https://github.com/spf13/cobra) | CLI 框架 | Go 生态最流行的 CLI 框架 |
| [viper](https://github.com/spf13/viper) | 配置管理 | 支持多种配置格式 |
| [zerolog](https://github.com/rs/zerolog) | 日志 | 高性能结构化日志 |
| [color](https://github.com/fatih/color) | 终端着色 | 美化 CLI 输出 |
| [progressbar](https://github.com/schollz/progressbar) | 进度条 | 长时操作反馈 |
| [survey](https://github.com/AlecAivazis/survey) | 交互式提示 | 用户交互 |
| [archiver](https://github.com/mholt/archiver) | 压缩归档 | 支持多种归档格式 |
| [go-yaml](https://github.com/go-yaml/yaml) | YAML 解析 | Profile 文件解析 |
| [uuid](https://github.com/google/uuid) | UUID 生成 | Pack ID 生成 |
| [golang.org/x/crypto](https://golang.org/x/crypto) | 加密库 | Argon2, AES |
### 4.2 构建工具
| 工具 | 用途 |
|------|------|
| Go 1.22+ | 编程语言 |
| Make / Task | 构建脚本 |
| GoReleaser | 多平台发布 |
| golangci-lint | 代码检查 |
| GitHub Actions | CI/CD |
---
## 5. 错误处理策略
### 5.1 错误分级
```go
// ErrorLevel 错误级别
type ErrorLevel int
const (
ErrorFatal ErrorLevel = iota // 致命错误,必须终止
ErrorCritical // 严重错误,当前采集器失败
ErrorWarning // 警告,可继续但需告知用户
ErrorInfo // 信息,记录但不影响流程
)
// CollectorError 采集器错误
type CollectorError struct {
Collector string
Level ErrorLevel
Message string
Cause error
Hint string // 给用户的建议
}
```
### 5.2 错误处理原则
1. **采集阶段** — 单个采集器失败不影响其他采集器
2. **打包阶段** — 失败的采集器数据不包含在 Pack 中,但 Pack 仍然生成
3. **还原阶段** — 根据策略决定是继续还是终止
4. **所有操作** — 详细日志记录,用户友好的错误信息
---
## 6. 安全设计
### 6.1 敏感数据识别
| 数据类型 | 敏感级别 | 处理方式 |
|---------|---------|---------|
| SSH 私钥 | 高 | AES-256 加密 |
| GPG 私钥 | 高 | AES-256 加密 |
| API Token | 高 | AES-256 加密或排除 |
| 环境变量中的密码 | 高 | 默认排除 |
| Git 凭证 | 高 | 默认排除 |
| IDE 设置 | 低 | 明文存储 |
| Shell 配置 | 中 | 扫描并警告内嵌 Token |
### 6.2 加密流程
```
用户密码 ──▶ Argon2id ──▶ 派生密钥 (256-bit)
敏感数据 ──▶ AES-256-GCM 加密 ──▶ 加密数据 + Nonce + Salt
存入 encrypted/ 目录
```
---
## 7. 可扩展性设计
### 7.1 插件系统(远期规划)
未来计划支持外部插件:
```go
// Plugin 外部插件接口
type Plugin interface {
Collector
// PluginInfo 返回插件元信息
PluginInfo() PluginMeta
}
type PluginMeta struct {
Name string `json:"name"`
Version string `json:"version"`
Author string `json:"author"`
MinDevPack string `json:"min_devpack_version"`
}
```
插件发现机制:
1. `~/.devpack/plugins/` 目录下的可执行文件
2. 通过 Go Plugin 机制加载 `.so` / `.dll`
3. 通过 gRPC/JSON-RPC 与外部进程通信(推荐,跨语言)
---
## 8. 测试策略
### 8.1 测试层级
| 层级 | 范围 | 工具 |
|------|------|------|
| 单元测试 | 各模块独立逻辑 | Go testing + testify |
| 集成测试 | 模块间协作 | Go testing |
| 端到端测试 | 完整的 capture → restore 流程 | 脚本 + Docker/VM |
| 平台测试 | 各操作系统适配 | GitHub Actions Matrix |
### 8.2 测试环境
- CI 环境使用 GitHub Actions 矩阵构建Windows/macOS/Linux
- 端到端测试使用虚拟机或容器模拟真实环境
- Mock 框架用于隔离外部依赖(如包管理器)

196
docs/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,196 @@
# DevPack 贡献指南
> 感谢你对 DevPack 的兴趣!我们欢迎各种形式的贡献。
---
## 如何贡献
### 🐛 报告 Bug
1. 搜索 [已有的 Issues](https://github.com/user/devpack/issues),确认没有重复
2. 创建新 Issue包含以下信息
- DevPack 版本(`devpack version`
- 操作系统和版本
- 复现步骤
- 期望行为 vs 实际行为
- 日志信息(`--log-level debug`
### 💡 功能建议
1. 搜索已有 Issues确认没有类似建议
2. 创建 Feature Request Issue
3. 描述使用场景和期望行为
4. 解释为什么这个功能对用户有价值
### 🔧 提交代码
#### 环境准备
```bash
# 1. Fork 项目
# 2. Clone 你的 Fork
git clone https://github.com/YOUR_USERNAME/devpack.git
cd devpack
# 3. 添加上游仓库
git remote add upstream https://github.com/user/devpack.git
# 4. 安装依赖
go mod download
# 5. 安装开发工具
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# 6. 验证环境
make test
make lint
```
#### 开发流程
```bash
# 1. 同步上游
git fetch upstream
git checkout develop
git merge upstream/develop
# 2. 创建分支
git checkout -b feature/your-feature
# 3. 编码
# ... 编写代码和测试
# 4. 运行检查
make fmt # 格式化代码
make lint # 代码检查
make test # 运行测试
# 5. 提交
git add .
git commit -m "feat(scope): description"
# 6. 推送
git push origin feature/your-feature
# 7. 创建 Pull Request
```
#### Pull Request 规范
- **标题:** 遵循 Conventional Commits 格式
- **描述:** 说明改了什么、为什么改、怎么测试的
- **关联 Issue** 如果有对应的 Issue请关联
- **测试:** 确保新代码有对应的测试
- **文档:** 如果改变了用户可见行为,更新对应文档
#### PR 模板
```markdown
## 描述
简述这个 PR 做了什么。
## 改动类型
- [ ] Bug fix
- [ ] New feature
- [ ] Refactoring
- [ ] Documentation
- [ ] Test
## 关联 Issue
Fixes #123
## 测试
描述如何测试这个改动。
## 检查清单
- [ ] 代码通过 `make lint`
- [ ] 添加了对应的单元测试
- [ ] 所有测试通过 `make test`
- [ ] 更新了相关文档
```
---
## 编码规范
### Go 代码规范
- 遵循 [Effective Go](https://golang.org/doc/effective_go)
- 使用 `gofmt` 格式化代码
- 通过 `golangci-lint` 检查
- 公开 API 必须有文档注释
- 错误必须包含上下文信息
### 提交规范
使用 [Conventional Commits](https://www.conventionalcommits.org/)
```
feat(collector): add Docker runtime collector
fix(restore): handle version conflict correctly
docs(readme): update installation instructions
test(pack): add pack/unpack round-trip test
refactor(platform): simplify Windows adapter
chore(ci): add macOS build matrix
```
### 测试规范
- 所有新功能必须有单元测试
- 测试文件与源码在同一包下
- 使用 `testify` 断言库
- 平台特定逻辑需要条件跳过:
```go
func TestWindowsSpecific(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Windows-only test")
}
// ...
}
```
---
## 添加新采集器
如果你想为 DevPack 添加一个新的采集器,请参考以下步骤:
1.`internal/collector/` 下创建新的包
2. 实现 `Collector` 接口的所有方法
3.`registry.go` 中注册
4. 编写单元测试
5. 更新文档PRD.md 和 USER-GUIDE.md
详细指南请参考 [开发指南 - 添加新采集器](DEVELOPMENT.md#5-添加新采集器指南)。
---
## 社区准则
- 友善和尊重
- 建设性的讨论
- 接受不同意见
- 关注事实和技术
---
## 获取帮助
- 📖 [用户手册](USER-GUIDE.md)
- 🏗️ [架构设计](ARCHITECTURE.md)
- 💻 [开发指南](DEVELOPMENT.md)
- ❓ [GitHub Discussions](https://github.com/user/devpack/discussions)
---
## 许可证
贡献的代码将按照项目的 [MIT License](../LICENSE) 发布。

638
docs/DEVELOPMENT.md Normal file
View File

@@ -0,0 +1,638 @@
# DevPack 开发指南
> 版本v1.0.0-draft
> 更新日期2026-03-03
> 作者DevPack Team
---
## 1. 开发环境搭建
### 1.1 前置要求
| 工具 | 最低版本 | 安装方式 |
|------|---------|---------|
| Go | 1.22+ | [golang.org](https://golang.org/dl/) |
| Git | 2.30+ | [git-scm.com](https://git-scm.com/) |
| Make | 3.81+ | 系统包管理器Windows 推荐用 `choco install make` |
| golangci-lint | 1.55+ | `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` |
### 1.2 获取代码
```bash
git clone https://github.com/user/devpack.git
cd devpack
```
### 1.3 安装依赖
```bash
go mod download
```
### 1.4 验证环境
```bash
# 运行测试
go test ./...
# 构建
go build -o devpack ./cmd/devpack/
# 代码检查
golangci-lint run
```
---
## 2. 项目结构详解
```
DevPack/
├── cmd/ # 应用入口
│ └── devpack/
│ ├── main.go # 主入口
│ └── commands/ # CLI 命令
│ ├── root.go # 根命令
│ ├── init.go # devpack init
│ ├── scan.go # devpack scan
│ ├── capture.go # devpack capture
│ ├── restore.go # devpack restore
│ ├── diff.go # devpack diff
│ ├── export.go # devpack export
│ ├── import.go # devpack import
│ ├── list.go # devpack list
│ ├── profile.go # devpack profile
│ └── version.go # devpack version
├── internal/ # 内部包
│ ├── collector/ # 采集器核心
│ │ ├── collector.go # Collector 接口定义
│ │ ├── registry.go # 采集器注册中心
│ │ ├── options.go # 扫描/捕获/还原选项
│ │ ├── result.go # 结果类型定义
│ │ │
│ │ ├── runtime/ # 运行时采集器
│ │ │ ├── go.go # Go 采集器
│ │ │ ├── node.go # Node.js 采集器
│ │ │ ├── python.go # Python 采集器
│ │ │ ├── java.go # Java 采集器
│ │ │ ├── rust.go # Rust 采集器
│ │ │ ├── dotnet.go # .NET 采集器
│ │ │ └── runtime_test.go # 运行时采集器测试
│ │ │
│ │ ├── editor/ # 编辑器采集器
│ │ │ ├── vscode.go # VS Code 采集器
│ │ │ ├── jetbrains.go # JetBrains 采集器
│ │ │ ├── vim.go # Vim/Neovim 采集器
│ │ │ └── editor_test.go
│ │ │
│ │ ├── shell/ # Shell 采集器
│ │ │ ├── powershell.go # PowerShell 采集器
│ │ │ ├── bash.go # Bash 采集器
│ │ │ ├── zsh.go # Zsh 采集器
│ │ │ ├── fish.go # Fish 采集器
│ │ │ ├── terminal.go # 终端模拟器配置Windows Terminal 等)
│ │ │ └── shell_test.go
│ │ │
│ │ ├── pkgmgr/ # 包管理器采集器
│ │ │ ├── scoop.go # Scoop 采集器
│ │ │ ├── chocolatey.go # Chocolatey 采集器
│ │ │ ├── winget.go # Winget 采集器
│ │ │ ├── homebrew.go # Homebrew 采集器
│ │ │ ├── apt.go # APT 采集器
│ │ │ └── pkgmgr_test.go
│ │ │
│ │ ├── git/ # Git 配置采集器
│ │ │ ├── gitconfig.go
│ │ │ └── git_test.go
│ │ │
│ │ ├── env/ # 环境变量采集器
│ │ │ ├── envvar.go
│ │ │ └── env_test.go
│ │ │
│ │ ├── font/ # 字体采集器
│ │ │ ├── font.go
│ │ │ └── font_test.go
│ │ │
│ │ └── ssh/ # SSH/GPG 采集器
│ │ ├── ssh.go
│ │ └── ssh_test.go
│ │
│ ├── pack/ # 打包引擎
│ │ ├── packer.go # 打包器
│ │ ├── unpacker.go # 解包器
│ │ ├── format.go # Pack 格式定义
│ │ └── pack_test.go
│ │
│ ├── restore/ # 还原引擎
│ │ ├── engine.go # 还原核心逻辑
│ │ ├── planner.go # 还原计划生成
│ │ ├── conflict.go # 冲突检测与处理
│ │ ├── rollback.go # 回滚管理
│ │ ├── verifier.go # 还原验证
│ │ └── restore_test.go
│ │
│ ├── profile/ # 配置文件管理
│ │ ├── profile.go # Profile 定义与操作
│ │ ├── parser.go # YAML 解析
│ │ └── profile_test.go
│ │
│ ├── diff/ # 差异对比
│ │ ├── differ.go # 差异计算
│ │ ├── renderer.go # 差异展示
│ │ └── diff_test.go
│ │
│ ├── crypto/ # 加密模块
│ │ ├── aesgcm.go # AES-256-GCM 实现
│ │ ├── argon2.go # Argon2id 密钥派生
│ │ └── crypto_test.go
│ │
│ ├── platform/ # 平台适配层
│ │ ├── platform.go # Platform 接口
│ │ ├── detect.go # 平台检测
│ │ ├── windows.go # Windows 实现
│ │ ├── darwin.go # macOS 实现
│ │ ├── linux.go # Linux 实现
│ │ └── platform_test.go
│ │
│ ├── config/ # 应用配置
│ │ ├── config.go # 全局配置
│ │ └── paths.go # 路径管理
│ │
│ ├── ui/ # 终端 UI
│ │ ├── printer.go # 格式化输出
│ │ ├── progress.go # 进度条
│ │ ├── table.go # 表格输出
│ │ └── prompt.go # 用户交互
│ │
│ └── logger/ # 日志
│ └── logger.go # 日志初始化
├── pkg/ # 公共包
│ ├── manifest/ # Pack 清单
│ │ ├── manifest.go # Manifest 结构定义
│ │ └── validate.go # Manifest 验证
│ └── version/ # 版本信息
│ └── version.go # 版本号管理
├── plugins/ # 外部插件目录
│ └── README.md # 插件开发指南
├── test/ # 集成与端到端测试
│ ├── e2e/ # 端到端测试
│ │ ├── capture_restore_test.go
│ │ └── testdata/ # 测试数据
│ └── fixtures/ # 测试 fixtures
│ └── sample-pack/
├── scripts/ # 脚本
│ ├── build.sh # 构建脚本
│ ├── install.sh # 安装脚本
│ └── release.sh # 发布脚本
├── .github/
│ └── workflows/
│ ├── ci.yml # CI 流水线
│ └── release.yml # 发布流水线
├── .golangci.yml # Lint 配置
├── .gitignore
├── go.mod
├── go.sum
├── Makefile
├── LICENSE
└── README.md
```
---
## 3. 编码规范
### 3.1 Go 编码规范
遵循 [Effective Go](https://golang.org/doc/effective_go) 和 [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)。
#### 命名规范
```go
// ✅ 包名:小写单词,不用下划线
package collector
package pkgmgr
// ✅ 接口名:动词或名词,单方法接口用 -er 后缀
type Collector interface { ... }
type Scanner interface { ... }
// ✅ 导出函数/方法:大驼峰
func NewRegistry() *Registry { ... }
func (r *Registry) Register(c Collector) error { ... }
// ✅ 非导出函数/方法:小驼峰
func parseVersion(raw string) (string, error) { ... }
// ✅ 常量:大驼峰(导出)或小驼峰(非导出)
const DefaultTimeout = 30 * time.Second
const maxRetries = 3
// ✅ 错误变量Err 前缀
var ErrCollectorNotFound = errors.New("collector not found")
var ErrPackCorrupted = errors.New("pack file corrupted")
```
#### 错误处理
```go
// ✅ 使用 fmt.Errorf 包装错误,提供上下文
func (c *GoCollector) Scan(ctx context.Context) (*ScanResult, error) {
version, err := c.detectVersion(ctx)
if err != nil {
return nil, fmt.Errorf("go collector: detect version: %w", err)
}
// ...
}
// ✅ 自定义错误类型用于需要类型判断的场景
type CollectorError struct {
Collector string
Op string
Err error
}
func (e *CollectorError) Error() string {
return fmt.Sprintf("%s: %s: %v", e.Collector, e.Op, e.Err)
}
func (e *CollectorError) Unwrap() error {
return e.Err
}
```
#### Context 使用
```go
// ✅ 第一个参数传递 context
func (c *GoCollector) Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error) {
// 使用 context 控制超时
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "go", "version")
// ...
}
```
### 3.2 项目约定
| 约定 | 说明 |
|------|------|
| 文件命名 | 小写 + 下划线:`go_collector.go`, `scan_result.go` |
| 测试文件 | `*_test.go` 放在同一包下 |
| 平台特定 | 使用 Go build tag`//go:build windows` |
| 依赖注入 | 通过接口 + 构造函数实现 |
| 配置 | 使用 Viper支持文件 + 环境变量 + 命令行参数 |
| 日志 | 使用 zerolog结构化日志 |
### 3.3 Git 提交规范
使用 [Conventional Commits](https://www.conventionalcommits.org/)
```
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
```
**类型 (type)**
| Type | 说明 |
|------|------|
| `feat` | 新功能 |
| `fix` | Bug 修复 |
| `docs` | 文档 |
| `style` | 代码格式 |
| `refactor` | 重构 |
| `perf` | 性能优化 |
| `test` | 测试 |
| `chore` | 构建/工具 |
**Scope 示例:** `collector`, `pack`, `restore`, `cli`, `platform`, `crypto`
**示例:**
```
feat(collector): add VS Code extension collector
Scan and capture VS Code extensions list and settings.
Closes #42
```
---
## 4. 开发工作流
### 4.1 分支策略
```
main (稳定发布)
├── develop (开发主干)
│ │
│ ├── feature/collector-vscode (功能分支)
│ ├── feature/restore-engine (功能分支)
│ └── fix/scan-timeout (修复分支)
└── release/v0.1.0 (发布分支)
```
### 4.2 开发流程
1. **创建分支:** `git checkout -b feature/xxx develop`
2. **开发 & 测试:** 编写代码 + 单元测试
3. **代码检查:** `make lint`
4. **提交:** 遵循 Conventional Commits
5. **推送 & PR** 推送分支,创建 Pull Request
6. **Code Review** 至少一人 Review 通过
7. **合并:** Squash Merge 到 develop
### 4.3 常用 Make 命令
```makefile
# 构建
make build # 构建当前平台
make build-all # 构建所有平台
make build-windows # 构建 Windows 版本
make build-darwin # 构建 macOS 版本
make build-linux # 构建 Linux 版本
# 测试
make test # 运行所有测试
make test-unit # 运行单元测试
make test-integration # 运行集成测试
make test-e2e # 运行端到端测试
make test-coverage # 生成覆盖率报告
# 代码质量
make lint # 运行 golangci-lint
make fmt # 格式化代码
make vet # go vet 检查
# 开发
make run # 编译并运行
make dev # 开发模式(热重载)
make clean # 清理构建产物
# 发布
make release # 使用 GoReleaser 发布
make snapshot # GoReleaser 快照(不发布)
```
---
## 5. 添加新采集器指南
### 5.1 步骤
以添加 "Docker" 采集器为例:
#### Step 1: 创建采集器文件
```go
// internal/collector/runtime/docker.go
package runtime
import (
"context"
"github.com/user/devpack/internal/collector"
)
// DockerCollector Docker 环境采集器
type DockerCollector struct{}
// 确保实现了 Collector 接口
var _ collector.Collector = (*DockerCollector)(nil)
func NewDockerCollector() *DockerCollector {
return &DockerCollector{}
}
func (c *DockerCollector) Name() string { return "docker" }
func (c *DockerCollector) DisplayName() string { return "Docker" }
func (c *DockerCollector) Description() string {
return "Captures Docker version, images, and configuration"
}
func (c *DockerCollector) Category() collector.Category {
return collector.CategoryRuntime
}
func (c *DockerCollector) IsAvailable(ctx context.Context) bool {
_, err := exec.LookPath("docker")
return err == nil
}
func (c *DockerCollector) Scan(ctx context.Context, opts collector.ScanOptions) (*collector.ScanResult, error) {
// 实现扫描逻辑
// 1. 检测 Docker 版本
// 2. 列出本地镜像
// 3. 获取 Docker daemon 配置
return nil, nil
}
func (c *DockerCollector) Capture(ctx context.Context, targetDir string, opts collector.CaptureOptions) error {
// 实现捕获逻辑
return nil
}
func (c *DockerCollector) Restore(ctx context.Context, sourceDir string, opts collector.RestoreOptions) error {
// 实现还原逻辑
return nil
}
func (c *DockerCollector) Verify(ctx context.Context) (*collector.VerifyResult, error) {
// 实现验证逻辑
return nil, nil
}
```
#### Step 2: 注册采集器
```go
// internal/collector/registry.go 中添加注册
func NewDefaultRegistry() *Registry {
r := NewRegistry()
// Runtime collectors
r.Register(runtime.NewGoCollector())
r.Register(runtime.NewNodeCollector())
r.Register(runtime.NewDockerCollector()) // 新增
// ...
return r
}
```
#### Step 3: 编写测试
```go
// internal/collector/runtime/docker_test.go
func TestDockerCollector_Scan(t *testing.T) {
c := NewDockerCollector()
if !c.IsAvailable(context.Background()) {
t.Skip("Docker not available")
}
result, err := c.Scan(context.Background(), collector.ScanOptions{})
assert.NoError(t, err)
assert.NotEmpty(t, result.Items)
}
```
#### Step 4: 更新文档
在 README.md 和 PRD.md 中添加 Docker 采集器的说明。
---
## 6. 调试指南
### 6.1 日志级别
```bash
# 设置日志级别
DEVPACK_LOG_LEVEL=debug devpack scan
# 或通过命令行参数
devpack scan --log-level debug
```
| 级别 | 用途 |
|------|------|
| `trace` | 非常详细的调试信息 |
| `debug` | 调试信息 |
| `info` | 正常操作信息(默认) |
| `warn` | 警告 |
| `error` | 错误 |
| `fatal` | 致命错误 |
### 6.2 VS Code 调试配置
```json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug DevPack Scan",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["scan", "--verbose"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
},
{
"name": "Debug DevPack Capture",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["capture", "--name", "test-env"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
}
]
}
```
### 6.3 常见问题排查
| 问题 | 排查方向 |
|------|---------|
| 采集器找不到工具 | 检查 PATH 环境变量,确认工具已安装 |
| Pack 文件损坏 | 使用 `devpack verify <pack>` 校验 |
| 还原权限不足 | 以管理员身份运行 |
| 跨版本不兼容 | 检查 Manifest 中的 format_ver |
---
## 7. 发布流程
### 7.1 版本号规范
遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)
```
MAJOR.MINOR.PATCH[-PRERELEASE]
- MAJOR: 不兼容的 API 变更
- MINOR: 向下兼容的功能新增
- PATCH: 向下兼容的 Bug 修复
```
### 7.2 发布步骤
```bash
# 1. 确保所有测试通过
make test
# 2. 更新 CHANGELOG
# 编辑 CHANGELOG.md
# 3. 创建 Release Tag
git tag -a v0.1.0 -m "Release v0.1.0"
git push origin v0.1.0
# 4. GoReleaser 自动构建发布CI 触发)
# 或手动:
make release
```
### 7.3 GoReleaser 配置
```yaml
# .goreleaser.yml
version: 2
builds:
- main: ./cmd/devpack
binary: devpack
env:
- CGO_ENABLED=0
goos:
- windows
- darwin
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X github.com/user/devpack/pkg/version.Version={{.Version}}
- -X github.com/user/devpack/pkg/version.Commit={{.Commit}}
- -X github.com/user/devpack/pkg/version.Date={{.Date}}
archives:
- format: tar.gz
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^chore:'
```

288
docs/PRD.md Normal file
View File

@@ -0,0 +1,288 @@
# DevPack 产品需求文档 (PRD)
> 版本v1.0.0-draft
> 更新日期2026-03-03
> 作者DevPack Team
---
## 1. 产品概述
### 1.1 产品定位
DevPack 是一款面向开发者的**开发环境打包迁移工具**。它能够将一台电脑上的完整开发环境运行时、工具链、IDE 配置、Shell 设置等)扫描、捕获并打包成一个可移植的文件,在另一台同操作系统的电脑上快速还原,大幅减少环境搭建时间。
### 1.2 产品愿景
> **让开发环境像 U 盘一样即插即用。**
### 1.3 目标用户
| 用户画像 | 描述 | 核心诉求 |
|---------|------|---------|
| 🧑‍💻 个人开发者 | 拥有多台电脑或定期更换设备 | 快速在新设备上恢复工作环境 |
| 👥 团队 Leader | 负责团队开发规范和环境标准化 | 统一团队成员开发环境 |
| 🏢 企业 IT 管理员 | 需要批量部署开发工位 | 标准化开发环境快速部署 |
| 🎓 培训讲师 | 需要为学员提供一致的学习环境 | 一键分发教学开发环境 |
| 🔄 DevOps 工程师 | 管理多个项目的不同环境需求 | 快速切换不同项目环境配置 |
### 1.4 核心价值主张
1. **节省时间** — 将环境搭建从数小时/天缩短至分钟级别
2. **避免遗漏** — 自动扫描确保不遗漏任何配置
3. **可复现** — 确保环境的精确复现
4. **安全可靠** — 敏感数据加密,还原可回滚
---
## 2. 用户场景 (User Stories)
### 2.1 核心场景
#### US-001: 换电脑迁移开发环境
> **作为** 一名全栈开发者
> **我希望** 在换新电脑时能一键迁移我的所有开发环境
> **以便** 我可以在新电脑上立即开始工作,而不用花一整天重新配置
**验收标准:**
- [ ] 能在旧电脑上扫描并打包完整开发环境
- [ ] 生成的 `.devpack` 文件可通过 U 盘或网络传输
- [ ] 在新电脑上能一键还原所有工具和配置
- [ ] 还原后的环境与原始环境功能一致
#### US-002: 团队环境标准化
> **作为** 一名技术团队 Leader
> **我希望** 能导出一个标准开发环境包分发给团队成员
> **以便** 所有人使用一致的开发工具和配置
**验收标准:**
- [ ] 能创建一个"标准环境"配置文件
- [ ] 团队成员可导入并一键应用标准环境
- [ ] 可指定哪些配置是强制的,哪些是可选的
- [ ] 支持环境包版本更新推送
#### US-003: 选择性环境打包
> **作为** 一名开发者
> **我希望** 只打包特定的工具链(如只打包 Go + VS Code
> **以便** 我可以为不同项目准备精简的环境包
**验收标准:**
- [ ] 扫描时能分类展示各类工具/配置
- [ ] 用户可选择只打包特定分类
- [ ] 支持通过过滤器精确指定打包内容
- [ ] 支持保存过滤器为命名配置
#### US-004: 环境差异对比
> **作为** 一名开发者
> **我希望** 对比两台电脑的开发环境差异
> **以便** 我能定位"为什么在你电脑上能跑在我这里不行"的问题
**验收标准:**
- [ ] 能导入另一台电脑的环境快照
- [ ] 以清晰的格式展示两个环境的差异
- [ ] 差异按分类(运行时版本、配置差异、缺失工具等)组织
- [ ] 可选择性地将差异应用到当前环境
#### US-005: 环境备份与恢复
> **作为** 一名开发者
> **我希望** 定期备份我的开发环境配置
> **以便** 系统崩溃或重装后能快速恢复
**验收标准:**
- [ ] 支持增量备份(只记录变化部分)
- [ ] 支持查看历史版本
- [ ] 能从指定版本恢复
- [ ] 备份文件占用空间合理
### 2.2 扩展场景
#### US-006: 干运行模式
> **作为** 一名谨慎的开发者
> **我希望** 在还原前能预览将要做的所有更改
> **以便** 确认不会破坏我现有的环境
#### US-007: 还原回滚
> **作为** 一名开发者
> **我希望** 还原失败或不满意时能撤销所有更改
> **以便** 我的电脑不会因为错误的还原变得更糟
#### US-008: 敏感数据保护
> **作为** 一名注重安全的开发者
> **我希望** SSH 密钥和 Token 等敏感数据在打包时被加密
> **以便** 即使 .devpack 文件泄露也不会暴露我的密钥
---
## 3. 功能需求
### 3.1 功能矩阵
| 功能模块 | 功能项 | 优先级 | MVP | 说明 |
|---------|--------|--------|-----|------|
| **环境扫描** | 运行时检测 | P0 | ✅ | Go, Node, Python, Java, Rust, .NET |
| | 包管理器检测 | P0 | ✅ | Scoop, Choco, Winget, Brew, APT |
| | 编辑器/IDE 配置 | P0 | ✅ | VS Code (扩展+设置), JetBrains |
| | Shell 配置 | P0 | ✅ | PowerShell Profile, .bashrc, .zshrc |
| | 环境变量 | P0 | ✅ | 用户级环境变量 |
| | Git 配置 | P1 | ✅ | .gitconfig, aliases |
| | SSH/GPG 密钥 | P1 | ❌ | 加密处理 |
| | 字体 | P2 | ❌ | 开发用等宽字体 |
| | 自定义脚本 | P2 | ❌ | 用户指定的脚本/工具 |
| **打包引擎** | Pack 文件生成 | P0 | ✅ | .devpack 格式 |
| | 选择性打包 | P0 | ✅ | 按采集器/过滤器 |
| | 增量打包 | P1 | ❌ | 基于上一次快照的差异包 |
| | 数据加密 | P1 | ❌ | AES-256-GCM 加密敏感数据 |
| | 压缩优化 | P1 | ✅ | 多种压缩算法可选 |
| **还原引擎** | 自动安装 | P0 | ✅ | 按依赖顺序安装 |
| | 配置还原 | P0 | ✅ | 还原配置文件 |
| | 冲突处理 | P0 | ✅ | 检测并处理版本冲突 |
| | 干运行模式 | P0 | ✅ | 预览更改不实际执行 |
| | 回滚机制 | P1 | ❌ | 还原前创建还原点 |
| **环境管理** | Profile 管理 | P0 | ✅ | 创建/编辑/删除配置 |
| | 环境对比 | P1 | ❌ | diff 两个环境 |
| | 版本历史 | P2 | ❌ | 环境快照版本管理 |
| **插件系统** | 内置采集器 | P0 | ✅ | 核心采集器 |
| | 自定义插件 | P2 | ❌ | 外部插件加载 |
| **注册中心** | 本地存储 | P0 | ✅ | 本地 Pack 管理 |
| | 远程注册中心 | P2 | ❌ | 类似 Docker Hub |
### 3.2 采集器详细需求
#### 3.2.1 运行时采集器 (Runtime Collector)
**扫描内容:**
| 语言/工具 | 检测项 | 还原方式 |
|-----------|--------|---------|
| Go | 版本、GOPATH、GOROOT、go env 全部配置 | 下载安装对应版本 |
| Node.js | 版本、npm/yarn/pnpm 全局包 | 通过 nvm/fnm 或直接安装 |
| Python | 版本、pip 全局包、virtualenv/conda | 安装器或 pyenv |
| Java | 版本、JAVA_HOME、Maven/Gradle 配置 | 通过 SDKMAN 或直接安装 |
| Rust | 版本、rustup 工具链、cargo 全局安装 | 通过 rustup |
| .NET | 版本、SDK 版本列表 | 通过官方安装器 |
| Ruby | 版本、gem 全局包 | 通过 rbenv/rvm |
| PHP | 版本、Composer 全局包 | 通过安装器 |
#### 3.2.2 包管理器采集器 (Package Collector)
| 平台 | 包管理器 | 采集内容 |
|------|---------|---------|
| Windows | Scoop | bucket 列表、已安装包列表 |
| Windows | Chocolatey | 已安装包列表及版本 |
| Windows | Winget | 已安装包列表 |
| macOS | Homebrew | tap 列表、formulae、casks |
| Linux (Debian) | APT | 手动安装的包列表 |
| Linux (RHEL) | DNF/YUM | 手动安装的包列表 |
| Linux (Arch) | Pacman/Yay | 显式安装的包列表 |
#### 3.2.3 编辑器采集器 (Editor Collector)
| 编辑器 | 采集内容 |
|--------|---------|
| VS Code | 扩展列表、settings.json、keybindings.json、代码片段 |
| VS Code Insiders | 同上 |
| JetBrains IDEs | 设置仓库导出、插件列表 |
| Vim/Neovim | .vimrc / init.vim / init.lua、插件列表 |
| Sublime Text | 包列表、配置文件 |
| Emacs | .emacs / init.el 配置 |
#### 3.2.4 Shell 采集器 (Shell Collector)
| Shell | 采集内容 |
|-------|---------|
| PowerShell | Profile 脚本、已安装模块 |
| Bash | .bashrc, .bash_profile, .bash_aliases |
| Zsh | .zshrc, oh-my-zsh 配置/主题/插件 |
| Fish | config.fish, 自定义函数 |
| Windows Terminal | settings.json |
| iTerm2 (macOS) | 配置文件 |
---
## 4. 非功能需求
### 4.1 性能要求
| 指标 | 要求 | 说明 |
|------|------|------|
| 扫描速度 | < 30 秒 | 完整环境扫描 |
| 打包速度 | < 2 分钟 | 典型开发环境(不含大文件) |
| 还原速度 | 取决于网络 | 下载安装器受网速限制 |
| Pack 文件大小 | < 50 MB | 典型环境(不含二进制文件) |
| 内存占用 | < 200 MB | 运行时峰值 |
### 4.2 安全要求
- 敏感数据SSH 密钥、Token、密码必须使用 AES-256-GCM 加密
- 加密密钥由用户密码通过 Argon2id 派生
- Pack 文件支持完整性校验SHA-256
- 还原前必须校验 Pack 文件签名
- 不采集浏览器密码、系统密码等隐私数据
### 4.3 兼容性要求
- 同操作系统类型之间迁移Windows → Windows, macOS → macOS, Linux → Linux
- 不支持跨操作系统迁移(设计决策:不同 OS 的工具链差异太大)
- 支持同一 OS 不同版本之间迁移(尽力兼容,不保证 100%
- Pack 文件格式向后兼容(新版本能读取旧版本生成的 Pack
### 4.4 可靠性要求
- 还原过程中断后可恢复(断点续装)
- 还原失败自动回滚
- 详细的操作日志记录
- 错误信息清晰可操作
---
## 5. 约束与假设
### 5.1 技术约束
- 使用 Go 语言开发,编译为单一静态二进制文件
- 不依赖运行时环境(不需要预装 Go、Python 等)
- 跨平台编译支持 Windows/macOS/Linux
- CLI 优先,暂不开发 GUI
### 5.2 假设
- 用户具备基本的命令行使用能力
- 目标电脑有网络连接(用于下载安装器)
- 用户有对应平台的管理员/sudo 权限
- 源电脑和目标电脑使用相同的操作系统类型
---
## 6. 成功指标
| 指标 | 目标 | 衡量方式 |
|------|------|---------|
| 环境恢复时间 | 缩短 80%+ | 对比手动配置时间 |
| 环境完整度 | > 95% 配置项正确恢复 | 还原后验证 |
| 用户满意度 | > 4.0/5.0 | 用户调查 |
| GitHub Stars | 1000+ (首年) | GitHub |
| 活跃用户 | 500+ MAU (首年) | 遥测数据 |
---
## 7. 风险评估
| 风险 | 可能性 | 影响 | 缓解措施 |
|------|--------|------|---------|
| 不同版本 OS 兼容性问题 | 高 | 中 | 充分测试,兼容性矩阵 |
| 安全敏感数据泄露 | 低 | 高 | 强加密,安全审计 |
| 包管理器 API 变更 | 中 | 中 | 插件化设计,快速适配 |
| 还原过程破坏现有环境 | 中 | 高 | 回滚机制,干运行模式 |
| Pack 文件过大 | 中 | 低 | 选择性打包,增量打包 |
---
## 附录 A: 术语表
| 术语 | 定义 |
|------|------|
| Pack | DevPack 生成的打包文件(.devpack 格式) |
| Profile | 命名的环境配置方案,定义打包范围和规则 |
| Collector | 采集器,负责扫描和收集特定类别的环境信息 |
| Manifest | Pack 内的清单文件,描述 Pack 的完整内容和元数据 |
| Restore Point | 还原点,还原前创建的当前环境快照,用于回滚 |
| Dry Run | 干运行,模拟执行并展示将要做的更改,不实际操作 |

232
docs/ROADMAP.md Normal file
View File

@@ -0,0 +1,232 @@
# DevPack 开发路线图
> 更新日期2026-03-03
> 作者DevPack Team
---
## 版本规划概览
```
v0.1.0 (MVP) v0.2.0 v0.3.0 v1.0.0
──────────────── → ──────────────── → ──────────────── → ────────────────
核心框架 + 基础 扩展采集器 + 加密 + 增量 + 稳定发布 +
采集器 + 打包还原 环境对比 + 回滚 远程注册中心 性能优化
2026 Q2 2026 Q3 2026 Q4 2027 Q1
```
---
## v0.1.0 — MVP (最小可行产品)
> **目标:** 核心打包还原流程跑通,支持最常用的开发工具
> **预计时间:** 2026 Q2约 3 个月)
### 里程碑
#### M1: 项目基础架构2 周)
- [ ] 项目脚手架搭建Go Module、目录结构、Makefile
- [ ] CLI 框架搭建Cobra + Viper
- [ ] 日志系统搭建zerolog
- [ ] 平台检测与适配层基础实现
- [ ] Collector 接口定义与注册中心实现
- [ ] 基础配置系统config.yaml
- [ ] `devpack version` 命令
#### M2: 核心采集器3 周)
- [ ] Go 运行时采集器
- [ ] Node.js 运行时采集器
- [ ] Python 运行时采集器
- [ ] VS Code 编辑器采集器(扩展 + 设置)
- [ ] PowerShell Shell 采集器
- [ ] Scoop 包管理器采集器Windows
- [ ] Git 配置采集器
- [ ] 环境变量采集器
- [ ] `devpack scan` 命令
#### M3: 打包引擎2 周)
- [ ] Pack 文件格式实现
- [ ] Manifest 生成与解析
- [ ] tar.gz 打包/解包
- [ ] 校验和计算与验证
- [ ] `devpack capture` 命令
- [ ] `devpack export` / `devpack import` 命令
#### M4: 还原引擎3 周)
- [ ] 还原计划生成器
- [ ] 基础冲突检测(版本对比)
- [ ] 各采集器的还原逻辑实现
- [ ] 干运行模式
- [ ] 还原报告生成
- [ ] `devpack restore` 命令
#### M5: 配置与打磨2 周)
- [ ] Profile 系统实现
- [ ] `devpack init` 命令
- [ ] `devpack list` 命令
- [ ] `devpack profile` 命令
- [ ] 终端 UI 优化(进度条、表格、颜色)
- [ ] 错误信息优化
- [ ] 单元测试覆盖率 > 60%
- [ ] README 和基础文档完善
- [ ] GitHub Actions CI 配置
- [ ] 首次内测发布
### MVP 交付标准
- ✅ 能在 Windows 上扫描 Go、Node、Python、VS Code、PowerShell 配置
- ✅ 能打包生成 `.devpack` 文件
- ✅ 能在另一台 Windows 上还原
- ✅ 干运行模式可用
- ✅ 基本的冲突处理
- ✅ CLI 交互友好
---
## v0.2.0 — 功能扩展
> **目标:** 更多采集器、环境对比、回滚、macOS 支持
> **预计时间:** 2026 Q3
### 新增功能
#### 更多采集器
- [ ] Java/Kotlin 运行时采集器JDK + Maven/Gradle
- [ ] Rust 运行时采集器rustup + cargo
- [ ] .NET 运行时采集器
- [ ] JetBrains IDE 采集器
- [ ] Vim/Neovim 采集器
- [ ] Bash/Zsh Shell 采集器
- [ ] Homebrew 包管理器采集器macOS
- [ ] Chocolatey 包管理器采集器Windows
- [ ] Winget 包管理器采集器Windows
- [ ] Windows Terminal 配置采集器
#### 环境对比
- [ ] `devpack diff` 命令实现
- [ ] Pack vs 当前环境对比
- [ ] Pack vs Pack 对比
- [ ] 差异可视化输出
#### 回滚机制
- [ ] 还原点创建
- [ ] 还原点管理(列出、删除)
- [ ] 从还原点回滚
- [ ] `devpack rollback` 命令
#### 平台扩展
- [ ] macOS 平台完整支持
- [ ] macOS 采集器适配
- [ ] 跨平台 CI 测试矩阵
#### 改进
- [ ] 还原进度条改进
- [ ] 并行扫描/还原
- [ ] 更好的错误消息和排障指南
- [ ] 测试覆盖率 > 75%
---
## v0.3.0 — 安全与高级功能
> **目标:** 加密支持、增量打包、Linux 支持、远程注册中心
> **预计时间:** 2026 Q4
### 新增功能
#### 安全
- [ ] AES-256-GCM 数据加密
- [ ] Argon2id 密钥派生
- [ ] SSH/GPG 密钥采集器(加密存储)
- [ ] 敏感数据自动检测与警告
- [ ] Pack 文件签名
#### 增量打包
- [ ] 基于上一次快照的差异计算
- [ ] 增量 Pack 生成
- [ ] 增量还原
#### Linux 支持
- [ ] Linux 平台适配
- [ ] APT 包管理器采集器
- [ ] DNF/YUM 包管理器采集器
- [ ] Linux 特定配置采集
#### 远程注册中心(实验性)
- [ ] 注册中心 API 设计
- [ ] Pack 上传/下载
- [ ] 版本管理
- [ ] `devpack registry` 命令
- [ ] 团队共享支持
#### 字体采集器
- [ ] 开发字体检测
- [ ] 字体文件采集
- [ ] 字体安装还原
---
## v1.0.0 — 稳定发布
> **目标:** 生产就绪、性能优化、完善文档
> **预计时间:** 2027 Q1
### 重点
#### 稳定性
- [ ] 全面端到端测试
- [ ] 多 OS 版本兼容性测试
- [ ] 压力测试
- [ ] 边界情况处理
- [ ] 测试覆盖率 > 85%
#### 性能优化
- [ ] 扫描性能优化(缓存、并行)
- [ ] 打包性能优化zstd 压缩可选)
- [ ] 还原性能优化(并行安装)
- [ ] 大文件处理优化
#### 文档完善
- [ ] 完整用户手册
- [ ] API 参考文档
- [ ] 插件开发指南
- [ ] 故障排除指南
- [ ] 视频教程
#### 发布
- [ ] GoReleaser 多平台构建
- [ ] Homebrew Formula
- [ ] Scoop Manifest
- [ ] Docker 镜像(用于 CI
- [ ] 官方网站
---
## 远期规划 (v1.x+)
### 可能的功能方向
| 功能 | 描述 | 优先级 |
|------|------|--------|
| 🔌 插件系统 | 支持第三方采集器插件 | 高 |
| 🌐 Web UI | 基于 Web 的管理界面 | 中 |
| 📱 GUI 客户端 | 桌面 GUI 客户端 | 中 |
| ☁️ 云同步 | 通过云端同步 Pack | 中 |
| 🔄 自动更新 | 定时检查环境变更并自动备份 | 中 |
| 🤝 团队管理 | 团队 Pack 管理和权限控制 | 低 |
| 📊 环境报告 | 生成环境配置报告 | 低 |
| 🐳 Docker 集成 | 从 Dockerfile 提取环境信息 | 低 |
| 🧪 沙盒预览 | 在沙盒中预览还原效果 | 低 |
| 📋 配方系统 | 预设的"开发环境配方"共享 | 低 |
---
## 贡献
路线图中的功能优先级可能根据社区反馈调整。如果你对某个功能特别期待,欢迎:
1. 在 [Issues](https://github.com/user/devpack/issues) 中提出功能请求
2. 为你期待的 Issue 点赞 👍
3. 提交 Pull Request 实现功能
我们优先实现社区呼声最高的功能。

558
docs/USER-GUIDE.md Normal file
View File

@@ -0,0 +1,558 @@
# DevPack 用户手册
> 版本v0.1.0
> 更新日期2026-03-03
---
## 目录
1. [简介](#1-简介)
2. [安装](#2-安装)
3. [快速入门](#3-快速入门)
4. [核心概念](#4-核心概念)
5. [命令详解](#5-命令详解)
6. [使用场景](#6-使用场景)
7. [配置文件](#7-配置文件)
8. [常见问题](#8-常见问题)
9. [故障排除](#9-故障排除)
---
## 1. 简介
DevPack 是一个开发环境打包迁移工具。它可以:
- 🔍 **扫描** 你当前电脑上安装的开发工具、配置、环境变量等
- 📦 **打包** 成一个可移植的 `.devpack` 文件
- 🚀 **还原** 在另一台电脑上一键恢复完整开发环境
### 支持的内容
| 类别 | 支持的项目 |
|------|-----------|
| 编程语言 | Go, Node.js, Python, Java, Rust, .NET, Ruby, PHP |
| 包管理器 | Scoop, Chocolatey, Winget, Homebrew, APT |
| 编辑器 | VS Code (扩展+设置), JetBrains IDEs, Vim/Neovim |
| Shell | PowerShell, Bash, Zsh, Fish, Windows Terminal |
| 其他 | Git 配置, 环境变量, SSH 密钥, 字体 |
---
## 2. 安装
### 2.1 通过 Go 安装
```bash
go install github.com/user/devpack@latest
```
### 2.2 通过包管理器安装
**Windows (Scoop):**
```powershell
scoop bucket add devpack https://github.com/user/devpack-bucket
scoop install devpack
```
**Windows (Chocolatey):**
```powershell
choco install devpack
```
**macOS (Homebrew):**
```bash
brew tap user/devpack
brew install devpack
```
### 2.3 下载二进制文件
1. 前往 [GitHub Releases](https://github.com/user/devpack/releases)
2. 下载对应你系统的压缩包
3. 解压到一个目录(如 `C:\Tools\devpack\`
4. 将该目录添加到系统 PATH
### 2.4 验证安装
```bash
devpack version
```
输出示例:
```
DevPack v0.1.0 (commit: abc1234, built: 2026-03-03)
OS: windows/amd64
```
---
## 3. 快速入门
### 3.1 五分钟上手
```bash
# 第一步:初始化
devpack init
# 第二步:扫描你的环境
devpack scan
# 第三步:打包
devpack capture --name "my-env"
# 第四步:导出为文件(可以拷贝到 U 盘)
devpack export my-env -o my-env.devpack
```
拿到 `.devpack` 文件后,在新电脑上:
```bash
# 第五步:安装 DevPack 后,导入文件
devpack import my-env.devpack
# 第六步:预览将要做的更改
devpack restore my-env --dry-run
# 第七步:正式还原
devpack restore my-env
```
### 3.2 推荐工作流
```
旧电脑 新电脑
────── ──────
devpack init (安装 DevPack)
│ │
devpack scan │
│ │
devpack capture --name env │
│ │
devpack export env │
│ │
└── my-env.devpack ──(U盘)──▶ │
devpack import my-env.devpack
devpack restore env --dry-run
devpack restore env
✅ 环境就绪!
```
---
## 4. 核心概念
### 4.1 Pack环境包
Pack 是 DevPack 生成的打包文件(`.devpack` 格式)。它包含:
- **Manifest** — 描述包内容的清单
- **采集器数据** — 各个采集器收集的环境数据
- **校验信息** — 确保文件完整性
一个 Pack 代表某一时刻你的开发环境的"快照"。
### 4.2 Profile配置方案
Profile 定义了"打包什么"和"怎么打包"。通过 Profile你可以
- 只打包特定的工具链(如只打包 Go + VS Code
- 排除不需要的内容
- 为不同场景准备不同的配置方案
内置 Profile 模板:
| 模板 | 包含内容 |
|------|---------|
| `minimal` | 只有编辑器设置和 Shell 配置 |
| `standard` | 运行时 + 编辑器 + Shell + Git + 环境变量 |
| `full` | 所有可用的采集器 |
### 4.3 Collector采集器
采集器是负责扫描和收集特定类型环境信息的模块。每个采集器独立工作:
| 采集器 | 扫描内容 | 还原方式 |
|--------|---------|---------|
| `go` | Go 版本、go env、GOPATH/bin 工具 | 下载安装器 |
| `node` | Node 版本、npm 全局包 | nvm/直接安装 |
| `python` | Python 版本、pip 全局包 | 安装器/pyenv |
| `vscode` | 扩展列表、settings.json | code --install-extension |
| `powershell` | Profile、已安装模块 | 复制 Profile + Install-Module |
| `scoop` | bucket 列表、已安装包 | scoop install |
| `git` | .gitconfig、aliases | 复制配置 |
| `env` | 用户环境变量 | setx/export |
### 4.4 还原策略
当还原时遇到已存在的配置DevPack 提供多种处理策略:
| 策略 | 行为 |
|------|------|
| `skip` | 跳过,保留现有配置 |
| `overwrite` | 覆盖,使用 Pack 中的配置 |
| `merge` | 合并两者(适用于配置文件) |
| `prompt` | 逐项询问用户(默认) |
| `newest` | 使用较新的版本 |
---
## 5. 命令详解
### 5.1 devpack init
初始化 DevPack 配置。
```bash
# 默认初始化
devpack init
# 使用 standard 模板
devpack init --template standard
# 指定 Profile 名称
devpack init --profile my-profile
```
这会在 `~/.devpack/` 下创建配置目录和默认配置文件。
### 5.2 devpack scan
扫描当前环境,展示检测到的开发工具和配置。
```bash
# 扫描所有
devpack scan
# 详细模式
devpack scan --detailed
# 只扫描运行时
devpack scan --category runtime
# 只扫描特定采集器
devpack scan -c go,node,vscode
# 输出为 JSON
devpack scan -o json
# 保存扫描结果
devpack scan --save my-scan.json
```
### 5.3 devpack capture
捕获环境数据并创建 Pack。
```bash
# 基本用法
devpack capture --name "my-env"
# 使用 Profile
devpack capture --name "go-env" --profile golang-dev
# 捕获所有
devpack capture --name "full-env" --all
# 只捕获特定采集器
devpack capture --name "editor-env" -c vscode,git
# 带描述和标签
devpack capture --name "v1" \
--description "生产环境工具链" \
--tag golang,production
```
### 5.4 devpack restore
从 Pack 还原环境。
```bash
# 先预览
devpack restore my-env --dry-run
# 执行还原
devpack restore my-env
# 跳过冲突项
devpack restore my-env --conflict skip
# 覆盖所有冲突
devpack restore my-env --conflict overwrite
# 只还原特定采集器
devpack restore my-env -c vscode,git
# 跳过确认
devpack restore my-env -y
```
### 5.5 devpack export / import
```bash
# 导出为文件
devpack export my-env -o ~/Desktop/my-env.devpack
# 导入文件
devpack import ~/Desktop/my-env.devpack
# 导入并指定名称
devpack import ~/Desktop/my-env.devpack --name imported-env
# 导入前验证
devpack import ~/Desktop/my-env.devpack --verify
```
### 5.6 devpack diff
对比环境差异。
```bash
# 与当前环境对比
devpack diff my-env --current
# 两个 Pack 对比
devpack diff old-env --with new-env
# 只对比特定采集器
devpack diff my-env --current -c runtime
```
### 5.7 devpack list
```bash
# 列出所有 Pack
devpack list packs
# 列出所有 Profile
devpack list profiles
# 列出所有可用采集器
devpack list collectors
# 详细信息
devpack list packs --detailed
```
### 5.8 devpack profile
```bash
# 创建 Profile
devpack profile create my-profile
# 查看 Profile
devpack profile show my-profile
# 编辑 Profile使用默认编辑器打开
devpack profile edit my-profile
# 设置默认 Profile
devpack profile use my-profile
# 删除 Profile
devpack profile delete my-profile
# 列出所有 Profile
devpack profile list
```
---
## 6. 使用场景
### 6.1 个人换电脑
**场景:** 你买了一台新笔记本电脑,需要迁移开发环境。
**步骤:**
在旧电脑上:
```bash
devpack init
devpack scan --detailed # 确认要迁移的内容
devpack capture --name "laptop-2026" --all
devpack export laptop-2026 -o laptop-2026.devpack
```
`laptop-2026.devpack` 拷贝到 U 盘或通过网络传到新电脑。
在新电脑上:
```bash
# 先安装 DevPack
devpack import laptop-2026.devpack
devpack restore laptop-2026 --dry-run # 预览
devpack restore laptop-2026 # 开始还原
```
### 6.2 团队环境统一
**场景:** 你是 Tech Lead需要为团队准备统一的开发环境。
**步骤:**
1. 创建团队 Profile
```bash
devpack profile create team-standard
devpack profile edit team-standard
```
2. 在 Profile 中定义标准配置(编辑 YAML 文件)
3. 使用 Profile 打包:
```bash
devpack capture --name "team-env-v1" --profile team-standard
devpack export team-env-v1 -o team-env-v1.devpack
```
4. 分发给团队成员
5. 成员还原:
```bash
devpack import team-env-v1.devpack
devpack restore team-env-v1
```
### 6.3 项目环境隔离
**场景:** 你同时参与 Go 和 Python 项目,需要不同的环境配置。
**步骤:**
1. 为每个项目创建 Profile
```bash
devpack profile create golang-project
devpack profile create python-ml
```
2. 分别配置各 Profile 只包含对应工具链
3. 按项目打包:
```bash
devpack capture --name "go-env" --profile golang-project
devpack capture --name "ml-env" --profile python-ml
```
### 6.4 环境排障
**场景:** 同事说"在我电脑上能跑",你需要对比环境差异。
```bash
# 让同事导出环境
# (同事) devpack capture --name "alice-env" && devpack export alice-env
# 你导入并对比
devpack import alice-env.devpack
devpack diff alice-env --current
```
差异输出会清晰展示两个环境的不同之处。
---
## 7. 配置文件
### 7.1 配置文件位置
| 文件 | 路径 | 说明 |
|------|------|------|
| 全局配置 | `~/.devpack/config.yaml` | DevPack 全局设置 |
| Profile | `~/.devpack/profiles/<name>.yaml` | 命名配置方案 |
| 日志 | `~/.devpack/logs/devpack.log` | 运行日志 |
| Pack 存储 | `~/.devpack/packs/` | 本地 Pack 存储 |
### 7.2 自定义配置示例
```yaml
# ~/.devpack/config.yaml
defaults:
profile: "standard"
compression: "gzip"
conflict_strategy: "prompt"
logging:
level: "info"
ui:
color: true
progress_bar: true
security:
scan_for_secrets: true
exclude_patterns:
- "*_KEY"
- "*_SECRET"
- "*_TOKEN"
```
---
## 8. 常见问题
### Q: DevPack 和 Docker 有什么区别?
Docker 用于打包**应用运行环境**应用运行在容器中DevPack 用于打包**开发者的本地开发环境**,直接还原到宿主机。两者是互补关系。
### Q: 支持跨操作系统迁移吗?
不支持。DevPack 只支持同一操作系统之间的迁移Windows → Windows, macOS → macOS。不同操作系统的工具链和配置差异太大无法可靠地自动转换。
### Q: 还原会覆盖我现有的配置吗?
默认不会。DevPack 使用 `prompt` 策略,遇到冲突会询问你如何处理。你也可以先使用 `--dry-run` 预览所有更改。
### Q: Pack 文件大小一般多大?
典型的开发环境 Pack 大小在 5-50 MB 之间。Pack 文件只包含配置信息和元数据,不包含实际的安装程序二进制文件(还原时会从网络下载)。
### Q: 密码和密钥安全吗?
DevPack 默认不采集 SSH 密钥和密码。如果你选择采集 SSH 密钥,它们会使用 AES-256-GCM 加密存储。DevPack 不会采集浏览器密码、系统凭证等隐私数据。
### Q: 还原需要网络连接吗?
是的。还原过程需要从网络下载对应版本的安装程序。Pack 文件只包含"安装什么"的信息,不包含安装程序本身。
### Q: 还原失败了怎么办?
如果开启了还原点功能(默认开启),可以使用 `devpack rollback` 撤销所有更改,回到还原前的状态。
---
## 9. 故障排除
### 9.1 查看详细日志
```bash
# 开启详细输出
devpack scan --verbose
# 开启 debug 日志
devpack scan --log-level debug
# 查看日志文件
# Windows
notepad %USERPROFILE%\.devpack\logs\devpack.log
# macOS/Linux
cat ~/.devpack/logs/devpack.log
```
### 9.2 常见错误
| 错误 | 原因 | 解决方案 |
|------|------|---------|
| `collector not available` | 对应的工具未安装 | 安装对应工具或排除该采集器 |
| `pack file corrupted` | Pack 文件损坏 | 使用 `devpack verify` 检查,重新导出 |
| `platform mismatch` | Pack 的系统与当前不一致 | 只能在相同 OS 之间还原 |
| `permission denied` | 权限不足 | 以管理员身份运行 |
| `network timeout` | 下载超时 | 检查网络,或设置代理 |
### 9.3 报告问题
如果遇到无法解决的问题:
1. 运行 `devpack scan --log-level debug --save debug-info.json`
2. 查看 `~/.devpack/logs/devpack.log`
3. 在 [GitHub Issues](https://github.com/user/devpack/issues) 创建 Issue
4. 附上 debug 日志(注意删除敏感信息)

31
go.mod Normal file
View File

@@ -0,0 +1,31 @@
module github.com/user/devpack
go 1.22
require (
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
)
require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

75
go.sum Normal file
View File

@@ -0,0 +1,75 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,125 @@
package collector
import (
"context"
"time"
)
// Category 采集器分类
type Category string
const (
CategoryRuntime Category = "runtime" // 编程语言运行时
CategoryPackage Category = "package" // 包管理器
CategoryEditor Category = "editor" // 编辑器/IDE
CategoryShell Category = "shell" // Shell 配置
CategoryGit Category = "git" // Git 配置
CategoryEnv Category = "env" // 环境变量
CategoryFont Category = "font" // 字体
CategorySSH Category = "ssh" // SSH/GPG 密钥
CategoryCustom Category = "custom" // 自定义
)
// Collector 定义了所有采集器必须实现的接口
type Collector interface {
// Name 返回采集器名称(唯一标识)
Name() string
// DisplayName 返回采集器的显示名称
DisplayName() string
// Description 返回采集器的描述信息
Description() string
// Category 返回采集器所属分类
Category() Category
// IsAvailable 检测当前系统是否支持此采集器
IsAvailable(ctx context.Context) bool
// Scan 扫描当前环境,返回扫描结果
Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error)
// Capture 捕获环境数据,写入到指定目录
Capture(ctx context.Context, targetDir string, opts CaptureOptions) error
// Restore 从指定目录还原环境
Restore(ctx context.Context, sourceDir string, opts RestoreOptions) error
// Verify 验证还原后的环境是否正确
Verify(ctx context.Context) (*VerifyResult, error)
}
// ScanOptions 扫描选项
type ScanOptions struct {
Detailed bool `json:"detailed"`
Filters map[string]string `json:"filters,omitempty"`
Timeout time.Duration `json:"timeout,omitempty"`
}
// CaptureOptions 捕获选项
type CaptureOptions struct {
IncludePatterns []string `json:"include_patterns,omitempty"`
ExcludePatterns []string `json:"exclude_patterns,omitempty"`
Encrypt bool `json:"encrypt"`
}
// RestoreOptions 还原选项
type RestoreOptions struct {
ConflictStrategy ConflictStrategy `json:"conflict_strategy"`
DryRun bool `json:"dry_run"`
Force bool `json:"force"`
}
// ConflictStrategy 冲突处理策略
type ConflictStrategy string
const (
ConflictSkip ConflictStrategy = "skip"
ConflictOverwrite ConflictStrategy = "overwrite"
ConflictMerge ConflictStrategy = "merge"
ConflictPrompt ConflictStrategy = "prompt"
ConflictNewest ConflictStrategy = "newest"
)
// PlatformInfo 平台信息
type PlatformInfo struct {
OS string `json:"os"`
Version string `json:"os_version"`
Arch string `json:"arch"`
}
// ScanResult 扫描结果
type ScanResult struct {
Collector string `json:"collector"`
Category Category `json:"category"`
Items []ScanItem `json:"items"`
Timestamp time.Time `json:"timestamp"`
Platform PlatformInfo `json:"platform"`
Errors []string `json:"errors,omitempty"`
}
// ScanItem 单个扫描项
type ScanItem struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
Path string `json:"path,omitempty"`
Type string `json:"type,omitempty"`
Properties map[string]string `json:"properties,omitempty"`
Size int64 `json:"size,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
Children []ScanItem `json:"children,omitempty"`
}
// VerifyResult 验证结果
type VerifyResult struct {
Success bool `json:"success"`
Items []VerifyItem `json:"items"`
}
// VerifyItem 验证项
type VerifyItem struct {
Name string `json:"name"`
Status string `json:"status"` // ok, missing, version_mismatch, error
Message string `json:"message,omitempty"`
}

View File

@@ -0,0 +1,121 @@
package collector
import (
"context"
"fmt"
"sync"
)
// Registry 采集器注册中心
type Registry struct {
collectors map[string]Collector
mu sync.RWMutex
}
// NewRegistry 创建采集器注册中心
func NewRegistry() *Registry {
return &Registry{
collectors: make(map[string]Collector),
}
}
// Register 注册一个采集器
func (r *Registry) Register(c Collector) error {
r.mu.Lock()
defer r.mu.Unlock()
name := c.Name()
if _, exists := r.collectors[name]; exists {
return fmt.Errorf("collector already registered: %s", name)
}
r.collectors[name] = c
return nil
}
// Get 获取指定名称的采集器
func (r *Registry) Get(name string) (Collector, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
c, ok := r.collectors[name]
return c, ok
}
// List 列出所有已注册的采集器
func (r *Registry) List() []Collector {
r.mu.RLock()
defer r.mu.RUnlock()
result := make([]Collector, 0, len(r.collectors))
for _, c := range r.collectors {
result = append(result, c)
}
return result
}
// ListByCategory 按分类列出采集器
func (r *Registry) ListByCategory(cat Category) []Collector {
r.mu.RLock()
defer r.mu.RUnlock()
var result []Collector
for _, c := range r.collectors {
if c.Category() == cat {
result = append(result, c)
}
}
return result
}
// Available 返回当前系统上可用的采集器
func (r *Registry) Available(ctx context.Context) []Collector {
r.mu.RLock()
defer r.mu.RUnlock()
var result []Collector
for _, c := range r.collectors {
if c.IsAvailable(ctx) {
result = append(result, c)
}
}
return result
}
// ScanAll 使用所有可用的采集器扫描
func (r *Registry) ScanAll(ctx context.Context, opts ScanOptions) ([]*ScanResult, error) {
available := r.Available(ctx)
var (
results []*ScanResult
mu sync.Mutex
wg sync.WaitGroup
errs []error
)
for _, c := range available {
wg.Add(1)
go func(collector Collector) {
defer wg.Done()
result, err := collector.Scan(ctx, opts)
mu.Lock()
defer mu.Unlock()
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", collector.Name(), err))
return
}
results = append(results, result)
}(c)
}
wg.Wait()
if len(errs) > 0 && len(results) == 0 {
return nil, fmt.Errorf("all collectors failed: %v", errs)
}
return results, nil
}

View File

@@ -0,0 +1,87 @@
package collector
import (
"context"
"testing"
)
func TestRegistry_Register(t *testing.T) {
r := NewRegistry()
mock := &mockCollector{name: "test"}
err := r.Register(mock)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Duplicate registration should fail
err = r.Register(mock)
if err == nil {
t.Fatal("expected error for duplicate registration")
}
}
func TestRegistry_Get(t *testing.T) {
r := NewRegistry()
mock := &mockCollector{name: "test"}
_ = r.Register(mock)
c, ok := r.Get("test")
if !ok {
t.Fatal("expected to find collector")
}
if c.Name() != "test" {
t.Fatalf("expected name 'test', got '%s'", c.Name())
}
_, ok = r.Get("nonexistent")
if ok {
t.Fatal("expected not to find collector")
}
}
func TestRegistry_List(t *testing.T) {
r := NewRegistry()
_ = r.Register(&mockCollector{name: "a", category: CategoryRuntime})
_ = r.Register(&mockCollector{name: "b", category: CategoryEditor})
_ = r.Register(&mockCollector{name: "c", category: CategoryRuntime})
all := r.List()
if len(all) != 3 {
t.Fatalf("expected 3 collectors, got %d", len(all))
}
runtime := r.ListByCategory(CategoryRuntime)
if len(runtime) != 2 {
t.Fatalf("expected 2 runtime collectors, got %d", len(runtime))
}
}
// mockCollector implements Collector for testing
type mockCollector struct {
name string
category Category
available bool
}
func (m *mockCollector) Name() string { return m.name }
func (m *mockCollector) DisplayName() string { return m.name }
func (m *mockCollector) Description() string { return "mock collector" }
func (m *mockCollector) Category() Category { return m.category }
func (m *mockCollector) IsAvailable(ctx context.Context) bool {
return m.available
}
func (m *mockCollector) Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error) {
return &ScanResult{Collector: m.name, Category: m.category}, nil
}
func (m *mockCollector) Capture(ctx context.Context, targetDir string, opts CaptureOptions) error {
return nil
}
func (m *mockCollector) Restore(ctx context.Context, sourceDir string, opts RestoreOptions) error {
return nil
}
func (m *mockCollector) Verify(ctx context.Context) (*VerifyResult, error) {
return &VerifyResult{Success: true}, nil
}

57
internal/config/paths.go Normal file
View File

@@ -0,0 +1,57 @@
package config
import (
"os"
"path/filepath"
)
const (
// AppName 应用名称
AppName = "devpack"
// DefaultConfigFileName 默认配置文件名
DefaultConfigFileName = "config.yaml"
// DefaultProfileName 默认 Profile 名
DefaultProfileName = "default"
)
// Paths DevPack 路径管理
type Paths struct {
Home string // ~/.devpack
Config string // ~/.devpack/config.yaml
Profiles string // ~/.devpack/profiles
Packs string // ~/.devpack/packs
Logs string // ~/.devpack/logs
Temp string // ~/.devpack/tmp
}
// DefaultPaths 返回默认路径配置
func DefaultPaths() (*Paths, error) {
home, err := os.UserHomeDir()
if err != nil {
return nil, err
}
devpackHome := filepath.Join(home, ".devpack")
return &Paths{
Home: devpackHome,
Config: filepath.Join(devpackHome, DefaultConfigFileName),
Profiles: filepath.Join(devpackHome, "profiles"),
Packs: filepath.Join(devpackHome, "packs"),
Logs: filepath.Join(devpackHome, "logs"),
Temp: filepath.Join(devpackHome, "tmp"),
}, nil
}
// EnsureDirs 确保所有目录存在
func (p *Paths) EnsureDirs() error {
dirs := []string{p.Home, p.Profiles, p.Packs, p.Logs, p.Temp}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,65 @@
package platform
import (
"context"
"os"
"os/exec"
)
// DarwinPlatform macOS 平台实现
type DarwinPlatform struct {
homeDir string
}
// NewDarwinPlatform 创建 macOS 平台实例
func NewDarwinPlatform() *DarwinPlatform {
home, _ := os.UserHomeDir()
return &DarwinPlatform{homeDir: home}
}
func (p *DarwinPlatform) OS() string { return "darwin" }
func (p *DarwinPlatform) Arch() string { return "amd64" } // TODO: detect properly
func (p *DarwinPlatform) HomeDir() string { return p.homeDir }
func (p *DarwinPlatform) ConfigDir() string {
return p.homeDir + "/Library/Application Support"
}
func (p *DarwinPlatform) DataDir() string {
return p.homeDir + "/Library/Application Support"
}
func (p *DarwinPlatform) GetEnvVar(key string) string {
return os.Getenv(key)
}
func (p *DarwinPlatform) SetEnvVar(ctx context.Context, key, value string) error {
// On macOS, set via launchctl and shell profile
cmd := exec.CommandContext(ctx, "launchctl", "setenv", key, value)
return cmd.Run()
}
func (p *DarwinPlatform) AddToPath(ctx context.Context, dir string) error {
// TODO: Add to shell profile
return nil
}
func (p *DarwinPlatform) IsAdmin() bool {
return os.Geteuid() == 0
}
func (p *DarwinPlatform) PackageManagers() []string {
var pms []string
if _, err := exec.LookPath("brew"); err == nil {
pms = append(pms, "homebrew")
}
return pms
}
func (p *DarwinPlatform) DefaultShell() string {
shell := os.Getenv("SHELL")
if shell != "" {
return shell
}
return "/bin/zsh"
}

View File

@@ -0,0 +1,82 @@
package platform
import (
"context"
"os"
"os/exec"
)
// LinuxPlatform Linux 平台实现
type LinuxPlatform struct {
homeDir string
}
// NewLinuxPlatform 创建 Linux 平台实例
func NewLinuxPlatform() *LinuxPlatform {
home, _ := os.UserHomeDir()
return &LinuxPlatform{homeDir: home}
}
func (p *LinuxPlatform) OS() string { return "linux" }
func (p *LinuxPlatform) Arch() string { return "amd64" } // TODO: detect properly
func (p *LinuxPlatform) HomeDir() string { return p.homeDir }
func (p *LinuxPlatform) ConfigDir() string {
if dir := os.Getenv("XDG_CONFIG_HOME"); dir != "" {
return dir
}
return p.homeDir + "/.config"
}
func (p *LinuxPlatform) DataDir() string {
if dir := os.Getenv("XDG_DATA_HOME"); dir != "" {
return dir
}
return p.homeDir + "/.local/share"
}
func (p *LinuxPlatform) GetEnvVar(key string) string {
return os.Getenv(key)
}
func (p *LinuxPlatform) SetEnvVar(ctx context.Context, key, value string) error {
// TODO: Add to shell profile
return os.Setenv(key, value)
}
func (p *LinuxPlatform) AddToPath(ctx context.Context, dir string) error {
// TODO: Add to shell profile
return nil
}
func (p *LinuxPlatform) IsAdmin() bool {
return os.Geteuid() == 0
}
func (p *LinuxPlatform) PackageManagers() []string {
var pms []string
if _, err := exec.LookPath("apt"); err == nil {
pms = append(pms, "apt")
}
if _, err := exec.LookPath("dnf"); err == nil {
pms = append(pms, "dnf")
}
if _, err := exec.LookPath("yum"); err == nil {
pms = append(pms, "yum")
}
if _, err := exec.LookPath("pacman"); err == nil {
pms = append(pms, "pacman")
}
if _, err := exec.LookPath("snap"); err == nil {
pms = append(pms, "snap")
}
return pms
}
func (p *LinuxPlatform) DefaultShell() string {
shell := os.Getenv("SHELL")
if shell != "" {
return shell
}
return "/bin/bash"
}

View File

@@ -0,0 +1,57 @@
package platform
import (
"context"
"runtime"
)
// Platform 平台适配接口
type Platform interface {
// OS 返回操作系统标识
OS() string
// Arch 返回架构标识
Arch() string
// HomeDir 返回用户主目录
HomeDir() string
// ConfigDir 返回配置文件目录
ConfigDir() string
// DataDir 返回数据目录
DataDir() string
// GetEnvVar 获取环境变量
GetEnvVar(key string) string
// SetEnvVar 设置用户级环境变量
SetEnvVar(ctx context.Context, key, value string) error
// AddToPath 添加目录到 PATH
AddToPath(ctx context.Context, dir string) error
// IsAdmin 是否以管理员身份运行
IsAdmin() bool
// PackageManagers 返回可用的包管理器名称
PackageManagers() []string
// DefaultShell 返回默认 Shell
DefaultShell() string
}
// Detect 检测当前平台并返回对应的 Platform 实现
func Detect() Platform {
switch runtime.GOOS {
case "windows":
return NewWindowsPlatform()
case "darwin":
return NewDarwinPlatform()
case "linux":
return NewLinuxPlatform()
default:
// Fallback to Linux
return NewLinuxPlatform()
}
}

View File

@@ -0,0 +1,83 @@
package platform
import (
"context"
"os"
"os/exec"
)
// WindowsPlatform Windows 平台实现
type WindowsPlatform struct {
homeDir string
}
// NewWindowsPlatform 创建 Windows 平台实例
func NewWindowsPlatform() *WindowsPlatform {
return &WindowsPlatform{
homeDir: os.Getenv("USERPROFILE"),
}
}
func (p *WindowsPlatform) OS() string { return "windows" }
func (p *WindowsPlatform) Arch() string { return os.Getenv("PROCESSOR_ARCHITECTURE") }
func (p *WindowsPlatform) HomeDir() string {
return p.homeDir
}
func (p *WindowsPlatform) ConfigDir() string {
if dir := os.Getenv("APPDATA"); dir != "" {
return dir
}
return p.homeDir + "\\AppData\\Roaming"
}
func (p *WindowsPlatform) DataDir() string {
if dir := os.Getenv("LOCALAPPDATA"); dir != "" {
return dir
}
return p.homeDir + "\\AppData\\Local"
}
func (p *WindowsPlatform) GetEnvVar(key string) string {
return os.Getenv(key)
}
func (p *WindowsPlatform) SetEnvVar(ctx context.Context, key, value string) error {
cmd := exec.CommandContext(ctx, "setx", key, value)
return cmd.Run()
}
func (p *WindowsPlatform) AddToPath(ctx context.Context, dir string) error {
currentPath := os.Getenv("PATH")
newPath := dir + ";" + currentPath
return p.SetEnvVar(ctx, "PATH", newPath)
}
func (p *WindowsPlatform) IsAdmin() bool {
// Check if running as administrator on Windows
cmd := exec.Command("net", "session")
err := cmd.Run()
return err == nil
}
func (p *WindowsPlatform) PackageManagers() []string {
var pms []string
if _, err := exec.LookPath("scoop"); err == nil {
pms = append(pms, "scoop")
}
if _, err := exec.LookPath("choco"); err == nil {
pms = append(pms, "chocolatey")
}
if _, err := exec.LookPath("winget"); err == nil {
pms = append(pms, "winget")
}
return pms
}
func (p *WindowsPlatform) DefaultShell() string {
if _, err := exec.LookPath("pwsh"); err == nil {
return "pwsh" // PowerShell 7+
}
return "powershell" // Windows PowerShell 5.1
}

75
pkg/manifest/manifest.go Normal file
View File

@@ -0,0 +1,75 @@
package manifest
import (
"encoding/json"
"fmt"
"time"
)
// Manifest Pack 清单
type Manifest struct {
// 元信息
Version string `json:"version"` // DevPack 版本
FormatVer string `json:"format_ver"` // Pack 格式版本
PackID string `json:"pack_id"` // 唯一 ID (UUID)
Name string `json:"name"` // Pack 名称
Desc string `json:"description"`
Author string `json:"author"`
CreatedAt time.Time `json:"created_at"`
Tags []string `json:"tags,omitempty"`
// 源环境信息
Source SourceInfo `json:"source"`
// 采集器数据索引
Collectors []CollectorEntry `json:"collectors"`
// 加密信息
Encryption *EncryptionInfo `json:"encryption,omitempty"`
// 校验和
Checksum string `json:"checksum"`
}
// SourceInfo 源环境信息
type SourceInfo struct {
Hostname string `json:"hostname"`
OS string `json:"os"`
OSVersion string `json:"os_version"`
Arch string `json:"arch"`
Username string `json:"username"`
DevPackVersion string `json:"devpack_version"`
}
// CollectorEntry 采集器条目
type CollectorEntry struct {
Name string `json:"name"`
Category string `json:"category"`
Display string `json:"display_name"`
ItemCount int `json:"item_count"`
DataPath string `json:"data_path"`
DataSize int64 `json:"data_size"`
Checksum string `json:"checksum"`
}
// EncryptionInfo 加密信息
type EncryptionInfo struct {
Algorithm string `json:"algorithm"`
KDF string `json:"kdf"`
KDFParams map[string]int `json:"kdf_params"`
EncryptedCollectors []string `json:"encrypted_collectors"`
}
// ToJSON 序列化为 JSON
func (m *Manifest) ToJSON() ([]byte, error) {
return json.MarshalIndent(m, "", " ")
}
// FromJSON 从 JSON 反序列化
func FromJSON(data []byte) (*Manifest, error) {
var m Manifest
if err := json.Unmarshal(data, &m); err != nil {
return nil, fmt.Errorf("parse manifest: %w", err)
}
return &m, nil
}

28
pkg/version/version.go Normal file
View File

@@ -0,0 +1,28 @@
package version
import (
"fmt"
"runtime"
)
// These variables are set at build time via ldflags.
var (
Version = "dev"
Commit = "unknown"
Date = "unknown"
)
// GetVersionString returns a short version string.
func GetVersionString() string {
return Version
}
// GetFullVersionInfo returns complete version information.
func GetFullVersionInfo() string {
return fmt.Sprintf(
"DevPack %s\n Commit: %s\n Built: %s\n Go: %s\n OS/Arch: %s/%s",
Version, Commit, Date,
runtime.Version(),
runtime.GOOS, runtime.GOARCH,
)
}