Skip to content

Commit 403b01e

Browse files
committed
feat: add branded banner with logo and links to all commands, better docs
1 parent ccd98bd commit 403b01e

13 files changed

Lines changed: 137 additions & 40 deletions

File tree

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ services:
471471
backitup:
472472
image: ghcr.io/climactic/backitup:latest
473473
command: start
474+
user: root # Required for reading files with restrictive permissions
474475
volumes:
475476
- ./config:/config # Config + database
476477
- ./data:/data:ro # Source files (read-only)
@@ -482,6 +483,53 @@ services:
482483
restart: unless-stopped
483484
```
484485

486+
### File Permissions
487+
488+
The Docker image runs as a non-root user (UID 1000) by default. If you're backing up files with restrictive permissions (e.g., SSH keys, database files), you have two options:
489+
490+
> **Tip:** Always mount source data directories as read-only (`:ro`) to prevent accidental modifications during backup.
491+
492+
**Option 1: Run as root (simplest)**
493+
494+
```yaml
495+
services:
496+
backitup:
497+
image: ghcr.io/climactic/backitup:latest
498+
user: root
499+
# ...
500+
```
501+
502+
**Option 2: Match the host user**
503+
504+
If your files are owned by a specific user, run the container as that user:
505+
506+
```yaml
507+
services:
508+
backitup:
509+
image: ghcr.io/climactic/backitup:latest
510+
user: "1000:1000" # Match the UID:GID of file owner
511+
# ...
512+
```
513+
514+
### One-time vs Scheduled Backups
515+
516+
Use `command: start` for the long-running scheduler daemon with `restart: unless-stopped`.
517+
518+
For one-time backups (e.g., triggered by cron or CI), use `restart: on-failure` to prevent the container from restarting indefinitely after a successful backup:
519+
520+
```yaml
521+
services:
522+
backitup:
523+
image: ghcr.io/climactic/backitup:latest
524+
command: backup -s manual
525+
user: root
526+
volumes:
527+
- ./config:/config
528+
- ./data:/data:ro
529+
- ./backups:/backups
530+
restart: on-failure # Don't restart after successful backup
531+
```
532+
485533
### systemd
486534

487535
```ini

docs/configuration.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,43 @@ Named backup sources. Each source defines files to include in backups.
151151
| `path` | string | Yes | Base directory path |
152152
| `patterns` | string[] | No | Glob patterns (default: `["**/*"]`) |
153153

154-
**Glob Pattern Examples:**
154+
**Glob Pattern Syntax:**
155155

156-
- `**/*.ts` - All TypeScript files
157-
- `!**/node_modules/**` - Exclude node_modules
158-
- `src/**` - Everything in src directory
156+
Patterns use glob syntax to include or exclude files. Patterns starting with `!` are exclusions.
157+
158+
**Important:** Patterns are matched relative to the source `path`, not as absolute paths. If your source path is `/data`, a file at `/data/logs/app.log` would be matched against `logs/app.log`.
159+
160+
**Examples:**
161+
162+
| Pattern | Description |
163+
| ------- | ----------- |
164+
| `**/*` | All files (default) |
165+
| `**/*.ts` | All TypeScript files |
166+
| `src/**` | Everything in src directory |
167+
| `!**/node_modules/**` | Exclude node_modules anywhere |
168+
| `!logs/**` | Exclude the logs directory |
169+
| `!*.log` | Exclude .log files in root only |
170+
| `!**/*.log` | Exclude all .log files |
171+
172+
**Common mistake:** Using absolute paths in exclude patterns.
173+
174+
```yaml
175+
# Wrong - patterns are relative to path, not absolute
176+
sources:
177+
mydata:
178+
path: "/data"
179+
patterns:
180+
- "**/*"
181+
- "!/data/cache/**" # Won't work!
182+
183+
# Correct - use relative paths
184+
sources:
185+
mydata:
186+
path: "/data"
187+
patterns:
188+
- "**/*"
189+
- "!cache/**" # Excludes /data/cache/
190+
```
159191
160192
### `local`
161193

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@climactic/backitup",
3-
"version": "1.0.0-rc.1",
3+
"version": "1.0.0",
44
"description": "Secure backup utility with glob patterns, tar.gz archives, local + S3 storage, and safe cleanup",
55
"author": {
66
"name": "Climactic",

src/cli/commands/backup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export async function backupCommand(args: string[]): Promise<number> {
8181

8282
if (!schedule) {
8383
// Interactive mode - prompt for schedule
84-
ui.intro("backitup backup");
84+
ui.banner("backup");
8585

8686
const scheduleOptions = [
8787
{ value: "manual", label: "manual", hint: "One-time backup" },
@@ -114,7 +114,7 @@ export async function backupCommand(args: string[]): Promise<number> {
114114

115115
// Show intro if we didn't already (non-interactive mode)
116116
if (values.schedule) {
117-
ui.intro("backitup backup");
117+
ui.banner("backup");
118118
}
119119

120120
// Run backup with spinner

src/cli/commands/cleanup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export async function cleanupCommand(args: string[]): Promise<number> {
3030
try {
3131
const config = await findAndLoadConfig(values.config);
3232

33-
ui.intro("backitup cleanup");
33+
ui.banner("cleanup");
3434

3535
// Validate schedule if specified
3636
if (values.schedule && !config.schedules[values.schedule]) {

src/cli/commands/list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export async function listCommand(args: string[]): Promise<number> {
6666
return 0;
6767
default:
6868
// Show intro for table format
69-
ui.intro("backitup list");
69+
ui.banner("list");
7070

7171
if (backups.length === 0) {
7272
ui.info("No backups found");

src/cli/commands/start.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export async function startCommand(args: string[]): Promise<number> {
6868
}
6969
}
7070

71-
ui.intro("backitup scheduler");
71+
ui.banner("start");
7272

7373
if (Object.keys(config.schedules).length === 0) {
7474
ui.error("No schedules configured");

src/cli/commands/verify.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function verifyCommand(args: string[]): Promise<number> {
4343
const config = await findAndLoadConfig(values.config);
4444
await initDatabase(config.database.path);
4545

46-
ui.intro("backitup verify");
46+
ui.banner("verify");
4747

4848
// Initialize S3 if enabled
4949
if (config.s3.enabled) {

src/cli/ui.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
export type { SummaryItem } from "./ui/index";
66
export {
7+
banner,
78
cancel,
89
color,
910
confirm,
@@ -14,6 +15,8 @@ export {
1415
info,
1516
intro,
1617
isCancel,
18+
LINKS,
19+
LOGO,
1720
message,
1821
multiselect,
1922
note,
@@ -25,5 +28,6 @@ export {
2528
TABLE_WIDTHS,
2629
text,
2730
ui,
31+
VERSION,
2832
warn,
2933
} from "./ui/index";

src/cli/ui/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,21 @@ export type { SummaryItem } from "./formatters";
77
export { formatSummary, formatTableRow, formatTableSeparator, TABLE_WIDTHS } from "./formatters";
88
// Output
99
export {
10+
banner,
1011
cancel,
1112
color,
1213
error,
1314
info,
1415
intro,
16+
LINKS,
17+
LOGO,
1518
message,
1619
note,
1720
outro,
1821
spinner,
1922
step,
2023
success,
24+
VERSION,
2125
warn,
2226
} from "./output";
2327
// Prompts
@@ -28,6 +32,7 @@ import * as output from "./output";
2832
import * as prompts from "./prompts";
2933

3034
export const ui = {
35+
banner: output.banner,
3136
intro: output.intro,
3237
outro: output.outro,
3338
cancel: output.cancel,

0 commit comments

Comments
 (0)