Skip to content

Commit e6f994f

Browse files
jochbruclaude
andcommitted
Print full usage on missing required flags; support IMMICH_DIR
Bare "required flag(s) ... not set" with no usage text wasn't actionable. Required-flag checking now happens in PreRunE, which prints cmd.UsageString() before returning the error; runtime errors from RunE (network failures, sync errors) stay terse as before. --dir now also reads from IMMICH_DIR, matching --url/--api-key's IMMICH_URL/IMMICH_API_KEY pattern. Also fixed a malformed 4-column row in the README flags table. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 90566b1 commit e6f994f

2 files changed

Lines changed: 29 additions & 12 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ Immich-go-style sidecar JSON carrying the full original asset metadata.
99
```sh
1010
export IMMICH_URL=https://photos.example.com
1111
export IMMICH_API_KEY=your-user-api-key
12+
export IMMICH_DIR=/path/to/archive
1213

13-
immich-archiver --dir /path/to/archive # --dir is required
14+
immich-archiver
15+
# or, without env vars:
16+
immich-archiver --url https://photos.example.com --api-key your-user-api-key --dir /path/to/archive
1417
```
1518

1619
On a second run, assets already present on disk (verified by filename + a matching asset ID in
@@ -34,9 +37,9 @@ Live Photos are downloaded as a still + a paired motion video sharing the same b
3437

3538
| Flag | Default | Description |
3639
|---|---|---|
37-
| `--url` | `$IMMICH_URL` | Immich server URL |
38-
| `--api-key` | `$IMMICH_API_KEY` | Immich user API key |
39-
| `--dir` | *(required)* | destination root directory |
40+
| `--url` | *(required)* | Immich server URL (env `IMMICH_URL`) |
41+
| `--api-key` | *(required)* | Immich user API key (env `IMMICH_API_KEY`) |
42+
| `--dir` | *(required)* | destination root directory (env `IMMICH_DIR`) |
4043
| `--path-template` | `{year}/{year}-{month}` | folder structure template |
4144
| `--include-shared` | `false` | also mirror assets from albums shared with you |
4245
| `--shared-dir` | `<dir>/shared-with-me` | destination root for shared assets |

cmd/root.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"strings"
78
"time"
89

910
"github.com/pixelunioneu/immich-archiver/internal/archive"
@@ -42,14 +43,17 @@ func newRootCmd() *cobra.Command {
4243
Version: version,
4344
SilenceUsage: true,
4445
SilenceErrors: true,
46+
PreRunE: func(cmd *cobra.Command, args []string) error {
47+
return validateFlags(cmd, f)
48+
},
4549
RunE: func(cmd *cobra.Command, args []string) error {
4650
return runSync(cmd, f)
4751
},
4852
}
4953

5054
cmd.Flags().StringVar(&f.url, "url", os.Getenv("IMMICH_URL"), "Immich server URL (env IMMICH_URL)")
5155
cmd.Flags().StringVar(&f.apiKey, "api-key", os.Getenv("IMMICH_API_KEY"), "Immich user API key (env IMMICH_API_KEY)")
52-
cmd.Flags().StringVar(&f.dir, "dir", "", "destination root directory (required)")
56+
cmd.Flags().StringVar(&f.dir, "dir", os.Getenv("IMMICH_DIR"), "destination root directory (required, env IMMICH_DIR)")
5357
cmd.Flags().StringVar(&f.pathTemplate, "path-template", archive.DefaultPathTemplate, "folder structure template; supports {year}, {month}, {day}")
5458
cmd.Flags().BoolVar(&f.includeShared, "include-shared", false, "also mirror assets shared with you into a separate folder")
5559
cmd.Flags().StringVar(&f.sharedDir, "shared-dir", "", "destination root for shared assets (default: <dir>/shared-with-me)")
@@ -59,21 +63,31 @@ func newRootCmd() *cobra.Command {
5963
cmd.Flags().BoolVar(&f.dryRun, "dry-run", false, "list what would be downloaded without writing anything")
6064
cmd.Flags().BoolVarP(&f.verbose, "verbose", "v", false, "log a line per asset instead of showing a progress bar")
6165

62-
if err := cmd.MarkFlagRequired("dir"); err != nil {
63-
panic(err)
64-
}
65-
6666
return cmd
6767
}
6868

69-
func runSync(cmd *cobra.Command, f *flags) error {
69+
// validateFlags checks required inputs before RunE. Failures print the full
70+
// usage/help text (unlike runtime errors from RunE, which stay terse) so a
71+
// missing/misspelled flag is immediately actionable.
72+
func validateFlags(cmd *cobra.Command, f *flags) error {
73+
var missing []string
74+
if f.dir == "" {
75+
missing = append(missing, "--dir (or IMMICH_DIR)")
76+
}
7077
if f.url == "" {
71-
return fmt.Errorf("an Immich server URL is required: pass --url or set IMMICH_URL")
78+
missing = append(missing, "--url (or IMMICH_URL)")
7279
}
7380
if f.apiKey == "" {
74-
return fmt.Errorf("an Immich API key is required: pass --api-key or set IMMICH_API_KEY")
81+
missing = append(missing, "--api-key (or IMMICH_API_KEY)")
82+
}
83+
if len(missing) == 0 {
84+
return nil
7585
}
86+
_, _ = fmt.Fprintln(cmd.OutOrStderr(), cmd.UsageString())
87+
return fmt.Errorf("missing required flag(s): %s", strings.Join(missing, ", "))
88+
}
7689

90+
func runSync(cmd *cobra.Command, f *flags) error {
7791
client := immich.NewClient(f.url, f.apiKey)
7892
client.Retries = f.retries
7993

0 commit comments

Comments
 (0)