731 lines
28 KiB
Markdown
731 lines
28 KiB
Markdown
# AEVS CLI — Implementation Plan
|
||
|
||
> Пошаговый план для агента-разработчика.
|
||
|
||
## Общая информация
|
||
|
||
**Проект:** AEVS CLI — инструмент синхронизации `.env` файлов между машинами одного пользователя.
|
||
|
||
**Технологии:**
|
||
- Go (golang)
|
||
- CLI: `github.com/spf13/cobra`
|
||
- S3: `github.com/aws/aws-sdk-go-v2`
|
||
- YAML: `gopkg.in/yaml.v3`
|
||
|
||
**Документация:** `.docs/02-opus-cli-docs/`
|
||
|
||
---
|
||
|
||
## Фаза 1: Инициализация проекта
|
||
|
||
### Шаг 1.1: Создание структуры проекта
|
||
|
||
Создай следующую структуру директорий:
|
||
|
||
```
|
||
aevs/
|
||
├── cmd/
|
||
│ └── aevs/
|
||
│ └── main.go # точка входа
|
||
├── internal/
|
||
│ ├── cli/
|
||
│ │ ├── root.go # root command
|
||
│ │ ├── config.go # aevs config
|
||
│ │ ├── init.go # aevs init
|
||
│ │ ├── push.go # aevs push
|
||
│ │ ├── pull.go # aevs pull
|
||
│ │ ├── list.go # aevs list
|
||
│ │ └── status.go # aevs status
|
||
│ ├── config/
|
||
│ │ ├── global.go # GlobalConfig, загрузка/сохранение
|
||
│ │ ├── project.go # ProjectConfig
|
||
│ │ └── constants.go # константы
|
||
│ ├── storage/
|
||
│ │ ├── storage.go # Storage interface
|
||
│ │ └── s3.go # S3 implementation
|
||
│ ├── archiver/
|
||
│ │ └── archiver.go # tar.gz создание/распаковка
|
||
│ ├── scanner/
|
||
│ │ └── scanner.go # сканирование .env файлов
|
||
│ └── types/
|
||
│ └── types.go # Metadata, FileStatus, etc.
|
||
├── go.mod
|
||
├── go.sum
|
||
└── README.md
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Создана директория `cmd/aevs/`
|
||
- [x] Создана директория `internal/cli/`
|
||
- [x] Создана директория `internal/config/`
|
||
- [x] Создана директория `internal/storage/`
|
||
- [x] Создана директория `internal/archiver/`
|
||
- [x] Создана директория `internal/scanner/`
|
||
- [x] Создана директория `internal/types/`
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 1.2: Инициализация Go модуля
|
||
|
||
```bash
|
||
go mod init github.com/user/aevs
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Выполнена команда `go mod init`
|
||
- [x] Создан файл `go.mod`
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 1.3: Добавление зависимостей
|
||
|
||
```bash
|
||
go get github.com/spf13/cobra@latest
|
||
go get github.com/aws/aws-sdk-go-v2/config@latest
|
||
go get github.com/aws/aws-sdk-go-v2/service/s3@latest
|
||
go get github.com/aws/aws-sdk-go-v2/credentials@latest
|
||
go get gopkg.in/yaml.v3@latest
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Добавлена зависимость `github.com/spf13/cobra`
|
||
- [x] Добавлена зависимость `github.com/aws/aws-sdk-go-v2/config`
|
||
- [x] Добавлена зависимость `github.com/aws/aws-sdk-go-v2/service/s3`
|
||
- [x] Добавлена зависимость `github.com/aws/aws-sdk-go-v2/credentials`
|
||
- [x] Добавлена зависимость `gopkg.in/yaml.v3`
|
||
- [x] Создан файл `go.sum`
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 2: Types & Config
|
||
|
||
### Шаг 2.1: Создай `internal/types/types.go`
|
||
|
||
Реализуй типы из документации (см. `.docs/02-opus-cli-docs/04-types.md`):
|
||
|
||
- `Metadata` — метаданные в storage
|
||
- `ProjectInfo` — для list
|
||
- `FileStatus` — для status
|
||
- `SyncStatus` — константы статусов
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/types/types.go`
|
||
- [x] Реализован тип `Metadata`
|
||
- [x] Реализован тип `ProjectInfo`
|
||
- [x] Реализован тип `FileStatus`
|
||
- [x] Реализован тип `SyncStatus` с константами
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 2.2: Создай `internal/config/constants.go`
|
||
|
||
Константы из документации:
|
||
- `DefaultConfigDir = ".config/aevs"`
|
||
- `DefaultGlobalConfigFile = "config.yaml"`
|
||
- `DefaultProjectConfigFile = "aevs.yaml"`
|
||
- `ArchiveFileName = "envs.tar.gz"`
|
||
- `MetadataFileName = "metadata.json"`
|
||
- и т.д.
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/config/constants.go`
|
||
- [x] Определена константа `DefaultConfigDir`
|
||
- [x] Определена константа `DefaultGlobalConfigFile`
|
||
- [x] Определена константа `DefaultProjectConfigFile`
|
||
- [x] Определена константа `ArchiveFileName`
|
||
- [x] Определена константа `MetadataFileName`
|
||
- [x] Определены дефолтные значения для S3
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 2.3: Создай `internal/config/global.go`
|
||
|
||
```go
|
||
type GlobalConfig struct {
|
||
Storage StorageConfig `yaml:"storage"`
|
||
}
|
||
|
||
type StorageConfig struct {
|
||
Type string `yaml:"type"`
|
||
Endpoint string `yaml:"endpoint"`
|
||
Region string `yaml:"region"`
|
||
Bucket string `yaml:"bucket"`
|
||
AccessKey string `yaml:"access_key"`
|
||
SecretKey string `yaml:"secret_key"`
|
||
}
|
||
```
|
||
|
||
Функции:
|
||
- `LoadGlobalConfig() (*GlobalConfig, error)` — загрузка из `~/.config/aevs/config.yaml`
|
||
- `SaveGlobalConfig(cfg *GlobalConfig) error` — сохранение с правами `0600`
|
||
- `GlobalConfigPath() string` — путь к конфигу
|
||
- `GlobalConfigExists() bool` — проверка существования
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/config/global.go`
|
||
- [x] Реализован тип `GlobalConfig`
|
||
- [x] Реализован тип `StorageConfig`
|
||
- [x] Реализована функция `LoadGlobalConfig()`
|
||
- [x] Реализована функция `SaveGlobalConfig()` с правами `0600`
|
||
- [x] Реализована функция `GlobalConfigPath()`
|
||
- [x] Реализована функция `GlobalConfigExists()`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 2.4: Создай `internal/config/project.go`
|
||
|
||
```go
|
||
type ProjectConfig struct {
|
||
Project string `yaml:"project"`
|
||
Files []string `yaml:"files"`
|
||
}
|
||
```
|
||
|
||
Функции:
|
||
- `LoadProjectConfig(path string) (*ProjectConfig, error)`
|
||
- `SaveProjectConfig(path string, cfg *ProjectConfig) error`
|
||
- `ValidateProjectName(name string) error` — проверка `[a-z0-9_-]`
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/config/project.go`
|
||
- [x] Реализован тип `ProjectConfig`
|
||
- [x] Реализована функция `LoadProjectConfig()`
|
||
- [x] Реализована функция `SaveProjectConfig()`
|
||
- [x] Реализована функция `ValidateProjectName()` с regex `[a-z0-9_-]`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 3: Storage Layer
|
||
|
||
### Шаг 3.1: Создай `internal/storage/storage.go`
|
||
|
||
Интерфейс Storage:
|
||
|
||
```go
|
||
type Storage interface {
|
||
Upload(ctx context.Context, key string, data io.Reader, size int64) error
|
||
Download(ctx context.Context, key string) (io.ReadCloser, error)
|
||
Delete(ctx context.Context, key string) error
|
||
Exists(ctx context.Context, key string) (bool, error)
|
||
List(ctx context.Context, prefix string) ([]string, error)
|
||
ListProjects(ctx context.Context) ([]string, error)
|
||
TestConnection(ctx context.Context) error
|
||
}
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/storage/storage.go`
|
||
- [x] Определён интерфейс `Storage`
|
||
- [x] Метод `Upload` в интерфейсе
|
||
- [x] Метод `Download` в интерфейсе
|
||
- [x] Метод `Delete` в интерфейсе
|
||
- [x] Метод `Exists` в интерфейсе
|
||
- [x] Метод `List` в интерфейсе
|
||
- [x] Метод `ListProjects` в интерфейсе
|
||
- [x] Метод `TestConnection` в интерфейсе
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 3.2: Создай `internal/storage/s3.go`
|
||
|
||
S3 реализация интерфейса Storage:
|
||
|
||
```go
|
||
type S3Storage struct {
|
||
client *s3.Client
|
||
bucket string
|
||
}
|
||
|
||
func NewS3Storage(cfg *config.StorageConfig) (*S3Storage, error)
|
||
```
|
||
|
||
Методы:
|
||
- `Upload` — `s3.PutObject`
|
||
- `Download` — `s3.GetObject`
|
||
- `Delete` — `s3.DeleteObject`
|
||
- `Exists` — `s3.HeadObject`
|
||
- `List` — `s3.ListObjectsV2`
|
||
- `ListProjects` — list с delimiter `/`
|
||
- `TestConnection` — `s3.ListBuckets` или `HeadBucket`
|
||
|
||
**Важно:** Используй `aws.Config` с кастомным endpoint для поддержки MinIO, R2, Spaces.
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/storage/s3.go`
|
||
- [x] Реализована структура `S3Storage`
|
||
- [x] Реализована функция `NewS3Storage()` с кастомным endpoint
|
||
- [x] Реализован метод `Upload()`
|
||
- [x] Реализован метод `Download()`
|
||
- [x] Реализован метод `Delete()`
|
||
- [x] Реализован метод `Exists()`
|
||
- [x] Реализован метод `List()`
|
||
- [x] Реализован метод `ListProjects()` с delimiter
|
||
- [x] Реализован метод `TestConnection()`
|
||
- [x] S3Storage реализует интерфейс Storage
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 4: Scanner & Archiver
|
||
|
||
### Шаг 4.1: Создай `internal/scanner/scanner.go`
|
||
|
||
Сканирование директории на `.env` файлы:
|
||
|
||
```go
|
||
func Scan(rootDir string) ([]string, error)
|
||
```
|
||
|
||
Паттерны для включения:
|
||
- `.env`
|
||
- `.env.*`
|
||
- `*.env`
|
||
|
||
Исключения (директории):
|
||
- `node_modules`, `.git`, `vendor`, `venv`, `.venv`, `__pycache__`, `.idea`, `.vscode`, `dist`, `build`
|
||
|
||
Исключения (файлы):
|
||
- `.env.example`, `.env.sample`, `.env.template`
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/scanner/scanner.go`
|
||
- [x] Определены паттерны включения
|
||
- [x] Определены исключаемые директории
|
||
- [x] Определены исключаемые файлы
|
||
- [x] Реализована функция `Scan()`
|
||
- [x] Функция рекурсивно обходит директории
|
||
- [x] Функция корректно фильтрует файлы
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 4.2: Создай `internal/archiver/archiver.go`
|
||
|
||
```go
|
||
func Create(files []string, rootDir string) (io.Reader, int64, error)
|
||
func Extract(archive io.Reader, destDir string) ([]string, error)
|
||
func List(archive io.Reader) ([]string, error)
|
||
```
|
||
|
||
- `Create` — создаёт tar.gz архив из списка файлов
|
||
- `Extract` — распаковывает архив в директорию, создаёт недостающие папки
|
||
- `List` — возвращает список файлов в архиве без распаковки
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/archiver/archiver.go`
|
||
- [x] Реализована функция `Create()` (tar.gz)
|
||
- [x] Реализована функция `Extract()` с созданием директорий
|
||
- [x] Реализована функция `List()`
|
||
- [x] Пути в архиве сохраняются относительно rootDir
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 5: CLI Commands
|
||
|
||
### Шаг 5.1: Создай `internal/cli/root.go`
|
||
|
||
Root command с cobra:
|
||
|
||
```go
|
||
var rootCmd = &cobra.Command{
|
||
Use: "aevs",
|
||
Short: "Sync .env files between machines",
|
||
}
|
||
|
||
func Execute() error {
|
||
return rootCmd.Execute()
|
||
}
|
||
```
|
||
|
||
Глобальные флаги:
|
||
- `--verbose`, `-v` — verbose output
|
||
- `--debug` — debug output
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/root.go`
|
||
- [x] Определён `rootCmd` с cobra
|
||
- [x] Реализована функция `Execute()`
|
||
- [x] Добавлен флаг `--verbose` / `-v`
|
||
- [x] Добавлен флаг `--debug`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 5.2: Создай `internal/cli/config.go` — команда `aevs config`
|
||
|
||
Интерактивная настройка credentials:
|
||
|
||
1. Запросить storage type (default: `s3`)
|
||
2. Запросить endpoint (default: `https://s3.amazonaws.com`)
|
||
3. Запросить region (default: `us-east-1`)
|
||
4. Запросить bucket name
|
||
5. Запросить access key
|
||
6. Запросить secret key (скрытый ввод)
|
||
7. Проверить подключение (`TestConnection`)
|
||
8. Сохранить конфиг
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/config.go`
|
||
- [x] Определён `configCmd` с cobra
|
||
- [x] Реализован интерактивный ввод storage type
|
||
- [x] Реализован интерактивный ввод endpoint
|
||
- [x] Реализован интерактивный ввод region
|
||
- [x] Реализован интерактивный ввод bucket name
|
||
- [x] Реализован интерактивный ввод access key
|
||
- [x] Реализован скрытый ввод secret key
|
||
- [x] Вызывается `TestConnection()` для проверки
|
||
- [x] Конфиг сохраняется через `SaveGlobalConfig()`
|
||
- [x] Команда зарегистрирована в `rootCmd`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 5.3: Создай `internal/cli/init.go` — команда `aevs init`
|
||
|
||
```bash
|
||
aevs init [project-name]
|
||
```
|
||
|
||
Флаги:
|
||
- `--force`, `-f` — перезаписать существующий конфиг
|
||
|
||
Логика:
|
||
1. Проверить существование `aevs.yaml`
|
||
2. Если существует и нет `--force`:
|
||
- Сканировать на новые файлы
|
||
- Предложить добавить
|
||
3. Если не существует:
|
||
- Сканировать директорию
|
||
- Определить имя проекта (аргумент или имя папки)
|
||
- Создать `aevs.yaml`
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/init.go`
|
||
- [x] Определён `initCmd` с cobra
|
||
- [x] Добавлен флаг `--force` / `-f`
|
||
- [x] Реализована проверка существования `aevs.yaml`
|
||
- [x] Реализовано сканирование директории через `scanner.Scan()`
|
||
- [x] Реализовано определение имени проекта (аргумент или имя папки)
|
||
- [x] Реализовано добавление новых файлов в существующий конфиг
|
||
- [x] Создаётся `aevs.yaml` через `SaveProjectConfig()`
|
||
- [x] Команда зарегистрирована в `rootCmd`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 5.4: Создай `internal/cli/push.go` — команда `aevs push`
|
||
|
||
Флаги:
|
||
- `--config`, `-c` — путь к конфигу
|
||
- `--dry-run` — показать что будет загружено
|
||
|
||
Логика:
|
||
1. Загрузить глобальный конфиг
|
||
2. Загрузить проектный конфиг
|
||
3. Проверить существование всех файлов
|
||
4. Создать tar.gz архив
|
||
5. Загрузить архив в `{project}/envs.tar.gz`
|
||
6. Создать и загрузить metadata.json
|
||
7. Вывести результат
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/push.go`
|
||
- [x] Определён `pushCmd` с cobra
|
||
- [x] Добавлен флаг `--config` / `-c`
|
||
- [x] Добавлен флаг `--dry-run`
|
||
- [x] Реализована загрузка глобального конфига
|
||
- [x] Реализована загрузка проектного конфига
|
||
- [x] Реализована проверка существования файлов
|
||
- [x] Реализовано создание архива через `archiver.Create()`
|
||
- [x] Реализована загрузка архива через `storage.Upload()`
|
||
- [x] Реализовано создание и загрузка `metadata.json`
|
||
- [x] Реализован режим `--dry-run`
|
||
- [x] Команда зарегистрирована в `rootCmd`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 5.5: Создай `internal/cli/pull.go` — команда `aevs pull`
|
||
|
||
```bash
|
||
aevs pull [project-name]
|
||
```
|
||
|
||
Флаги:
|
||
- `--config`, `-c`
|
||
- `--force`, `-f` — перезаписать без подтверждения
|
||
- `--dry-run`
|
||
|
||
Логика:
|
||
1. Определить имя проекта (аргумент или из `aevs.yaml`)
|
||
2. Скачать metadata.json
|
||
3. Скачать envs.tar.gz
|
||
4. Для каждого файла:
|
||
- Если не существует — создать
|
||
- Если существует и отличается — спросить (или --force)
|
||
5. Вывести результат
|
||
|
||
Обработка конфликтов:
|
||
- `[o]verwrite` — перезаписать
|
||
- `[s]kip` — пропустить
|
||
- `[d]iff` — показать diff
|
||
- `[O]verwrite all`
|
||
- `[S]kip all`
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/pull.go`
|
||
- [x] Определён `pullCmd` с cobra
|
||
- [x] Добавлен флаг `--config` / `-c`
|
||
- [x] Добавлен флаг `--force` / `-f`
|
||
- [x] Добавлен флаг `--dry-run`
|
||
- [x] Реализовано определение имени проекта
|
||
- [x] Реализовано скачивание metadata.json
|
||
- [x] Реализовано скачивание envs.tar.gz
|
||
- [x] Реализована распаковка через `archiver.Extract()`
|
||
- [x] Реализована обработка конфликтов (o/s/d/O/S)
|
||
- [x] Реализован режим `--force`
|
||
- [x] Реализован режим `--dry-run`
|
||
- [x] Команда зарегистрирована в `rootCmd`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 5.6: Создай `internal/cli/list.go` — команда `aevs list`
|
||
|
||
Флаги:
|
||
- `--json` — вывод в JSON
|
||
|
||
Логика:
|
||
1. Загрузить глобальный конфиг
|
||
2. Получить список проектов (`ListProjects`)
|
||
3. Для каждого проекта загрузить metadata.json
|
||
4. Вывести таблицу или JSON
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/list.go`
|
||
- [x] Определён `listCmd` с cobra
|
||
- [x] Добавлен флаг `--json`
|
||
- [x] Реализована загрузка глобального конфига
|
||
- [x] Реализовано получение списка проектов через `ListProjects()`
|
||
- [x] Реализована загрузка metadata.json для каждого проекта
|
||
- [x] Реализован вывод в виде таблицы
|
||
- [x] Реализован вывод в формате JSON
|
||
- [x] Команда зарегистрирована в `rootCmd`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 5.7: Создай `internal/cli/status.go` — команда `aevs status`
|
||
|
||
Флаги:
|
||
- `--config`, `-c`
|
||
|
||
Логика:
|
||
1. Загрузить оба конфига
|
||
2. Скачать metadata.json из storage
|
||
3. Для каждого файла:
|
||
- Сравнить размер и хеш локального vs remote
|
||
- Определить статус
|
||
4. Вывести таблицу статусов
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/cli/status.go`
|
||
- [x] Определён `statusCmd` с cobra
|
||
- [x] Добавлен флаг `--config` / `-c`
|
||
- [x] Реализована загрузка обоих конфигов
|
||
- [x] Реализовано скачивание metadata.json
|
||
- [x] Реализовано сравнение локальных и remote файлов
|
||
- [x] Реализовано определение статуса каждого файла
|
||
- [x] Реализован вывод таблицы статусов
|
||
- [x] Команда зарегистрирована в `rootCmd`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 6: Error Handling
|
||
|
||
### Шаг 6.1: Создай `internal/errors/errors.go`
|
||
|
||
Определённые ошибки из документации:
|
||
|
||
```go
|
||
var (
|
||
ErrNoGlobalConfig = errors.New("no storage configured; run 'aevs config' first")
|
||
ErrNoProjectConfig = errors.New("no aevs.yaml found; run 'aevs init' first")
|
||
ErrConfigExists = errors.New("aevs.yaml already exists; use --force to overwrite")
|
||
ErrInvalidProject = errors.New("invalid project name; use only a-z, 0-9, -, _")
|
||
ErrProjectNotFound = errors.New("project not found in storage")
|
||
ErrAccessDenied = errors.New("access denied; check your credentials")
|
||
ErrBucketNotFound = errors.New("bucket not found")
|
||
ErrFileNotFound = errors.New("file not found")
|
||
ErrNoEnvFiles = errors.New("no env files found")
|
||
)
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `internal/errors/errors.go`
|
||
- [x] Определена ошибка `ErrNoGlobalConfig`
|
||
- [x] Определена ошибка `ErrNoProjectConfig`
|
||
- [x] Определена ошибка `ErrConfigExists`
|
||
- [x] Определена ошибка `ErrInvalidProject`
|
||
- [x] Определена ошибка `ErrProjectNotFound`
|
||
- [x] Определена ошибка `ErrAccessDenied`
|
||
- [x] Определена ошибка `ErrBucketNotFound`
|
||
- [x] Определена ошибка `ErrFileNotFound`
|
||
- [x] Определена ошибка `ErrNoEnvFiles`
|
||
- [x] Код компилируется без ошибок
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 6.2: Exit codes
|
||
|
||
Реализуй exit codes:
|
||
- `0` — Success
|
||
- `1` — General error
|
||
- `2` — Configuration error
|
||
- `3` — File system error
|
||
- `4` — Storage error
|
||
- `5` — Network error
|
||
- `130` — Interrupted
|
||
|
||
**Чеклист:**
|
||
- [x] Определены константы exit codes
|
||
- [x] Реализована функция для определения exit code по типу ошибки
|
||
- [x] Exit codes используются в `main.go`
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 7: Entry Point
|
||
|
||
### Шаг 7.1: Создай `cmd/aevs/main.go`
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"os"
|
||
"aevs/internal/cli"
|
||
)
|
||
|
||
func main() {
|
||
if err := cli.Execute(); err != nil {
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `cmd/aevs/main.go`
|
||
- [x] Импортирован пакет `cli`
|
||
- [x] Вызывается `cli.Execute()`
|
||
- [x] Используются корректные exit codes
|
||
- [x] Приложение компилируется: `go build ./cmd/aevs`
|
||
- [x] Приложение запускается: `./aevs --help`
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 8: Тестирование
|
||
|
||
### Шаг 8.1: Unit tests
|
||
|
||
Создай тесты для:
|
||
- `config/` — загрузка/сохранение конфигов
|
||
- `scanner/` — сканирование файлов
|
||
- `archiver/` — создание/распаковка архивов
|
||
- `storage/` — mock тесты для S3
|
||
|
||
**Чеклист:**
|
||
- [ ] Создан файл `internal/config/global_test.go`
|
||
- [ ] Создан файл `internal/config/project_test.go`
|
||
- [ ] Создан файл `internal/scanner/scanner_test.go`
|
||
- [ ] Создан файл `internal/archiver/archiver_test.go`
|
||
- [ ] Создан файл `internal/storage/s3_test.go` (mock)
|
||
- [ ] Все тесты проходят: `go test ./...`
|
||
- [ ] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 8.2: Integration tests
|
||
|
||
Тесты с реальным S3 (или MinIO в Docker):
|
||
- Полный цикл: config → init → push → pull → status → list
|
||
|
||
**Чеклист:**
|
||
- [ ] Настроен MinIO в Docker для тестов
|
||
- [ ] Создан файл интеграционных тестов
|
||
- [ ] Тест полного цикла работает
|
||
- [ ] Тесты можно запустить локально
|
||
- [ ] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Фаза 9: Build & Release
|
||
|
||
### Шаг 9.1: Makefile
|
||
|
||
```makefile
|
||
.PHONY: build test clean
|
||
|
||
build:
|
||
go build -o bin/aevs ./cmd/aevs
|
||
|
||
test:
|
||
go test ./...
|
||
|
||
clean:
|
||
rm -rf bin/
|
||
```
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `Makefile`
|
||
- [x] Добавлена цель `build`
|
||
- [x] Добавлена цель `test`
|
||
- [x] Добавлена цель `clean`
|
||
- [x] `make build` работает
|
||
- [x] `make test` работает
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
### Шаг 9.2: README.md
|
||
|
||
Создай README с:
|
||
- Описанием проекта
|
||
- Установкой
|
||
- Quick start
|
||
- Командами
|
||
|
||
**Чеклист:**
|
||
- [x] Создан файл `README.md`
|
||
- [x] Добавлено описание проекта
|
||
- [x] Добавлены инструкции по установке
|
||
- [x] Добавлен раздел Quick Start
|
||
- [x] Добавлено описание всех команд
|
||
- [x] Добавлены примеры использования
|
||
- [x] Проверен другой моделью и составлен следующий шаг
|
||
|
||
---
|
||
|
||
## Порядок реализации (приоритет)
|
||
|
||
1. **Фаза 1** — структура проекта
|
||
2. **Фаза 2** — types & config
|
||
3. **Фаза 4** — scanner & archiver (можно тестировать локально)
|
||
4. **Фаза 3** — storage layer
|
||
5. **Фаза 5** — CLI commands в порядке:
|
||
- `root.go`
|
||
- `config.go` (нужен для всего остального)
|
||
- `init.go`
|
||
- `push.go`
|
||
- `pull.go`
|
||
- `list.go`
|
||
- `status.go`
|
||
6. **Фаза 6** — error handling (можно делать параллельно)
|
||
7. **Фаза 7** — entry point
|
||
8. **Фаза 8** — тесты
|
||
9. **Фаза 9** — build & docs
|
||
|
||
---
|
||
|
||
## Ссылки на документацию
|
||
|
||
- [01-overview.md](./../02-opus-cli-docs/01-overview.md) — обзор и архитектура
|
||
- [02-configuration.md](./../02-opus-cli-docs/02-configuration.md) — конфигурация
|
||
- [03-commands.md](./../02-opus-cli-docs/03-commands.md) — детали команд
|
||
- [04-types.md](./../02-opus-cli-docs/04-types.md) — Go типы
|
||
- [05-scenarios.md](./../02-opus-cli-docs/05-scenarios.md) — сценарии использования
|
||
- [06-errors.md](./../02-opus-cli-docs/06-errors.md) — обработка ошибок
|