Merge feature/vanity-import-readme
All checks were successful
check / check (push) Successful in 2m36s

This commit is contained in:
2026-06-10 11:37:42 -07:00
66 changed files with 299 additions and 237 deletions

View File

@@ -20,8 +20,8 @@ builds:
- arm64 - arm64
ldflags: ldflags:
- -s -w - -s -w
- -X 'git.eeqj.de/sneak/vaultik/internal/globals.Version={{ .Version }}' - -X 'sneak.berlin/go/vaultik/internal/globals.Version={{ .Version }}'
- -X 'git.eeqj.de/sneak/vaultik/internal/globals.Commit={{ .Commit }}' - -X 'sneak.berlin/go/vaultik/internal/globals.Commit={{ .Commit }}'
archives: archives:
- id: default - id: default

View File

@@ -42,7 +42,7 @@ COPY . .
RUN make test RUN make test
# Build with CGO enabled (required for mattn/go-sqlite3) # Build with CGO enabled (required for mattn/go-sqlite3)
RUN CGO_ENABLED=1 go build -ldflags "-X 'git.eeqj.de/sneak/vaultik/internal/globals.Version=${VERSION}' -X 'git.eeqj.de/sneak/vaultik/internal/globals.Commit=$(git rev-parse HEAD 2>/dev/null || echo unknown)'" -o /vaultik ./cmd/vaultik RUN CGO_ENABLED=0 go build -ldflags "-X 'sneak.berlin/go/vaultik/internal/globals.Version=${VERSION}' -X 'sneak.berlin/go/vaultik/internal/globals.Commit=$(git rev-parse HEAD 2>/dev/null || echo unknown)'" -o /vaultik ./cmd/vaultik
# Runtime stage # Runtime stage
# alpine:3.21, 2026-02-25 # alpine:3.21, 2026-02-25

View File

@@ -7,8 +7,8 @@ VERSION := 1.0.0-rc.1
GIT_REVISION := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown") GIT_REVISION := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
# Linker flags # Linker flags
LDFLAGS := -X 'git.eeqj.de/sneak/vaultik/internal/globals.Version=$(VERSION)' \ LDFLAGS := -X 'sneak.berlin/go/vaultik/internal/globals.Version=$(VERSION)' \
-X 'git.eeqj.de/sneak/vaultik/internal/globals.Commit=$(GIT_REVISION)' -X 'sneak.berlin/go/vaultik/internal/globals.Commit=$(GIT_REVISION)'
# Default target # Default target
all: vaultik all: vaultik

107
README.md
View File

@@ -6,6 +6,32 @@ remote S3-compatible object store. It requires no private keys, secrets, or
credentials (other than those required to PUT to encrypted object storage, credentials (other than those required to PUT to encrypted object storage,
such as S3 API keys) stored on the backed-up system. such as S3 API keys) stored on the backed-up system.
## quickstart
```sh
# install
go install sneak.berlin/go/vaultik/cmd/vaultik@latest
# create a default config file (prints the path it wrote to)
vaultik config init
# generate an age keypair; keep key.txt somewhere safe and offline —
# you need it to restore, and the backed-up machine does not need it
age-keygen -o key.txt
grep 'public key' key.txt
# configure the encryption key and backup destination
vaultik config set age_recipients.0 age1YOUR_PUBLIC_KEY_HERE
vaultik config set storage_url "file:///Volumes/usbstick/mybackup"
# back up your home directory (the default config includes a "home"
# snapshot of ~ with sensible excludes)
vaultik snapshot create
# see what you have
vaultik snapshot list
```
Features: Features:
* modern encryption ([age](https://age-encryption.org/), X25519 + XChaCha20-Poly1305) * modern encryption ([age](https://age-encryption.org/), X25519 + XChaCha20-Poly1305)
@@ -38,43 +64,19 @@ Requirements that no existing tool meets:
* encrypted * encrypted
* s3 compatible without an intermediate step or tool * s3 compatible without an intermediate step or tool
## install ## daily use
```sh ```sh
go install git.eeqj.de/sneak/vaultik@latest # verify a snapshot (shallow: checks all blobs exist)
```
## quick start
```sh
# 1. Install
go install git.eeqj.de/sneak/vaultik@latest
# 2. Generate an age keypair (store the private key somewhere safe, offline)
age-keygen -o key.txt
# the public key is printed to stdout and also in key.txt
# 3. Create a default config file
vaultik config init
# Writes to the platform config directory with commented defaults:
# macOS: ~/Library/Application Support/vaultik/config.yml
# Linux: ~/.config/vaultik/config.yml
# root: /etc/vaultik/config.yml
# 4. Edit the config: set age_recipients, snapshots, and storage_url
vaultik config edit # opens $EDITOR
# or set individual values:
vaultik config set storage_url "file:///mnt/backups"
vaultik config get storage_url
# 5. Run your first backup
vaultik snapshot create
# 6. Verify it worked
vaultik snapshot list
vaultik snapshot verify <snapshot-id> vaultik snapshot verify <snapshot-id>
# 7. Set up a daily cron job (keeps last 4 weeks of snapshots) # deep verify (downloads and cryptographically verifies every blob)
VAULTIK_AGE_SECRET_KEY='AGE-SECRET-KEY-...' vaultik snapshot verify --deep <snapshot-id>
# restore (requires the private key)
VAULTIK_AGE_SECRET_KEY='AGE-SECRET-KEY-...' vaultik restore <snapshot-id> /tmp/restored
# daily cron job: back up, keep a 4-week rolling window of snapshots
# 0 3 * * * vaultik snapshot create --cron --prune --keep-newer-than 4w # 0 3 * * * vaultik snapshot create --cron --prune --keep-newer-than 4w
``` ```
@@ -120,22 +122,22 @@ vaultik version
### command details ### command details
**config init**: Write a default config file with commented explanations for **`config init`**: Write a default config file with commented explanations for
every setting. Writes to the path from `--config`, `$VAULTIK_CONFIG`, or the every setting. Writes to the path from `--config`, `$VAULTIK_CONFIG`, or the
platform config directory (`~/Library/Application Support/vaultik/` on macOS, platform config directory (`~/Library/Application Support/vaultik/` on macOS,
`~/.config/vaultik/` on Linux, `/etc/vaultik/` as root). Refuses to overwrite an `~/.config/vaultik/` on Linux, `/etc/vaultik/` as root). Refuses to overwrite an
existing file. Created with mode `0600` since it will contain credentials. existing file. Created with mode `0600` since it will contain credentials.
**config edit**: Open the config file in `$EDITOR` (falls back to `vi`). **`config edit`**: Open the config file in `$EDITOR` (falls back to `vi`).
**config get**: Print a config value addressed by dotted YAML path **`config get`**: Print a config value addressed by dotted YAML path
(e.g. `vaultik config get s3.bucket`). Non-scalar values print as YAML. (e.g. `vaultik config get s3.bucket`). Non-scalar values print as YAML.
**config set**: Set a scalar config value by dotted YAML path **`config set`**: Set a scalar config value by dotted YAML path
(e.g. `vaultik config set compression_level 9`). Comments and formatting (e.g. `vaultik config set compression_level 9`). Comments and formatting
in the file are preserved; intermediate maps are created as needed. in the file are preserved; intermediate maps are created as needed.
**snapshot create**: Perform incremental backup of configured snapshots. **`snapshot create`**: Perform incremental backup of configured snapshots.
* Optional snapshot names argument to create specific snapshots (default: all) * Optional snapshot names argument to create specific snapshots (default: all)
* `--cron`: Silent unless error (for crontab) * `--cron`: Silent unless error (for crontab)
* `--prune`: After backup, drop older snapshots of each backed-up name and * `--prune`: After backup, drop older snapshots of each backed-up name and
@@ -145,16 +147,16 @@ in the file are preserved; intermediate maps are created as needed.
this duration instead of only the latest (e.g. `4w`, `30d`, `6mo`, `1y`) this duration instead of only the latest (e.g. `4w`, `30d`, `6mo`, `1y`)
* `--skip-errors`: Skip file read errors (log them loudly but continue) * `--skip-errors`: Skip file read errors (log them loudly but continue)
**snapshot list**: List all snapshots with their timestamps and sizes. **`snapshot list`**: List all snapshots with their timestamps and sizes.
* `--json`: Output in JSON format * `--json`: Output in JSON format
**snapshot verify**: Verify snapshot integrity. **`snapshot verify`**: Verify snapshot integrity.
* Default (shallow): checks that all blobs referenced in the manifest exist in storage * Default (shallow): checks that all blobs referenced in the manifest exist in storage
* `--deep`: Downloads and decrypts each blob, verifies chunk hashes against the * `--deep`: Downloads and decrypts each blob, verifies chunk hashes against the
encrypted metadata database encrypted metadata database
* `--json`: Output results as JSON * `--json`: Output results as JSON
**snapshot purge**: Remove old snapshots based on criteria. Retention is **`snapshot purge`**: Remove old snapshots based on criteria. Retention is
per-snapshot-name (`--keep-latest` keeps the latest of each name, not the per-snapshot-name (`--keep-latest` keeps the latest of each name, not the
latest globally). latest globally).
* `--keep-latest`: Keep only the most recent snapshot of each name * `--keep-latest`: Keep only the most recent snapshot of each name
@@ -162,42 +164,42 @@ latest globally).
* `--snapshot <name>`: Restrict to specific snapshot names (repeat for multiple) * `--snapshot <name>`: Restrict to specific snapshot names (repeat for multiple)
* `--force`: Skip confirmation prompt * `--force`: Skip confirmation prompt
**snapshot remove**: Remove a specific snapshot from the local database. **`snapshot remove`**: Remove a specific snapshot from the local database.
* `--remote`: Also remove snapshot metadata from remote storage * `--remote`: Also remove snapshot metadata from remote storage
* `--all`: Remove all snapshots (requires `--force`) * `--all`: Remove all snapshots (requires `--force`)
* `--dry-run`: Show what would be deleted without deleting * `--dry-run`: Show what would be deleted without deleting
* `--force`: Skip confirmation prompt * `--force`: Skip confirmation prompt
* `--json`: Output result as JSON * `--json`: Output result as JSON
**snapshot prune**: Clean orphaned data from the local database (files, **`snapshot prune`**: Clean orphaned data from the local database (files,
chunks, blobs not referenced by any snapshot). chunks, blobs not referenced by any snapshot).
**snapshot cleanup**: Remove stale local snapshot records that have no **`snapshot cleanup`**: Remove stale local snapshot records that have no
corresponding metadata in remote storage. These are typically left behind corresponding metadata in remote storage. These are typically left behind
by incomplete or interrupted backups. Does not touch remote storage. by incomplete or interrupted backups. Does not touch remote storage.
**restore**: Restore files from a backup snapshot. **`restore`**: Restore files from a backup snapshot.
* Requires `VAULTIK_AGE_SECRET_KEY` environment variable * Requires `VAULTIK_AGE_SECRET_KEY` environment variable
* Optional path arguments to restore specific files/directories (default: all) * Optional path arguments to restore specific files/directories (default: all)
* Preserves file permissions, timestamps, ownership (ownership requires root), * Preserves file permissions, timestamps, ownership (ownership requires root),
symlinks, and empty directories symlinks, and empty directories
* `--verify`: After restoring, verify every file's chunk hashes match * `--verify`: After restoring, verify every file's chunk hashes match
**prune**: Remove unreferenced blobs from remote storage. **`prune`**: Remove unreferenced blobs from remote storage.
* Scans all snapshot manifests for referenced blobs, deletes any blob not referenced * Scans all snapshot manifests for referenced blobs, deletes any blob not referenced
* `--force`: Skip confirmation prompt * `--force`: Skip confirmation prompt
* `--json`: Output stats as JSON * `--json`: Output stats as JSON
**info**: Display system configuration, storage settings, encryption **`info`**: Display system configuration, storage settings, encryption
recipients, and local database statistics. recipients, and local database statistics.
**remote info**: Show detailed remote storage information including per-snapshot **`remote info`**: Show detailed remote storage information including per-snapshot
metadata sizes, blob counts, and orphaned blob detection. metadata sizes, blob counts, and orphaned blob detection.
* `--json`: Output as JSON * `--json`: Output as JSON
**store info**: Display storage backend type and statistics. **`store info`**: Display storage backend type and statistics.
**database purge**: Delete the local SQLite state database entirely. Remote **`database purge`**: Delete the local SQLite state database entirely. Remote
storage is unaffected; the next backup will do a full scan and re-deduplicate storage is unaffected; the next backup will do a full scan and re-deduplicate
against existing remote blobs. against existing remote blobs.
* `--force`: Skip confirmation prompt * `--force`: Skip confirmation prompt
@@ -300,7 +302,8 @@ Snapshot IDs follow the format `<hostname>_<snapshot-name>_<RFC3339-timestamp>`
## configuration reference ## configuration reference
See `config.example.yml` for a complete annotated example. Key fields: Run `vaultik config init` to generate a fully commented config file.
Key fields:
| Field | Default | Description | | Field | Default | Description |
|-------|---------|-------------| |-------|---------|-------------|
@@ -313,7 +316,7 @@ See `config.example.yml` for a complete annotated example. Key fields:
| `blob_size_limit` | `10GB` | Maximum blob size before splitting | | `blob_size_limit` | `10GB` | Maximum blob size before splitting |
| `compression_level` | `3` | zstd compression level (1-19) | | `compression_level` | `3` | zstd compression level (1-19) |
| `hostname` | system hostname | Hostname used in snapshot IDs | | `hostname` | system hostname | Hostname used in snapshot IDs |
| `index_path` | `~/.local/share/.../index.sqlite` | Local SQLite index path | | `index_path` | platform data dir | Local SQLite index path |
--- ---

View File

@@ -5,7 +5,7 @@ import (
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"git.eeqj.de/sneak/vaultik/internal/cli" "sneak.berlin/go/vaultik/internal/cli"
) )
func main() { func main() {

2
go.mod
View File

@@ -1,4 +1,4 @@
module git.eeqj.de/sneak/vaultik module sneak.berlin/go/vaultik
go 1.26.1 go 1.26.1

View File

@@ -23,12 +23,12 @@ import (
"sync" "sync"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/blobgen"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/spf13/afero" "github.com/spf13/afero"
"sneak.berlin/go/vaultik/internal/blobgen"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/types"
) )
// BlobHandler is a callback function invoked when a blob is finalized and ready for upload. // BlobHandler is a callback function invoked when a blob is finalized and ready for upload.

View File

@@ -10,11 +10,11 @@ import (
"testing" "testing"
"filippo.io/age" "filippo.io/age"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/klauspost/compress/zstd" "github.com/klauspost/compress/zstd"
"github.com/spf13/afero" "github.com/spf13/afero"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/types"
) )
const ( const (

View File

@@ -10,16 +10,16 @@ import (
"syscall" "syscall"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/globals"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/pidlock"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/storage"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/adrg/xdg" "github.com/adrg/xdg"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/globals"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/pidlock"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/storage"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// AppOptions contains common options for creating the fx application. // AppOptions contains common options for creating the fx application.
@@ -125,7 +125,7 @@ func RunApp(ctx context.Context, app *fx.App) error {
// It acquires a PID lock before starting to prevent concurrent instances. // It acquires a PID lock before starting to prevent concurrent instances.
func RunWithApp(ctx context.Context, opts AppOptions) error { func RunWithApp(ctx context.Context, opts AppOptions) error {
// Acquire PID lock to prevent concurrent instances // Acquire PID lock to prevent concurrent instances
lockDir := filepath.Join(xdg.DataHome, "berlin.sneak.app.vaultik") lockDir := filepath.Join(xdg.DataHome, "vaultik")
lock, err := pidlock.Acquire(lockDir) lock, err := pidlock.Acquire(lockDir)
if err != nil { if err != nil {
if errors.Is(err, pidlock.ErrAlreadyRunning) { if errors.Is(err, pidlock.ErrAlreadyRunning) {

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -12,7 +13,7 @@ import (
) )
const defaultConfigTemplate = `# vaultik configuration const defaultConfigTemplate = `# vaultik configuration
# Documentation: https://git.eeqj.de/sneak/vaultik # Documentation: https://sneak.berlin/go/vaultik
# ─── REQUIRED ──────────────────────────────────────────────────────────────── # ─── REQUIRED ────────────────────────────────────────────────────────────────
@@ -195,7 +196,9 @@ storage_url: ""
# hostname: myserver # hostname: myserver
# Path to the local SQLite index database. # Path to the local SQLite index database.
# Default: ~/.local/share/berlin.sneak.app.vaultik/index.sqlite # Default: the platform data directory, e.g.
# macOS: ~/Library/Application Support/vaultik/index.sqlite
# Linux: ~/.local/share/vaultik/index.sqlite
# index_path: /path/to/index.sqlite # index_path: /path/to/index.sqlite
` `
@@ -387,8 +390,9 @@ func loadYAMLFile(path string) (*yaml.Node, error) {
return &root, nil return &root, nil
} }
// yamlPathGet navigates a dotted key path through mapping nodes and // yamlPathGet navigates a dotted key path through mapping and sequence
// returns the value node. // nodes and returns the value node. Numeric path components index into
// sequences (e.g. "age_recipients.0").
func yamlPathGet(root *yaml.Node, keys []string) (*yaml.Node, error) { func yamlPathGet(root *yaml.Node, keys []string) (*yaml.Node, error) {
node := root node := root
if node.Kind == yaml.DocumentNode { if node.Kind == yaml.DocumentNode {
@@ -399,19 +403,30 @@ func yamlPathGet(root *yaml.Node, keys []string) (*yaml.Node, error) {
} }
for i, key := range keys { for i, key := range keys {
if node.Kind != yaml.MappingNode { switch node.Kind {
return nil, fmt.Errorf("key %q is not a map", strings.Join(keys[:i], ".")) case yaml.MappingNode:
} found := false
found := false for j := 0; j+1 < len(node.Content); j += 2 {
for j := 0; j+1 < len(node.Content); j += 2 { if node.Content[j].Value == key {
if node.Content[j].Value == key { node = node.Content[j+1]
node = node.Content[j+1] found = true
found = true break
break }
} }
} if !found {
if !found { return nil, fmt.Errorf("key not found: %s", strings.Join(keys[:i+1], "."))
return nil, fmt.Errorf("key not found: %s", strings.Join(keys[:i+1], ".")) }
case yaml.SequenceNode:
idx, err := strconv.Atoi(key)
if err != nil {
return nil, fmt.Errorf("key %q is a list; use a numeric index", strings.Join(keys[:i], "."))
}
if idx < 0 || idx >= len(node.Content) {
return nil, fmt.Errorf("index %d out of range for %s (len %d)", idx, strings.Join(keys[:i], "."), len(node.Content))
}
node = node.Content[idx]
default:
return nil, fmt.Errorf("key %q is not a map or list", strings.Join(keys[:i], "."))
} }
} }
@@ -419,7 +434,9 @@ func yamlPathGet(root *yaml.Node, keys []string) (*yaml.Node, error) {
} }
// yamlPathSet navigates a dotted key path, creating intermediate maps as // yamlPathSet navigates a dotted key path, creating intermediate maps as
// needed, and sets the final key to the given scalar value. // needed, and sets the final key to the given scalar value. Numeric path
// components index into sequences; an index equal to the sequence length
// appends a new element (e.g. "age_recipients.1" on a 1-element list).
func yamlPathSet(root *yaml.Node, keys []string, value string) error { func yamlPathSet(root *yaml.Node, keys []string, value string) error {
node := root node := root
if node.Kind == yaml.DocumentNode { if node.Kind == yaml.DocumentNode {
@@ -430,41 +447,67 @@ func yamlPathSet(root *yaml.Node, keys []string, value string) error {
} }
for i, key := range keys { for i, key := range keys {
if node.Kind != yaml.MappingNode {
return fmt.Errorf("key %q is not a map", strings.Join(keys[:i], "."))
}
last := i == len(keys)-1 last := i == len(keys)-1
var valueNode *yaml.Node switch node.Kind {
for j := 0; j+1 < len(node.Content); j += 2 { case yaml.MappingNode:
if node.Content[j].Value == key { var valueNode *yaml.Node
valueNode = node.Content[j+1] for j := 0; j+1 < len(node.Content); j += 2 {
break if node.Content[j].Value == key {
valueNode = node.Content[j+1]
break
}
} }
}
if valueNode == nil { if valueNode == nil {
keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key} keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key}
valueNode = &yaml.Node{Kind: yaml.MappingNode} valueNode = &yaml.Node{Kind: yaml.MappingNode}
if last { if last {
valueNode = &yaml.Node{Kind: yaml.ScalarNode, Value: value} valueNode = &yaml.Node{Kind: yaml.ScalarNode, Value: value}
}
node.Content = append(node.Content, keyNode, valueNode)
} else if last {
setScalar(valueNode, value)
} }
node.Content = append(node.Content, keyNode, valueNode)
} else if last {
valueNode.Kind = yaml.ScalarNode
valueNode.Tag = ""
valueNode.Value = value
valueNode.Content = nil
valueNode.Style = 0
}
node = valueNode node = valueNode
case yaml.SequenceNode:
idx, err := strconv.Atoi(key)
if err != nil {
return fmt.Errorf("key %q is a list; use a numeric index", strings.Join(keys[:i], "."))
}
if idx < 0 || idx > len(node.Content) {
return fmt.Errorf("index %d out of range for %s (len %d)", idx, strings.Join(keys[:i], "."), len(node.Content))
}
if idx == len(node.Content) {
newNode := &yaml.Node{Kind: yaml.MappingNode}
if last {
newNode = &yaml.Node{Kind: yaml.ScalarNode, Value: value}
}
node.Content = append(node.Content, newNode)
} else if last {
setScalar(node.Content[idx], value)
}
node = node.Content[idx]
default:
return fmt.Errorf("key %q is not a map or list", strings.Join(keys[:i], "."))
}
} }
return nil return nil
} }
// setScalar overwrites a node in place with a plain scalar value.
func setScalar(n *yaml.Node, value string) {
n.Kind = yaml.ScalarNode
n.Tag = ""
n.Value = value
n.Content = nil
n.Style = 0
}
// configPathForInit returns the config path to write, checking --config flag, // configPathForInit returns the config path to write, checking --config flag,
// VAULTIK_CONFIG env, and the platform default. // VAULTIK_CONFIG env, and the platform default.
func configPathForInit() string { func configPathForInit() string {

View File

@@ -4,8 +4,8 @@ import (
"strings" "strings"
"testing" "testing"
"git.eeqj.de/sneak/vaultik/internal/config"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sneak.berlin/go/vaultik/internal/config"
) )
// TestDefaultConfigTemplateParses ensures the init template is valid YAML // TestDefaultConfigTemplateParses ensures the init template is valid YAML
@@ -45,6 +45,8 @@ func TestDefaultConfigTemplateParses(t *testing.T) {
const testYAML = `# top comment const testYAML = `# top comment
compression_level: 3 compression_level: 3
age_recipients:
- age1aaa
s3: s3:
bucket: oldbucket # inline comment bucket: oldbucket # inline comment
region: us-east-1 region: us-east-1
@@ -74,6 +76,9 @@ func TestYAMLPathGet(t *testing.T) {
{"compression_level", "3", false}, {"compression_level", "3", false},
{"s3.bucket", "oldbucket", false}, {"s3.bucket", "oldbucket", false},
{"s3.region", "us-east-1", false}, {"s3.region", "us-east-1", false},
{"age_recipients.0", "age1aaa", false},
{"age_recipients.5", "", true},
{"age_recipients.notanumber", "", true},
{"s3.nonexistent", "", true}, {"s3.nonexistent", "", true},
{"nonexistent", "", true}, {"nonexistent", "", true},
{"compression_level.sub", "", true}, {"compression_level.sub", "", true},
@@ -114,6 +119,17 @@ func TestYAMLPathSet(t *testing.T) {
t.Fatalf("set newmap.newkey: %v", err) t.Fatalf("set newmap.newkey: %v", err)
} }
// Overwrite a sequence element and append a new one
if err := yamlPathSet(root, splitPath("age_recipients.0"), "age1bbb"); err != nil {
t.Fatalf("set age_recipients.0: %v", err)
}
if err := yamlPathSet(root, splitPath("age_recipients.1"), "age1ccc"); err != nil {
t.Fatalf("append age_recipients.1: %v", err)
}
if err := yamlPathSet(root, splitPath("age_recipients.5"), "age1ddd"); err == nil {
t.Error("expected out-of-range append to fail")
}
// Round-trip and verify values + comment preservation // Round-trip and verify values + comment preservation
out, err := yaml.Marshal(root) out, err := yaml.Marshal(root)
if err != nil { if err != nil {
@@ -121,7 +137,7 @@ func TestYAMLPathSet(t *testing.T) {
} }
text := string(out) text := string(out)
for _, want := range []string{"newbucket", "s3.example.com", "newkey: val", "# top comment", "# inline comment"} { for _, want := range []string{"newbucket", "s3.example.com", "newkey: val", "# top comment", "# inline comment", "age1bbb", "age1ccc"} {
if !contains(text, want) { if !contains(text, want) {
t.Errorf("round-tripped YAML missing %q:\n%s", want, text) t.Errorf("round-tripped YAML missing %q:\n%s", want, text)
} }

View File

@@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/log"
) )
// NewDatabaseCommand creates the database command group // NewDatabaseCommand creates the database command group

View File

@@ -4,10 +4,10 @@ import (
"context" "context"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// NewInfoCommand creates the info command // NewInfoCommand creates the info command

View File

@@ -4,10 +4,10 @@ import (
"context" "context"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// NewPruneCommand creates the prune command // NewPruneCommand creates the prune command

View File

@@ -4,10 +4,10 @@ import (
"context" "context"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// NewRemoteCommand creates the remote command and subcommands // NewRemoteCommand creates the remote command and subcommands

View File

@@ -4,13 +4,13 @@ import (
"context" "context"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/globals"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/storage"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/globals"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/storage"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// RestoreOptions contains options for the restore command // RestoreOptions contains options for the restore command

View File

@@ -6,10 +6,10 @@ import (
"io" "io"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// NewSnapshotCommand creates the snapshot command and subcommands // NewSnapshotCommand creates the snapshot command and subcommands

View File

@@ -6,10 +6,10 @@ import (
"strings" "strings"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/storage"
) )
// StoreApp contains dependencies for store commands // StoreApp contains dependencies for store commands

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"runtime" "runtime"
"git.eeqj.de/sneak/vaultik/internal/globals"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sneak.berlin/go/vaultik/internal/globals"
) )
// NewVersionCommand creates the version command // NewVersionCommand creates the version command

View File

@@ -9,13 +9,13 @@ import (
"filippo.io/age" "filippo.io/age"
"git.eeqj.de/sneak/smartconfig" "git.eeqj.de/sneak/smartconfig"
"git.eeqj.de/sneak/vaultik/internal/log"
"github.com/adrg/xdg" "github.com/adrg/xdg"
"go.uber.org/fx" "go.uber.org/fx"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sneak.berlin/go/vaultik/internal/log"
) )
const appName = "berlin.sneak.app.vaultik" const appName = "vaultik"
// expandTilde expands ~ at the start of a path to the user's home directory. // expandTilde expands ~ at the start of a path to the user's home directory.
func expandTilde(path string) string { func expandTilde(path string) string {

View File

@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
func TestBlobChunkRepository(t *testing.T) { func TestBlobChunkRepository(t *testing.T) {

View File

@@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/log"
) )
type BlobRepository struct { type BlobRepository struct {

View File

@@ -5,7 +5,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
func TestBlobRepository(t *testing.T) { func TestBlobRepository(t *testing.T) {

View File

@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
// TestCascadeDeleteDebug tests cascade delete with debug output // TestCascadeDeleteDebug tests cascade delete with debug output

View File

@@ -5,7 +5,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
type ChunkFileRepository struct { type ChunkFileRepository struct {

View File

@@ -5,7 +5,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
func TestChunkFileRepository(t *testing.T) { func TestChunkFileRepository(t *testing.T) {

View File

@@ -5,7 +5,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"git.eeqj.de/sneak/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/log"
) )
type ChunkRepository struct { type ChunkRepository struct {

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"testing" "testing"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
func TestChunkRepository(t *testing.T) { func TestChunkRepository(t *testing.T) {

View File

@@ -22,8 +22,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.eeqj.de/sneak/vaultik/internal/log"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
"sneak.berlin/go/vaultik/internal/log"
) )
//go:embed schema/*.sql //go:embed schema/*.sql

View File

@@ -5,7 +5,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
type FileChunkRepository struct { type FileChunkRepository struct {

View File

@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
func TestFileChunkRepository(t *testing.T) { func TestFileChunkRepository(t *testing.T) {

View File

@@ -6,8 +6,8 @@ import (
"fmt" "fmt"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
type FileRepository struct { type FileRepository struct {

View File

@@ -5,7 +5,7 @@ package database
import ( import (
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
// File represents a file or directory in the backup system. // File represents a file or directory in the backup system.

View File

@@ -6,9 +6,9 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/log"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/log"
) )
// Module provides database dependencies // Module provides database dependencies

View File

@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
func TestRepositoriesTransaction(t *testing.T) { func TestRepositoriesTransaction(t *testing.T) {

View File

@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
// TestFileRepositoryUUIDGeneration tests that files get unique UUIDs // TestFileRepositoryUUIDGeneration tests that files get unique UUIDs

View File

@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
// TestFileRepositoryEdgeCases tests edge cases for file repository // TestFileRepositoryEdgeCases tests edge cases for file repository

View File

@@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
type SnapshotRepository struct { type SnapshotRepository struct {

View File

@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
const ( const (

View File

@@ -5,7 +5,7 @@ import (
"database/sql" "database/sql"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/log"
) )
// Upload represents a blob upload record // Upload represents a blob upload record

View File

@@ -6,7 +6,7 @@ import (
"io" "io"
"testing" "testing"
"git.eeqj.de/sneak/vaultik/internal/s3" "sneak.berlin/go/vaultik/internal/s3"
) )
func TestClient(t *testing.T) { func TestClient(t *testing.T) {

View File

@@ -3,8 +3,8 @@ package s3
import ( import (
"context" "context"
"git.eeqj.de/sneak/vaultik/internal/config"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
) )
// Module exports S3 functionality as an fx module. // Module exports S3 functionality as an fx module.

View File

@@ -13,8 +13,8 @@ import (
"testing/fstest" "testing/fstest"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/database" "sneak.berlin/go/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
// MockS3Client is a mock implementation of S3 operations for testing // MockS3Client is a mock implementation of S3 operations for testing

View File

@@ -7,12 +7,12 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/types"
) )
func setupExcludeTestFS(t *testing.T) afero.Fs { func setupExcludeTestFS(t *testing.T) afero.Fs {

View File

@@ -6,13 +6,13 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/types"
) )
// TestFileContentChange verifies that when a file's content changes, // TestFileContentChange verifies that when a file's content changes,

View File

@@ -1,11 +1,11 @@
package snapshot package snapshot
import ( import (
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/storage"
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/storage"
) )
// ScannerParams holds parameters for scanner creation // ScannerParams holds parameters for scanner creation

View File

@@ -10,8 +10,8 @@ import (
"syscall" "syscall"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/log"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"sneak.berlin/go/vaultik/internal/log"
) )
const ( const (

View File

@@ -12,15 +12,15 @@ import (
"sync" "sync"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/blob"
"git.eeqj.de/sneak/vaultik/internal/chunker"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/storage"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/spf13/afero" "github.com/spf13/afero"
"sneak.berlin/go/vaultik/internal/blob"
"sneak.berlin/go/vaultik/internal/chunker"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/storage"
"sneak.berlin/go/vaultik/internal/types"
) )
// FileToProcess holds information about a file that needs processing // FileToProcess holds information about a file that needs processing

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/spf13/afero" "github.com/spf13/afero"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/types"
) )
func TestScannerSimpleDirectory(t *testing.T) { func TestScannerSimpleDirectory(t *testing.T) {

View File

@@ -44,15 +44,15 @@ import (
"strings" "strings"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/blobgen"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/storage"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/blobgen"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/storage"
"sneak.berlin/go/vaultik/internal/types"
) )
// SnapshotManager handles snapshot creation and metadata export // SnapshotManager handles snapshot creation and metadata export

View File

@@ -7,10 +7,10 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"github.com/spf13/afero" "github.com/spf13/afero"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
) )
const ( const (

View File

@@ -5,9 +5,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/s3"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/s3"
) )
// Module exports storage functionality as an fx module. // Module exports storage functionality as an fx module.

View File

@@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"git.eeqj.de/sneak/vaultik/internal/s3" "sneak.berlin/go/vaultik/internal/s3"
) )
// S3Storer wraps the existing s3.Client to implement Storer. // S3Storer wraps the existing s3.Client to implement Storer.

View File

@@ -8,7 +8,7 @@ import (
"io" "io"
"filippo.io/age" "filippo.io/age"
"git.eeqj.de/sneak/vaultik/internal/blobgen" "sneak.berlin/go/vaultik/internal/blobgen"
) )
// hashVerifyReader wraps a blobgen.Reader and verifies the double-SHA-256 hash // hashVerifyReader wraps a blobgen.Reader and verifies the double-SHA-256 hash

View File

@@ -10,8 +10,8 @@ import (
"testing" "testing"
"filippo.io/age" "filippo.io/age"
"git.eeqj.de/sneak/vaultik/internal/blobgen" "sneak.berlin/go/vaultik/internal/blobgen"
"git.eeqj.de/sneak/vaultik/internal/vaultik" "sneak.berlin/go/vaultik/internal/vaultik"
) )
// TestFetchAndDecryptBlobVerifiesHash verifies that FetchAndDecryptBlob checks // TestFetchAndDecryptBlobVerifiesHash verifies that FetchAndDecryptBlob checks

View File

@@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/types" "sneak.berlin/go/vaultik/internal/types"
) )
// SnapshotInfo contains information about a snapshot // SnapshotInfo contains information about a snapshot

View File

@@ -7,9 +7,9 @@ import (
"sort" "sort"
"strings" "strings"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
) )
// ShowInfo displays system and configuration information // ShowInfo displays system and configuration information

View File

@@ -11,16 +11,16 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/storage"
"git.eeqj.de/sneak/vaultik/internal/types"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/storage"
"sneak.berlin/go/vaultik/internal/types"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// MockStorer implements storage.Storer for testing // MockStorer implements storage.Storer for testing

View File

@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.eeqj.de/sneak/vaultik/internal/log"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"sneak.berlin/go/vaultik/internal/log"
) )
// PruneOptions contains options for the prune command // PruneOptions contains options for the prune command

View File

@@ -8,12 +8,12 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/types"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/types"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// setupPurgeTest creates a Vaultik instance with an in-memory database and mock // setupPurgeTest creates a Vaultik instance with an in-memory database and mock

View File

@@ -8,13 +8,13 @@ import (
"sync" "sync"
"testing" "testing"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/storage"
"git.eeqj.de/sneak/vaultik/internal/vaultik"
"github.com/klauspost/compress/zstd" "github.com/klauspost/compress/zstd"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/storage"
"sneak.berlin/go/vaultik/internal/vaultik"
) )
// testStorer implements storage.Storer for testing // testStorer implements storage.Storer for testing

View File

@@ -12,14 +12,14 @@ import (
"time" "time"
"filippo.io/age" "filippo.io/age"
"git.eeqj.de/sneak/vaultik/internal/blobgen"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/schollz/progressbar/v3" "github.com/schollz/progressbar/v3"
"github.com/spf13/afero" "github.com/spf13/afero"
"golang.org/x/term" "golang.org/x/term"
"sneak.berlin/go/vaultik/internal/blobgen"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/types"
) )
const ( const (

View File

@@ -12,12 +12,12 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/types"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/types"
) )
// SnapshotCreateOptions contains options for the snapshot create command // SnapshotCreateOptions contains options for the snapshot create command

View File

@@ -7,14 +7,14 @@ import (
"io" "io"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/crypto"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/globals"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"git.eeqj.de/sneak/vaultik/internal/storage"
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/fx" "go.uber.org/fx"
"sneak.berlin/go/vaultik/internal/config"
"sneak.berlin/go/vaultik/internal/crypto"
"sneak.berlin/go/vaultik/internal/database"
"sneak.berlin/go/vaultik/internal/globals"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/storage"
) )
// Vaultik contains all dependencies needed for vaultik operations // Vaultik contains all dependencies needed for vaultik operations

View File

@@ -10,11 +10,11 @@ import (
"os" "os"
"time" "time"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/snapshot"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/klauspost/compress/zstd" "github.com/klauspost/compress/zstd"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
) )
// VerifyOptions contains options for the verify command // VerifyOptions contains options for the verify command

View File

@@ -8,10 +8,10 @@ import (
"io" "io"
"testing" "testing"
"git.eeqj.de/sneak/vaultik/internal/crypto"
"github.com/klauspost/compress/zstd" "github.com/klauspost/compress/zstd"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/crypto"
) )
// TestTeeReaderWithDecryption tests that TeeReader correctly hashes all encrypted // TestTeeReaderWithDecryption tests that TeeReader correctly hashes all encrypted