refactor: use ~/.gordon for data, ~/.config/gordon for config
- Default data directory changed from /var/lib/gordon to ~/.gordon - Config file now searched in ~/.config/gordon (XDG compliant) - Add shared helpers in internal/app/paths.go for path logic - Update README with clearer setup guide and consistent paths - Remove version from goreleaser archive names for stable download URLs Directory structure: - Config: ~/.config/gordon/gordon.toml - Data: ~/.gordon/ (registry, env, logs, secrets)
- commit
- dae054ca7a77723aa13523b0234c1c4123a12fbb
- author
- brice · · 2026-01-12 17:30 UTC
- committer
- brice · · 2026-01-12 17:48 UTC
- parent
- 9a2dd31
- signature
- unsigned
5 files · +97 -51
M .goreleaser.yaml +1 -1
| @@ -26,7 +26,7 @@ builds: | |||
| 26 | 26 | # docker_manifests: | |
| 27 | 27 | ||
| 28 | 28 | archives: | |
| 29 | - | - name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" | |
| 29 | + | - name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" | |
| 30 | 30 | ||
| 31 | 31 | changelog: | |
| 32 | 32 | sort: asc | |
| @@ -26,7 +26,7 @@ builds: | |||||
| 26 | # docker_manifests: | 26 | # docker_manifests: | ||
| 27 | 27 | ||||
| 28 | archives: | 28 | archives: | ||
| 29 | - | - name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" | 29 | + | - name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" |
| 30 | 30 | ||||
| 31 | changelog: | 31 | changelog: | ||
| 32 | sort: asc | 32 | sort: asc | ||
M README.md +53 -25
| @@ -40,20 +40,19 @@ Build on your machine, push to deploy. Works from your laptop or CI. | |||
| 40 | 40 | ### Installation | |
| 41 | 41 | ||
| 42 | 42 | ```bash | |
| 43 | - | # Download Gordon from the releases page | |
| 44 | - | # https://github.com/bnema/gordon/releases/latest | |
| 45 | - | # Example: wget https://github.com/bnema/gordon/releases/download/v2.0.0/gordon_2.0.0_linux_amd64.tar.gz | |
| 46 | - | # Then extract: | |
| 47 | - | tar -xzf gordon_*.tar.gz | |
| 43 | + | # Download from releases | |
| 44 | + | wget https://github.com/bnema/gordon/releases/latest/download/gordon_linux_amd64.tar.gz | |
| 45 | + | tar -xzf gordon_linux_amd64.tar.gz | |
| 48 | 46 | chmod +x gordon | |
| 49 | 47 | sudo mv gordon /usr/local/bin/ | |
| 50 | 48 | ||
| 51 | - | # Create initial config | |
| 52 | - | gordon start | |
| 53 | - | # Press Ctrl+C after config is created | |
| 49 | + | # Create config | |
| 50 | + | mkdir -p ~/.config/gordon | |
| 51 | + | cp examples/minimal.toml ~/.config/gordon/gordon.toml | |
| 52 | + | # Edit gordon.toml with your domain | |
| 54 | 53 | ``` | |
| 55 | 54 | ||
| 56 | - | **Important:** This is just the binary installation. For a complete working setup including networking, firewall, and systemd service, follow the [detailed setup guide](#detailed-setup-guide-podman-rootless-mode) below. | |
| 55 | + | **Important:** For a complete working setup including networking, firewall, and systemd service, follow the [detailed setup guide](#detailed-setup-guide-podman-rootless-mode) below. | |
| 57 | 56 | ||
| 58 | 57 | ## Core Concepts | |
| 59 | 58 | ||
| @@ -86,7 +85,7 @@ VOLUME ["/app/uploads", "/app/data"] # Multiple volumes supported | |||
| 86 | 85 | ### Environment Configuration | |
| 87 | 86 | Gordon automatically creates and loads env files based on domain names: | |
| 88 | 87 | ```bash | |
| 89 | - | # Auto-created on first run: ~/.local/share/gordon/env/app_mydomain_com.env | |
| 88 | + | # Auto-created on first run: ~/.gordon/env/app_mydomain_com.env | |
| 90 | 89 | # Just add your variables: | |
| 91 | 90 | DATABASE_URL=postgresql://localhost:5432/myapp | |
| 92 | 91 | API_KEY=sk-1234567890 | |
| @@ -107,7 +106,7 @@ Gordon also supports network groups, see [examples/](examples/) for advanced usa | |||
| 107 | 106 | - **Volumes**: Automatically created from Dockerfile VOLUME directives, persist across updates | |
| 108 | 107 | ||
| 109 | 108 | ### Production ready logging | |
| 110 | - | - **Logs**: All container output saved to `~/.local/share/gordon/logs/containers/` | |
| 109 | + | - **Logs**: All container output saved to `~/.gordon/logs/containers/` | |
| 111 | 110 | - **Rotation**: Logs rotate at 100MB, keeping 3 backups for 28 days | |
| 112 | 111 | ||
| 113 | 112 | ## Configuration | |
| @@ -204,9 +203,9 @@ See [examples/github-workflow.yml](examples/github-workflow.yml) and [.github/ac | |||
| 204 | 203 | ||
| 205 | 204 | ## Detailed Setup Guide (Podman Rootless Mode) | |
| 206 | 205 | ||
| 207 | - | > **Note:** This guide uses Podman in rootless mode for enhanced security. Docker users can follow similar steps - Gordon works with any OCI-compatible runtime. Refer to Quick Start for the binary installation. | |
| 206 | + | > **Note:** This guide uses Podman in rootless mode for enhanced security. Docker users can follow similar steps. Gordon works with any OCI-compatible runtime. | |
| 208 | 207 | ||
| 209 | - | ### 1. VPS Preparation | |
| 208 | + | ### 1. System Preparation | |
| 210 | 209 | ```bash | |
| 211 | 210 | # Update system | |
| 212 | 211 | sudo apt update && sudo apt upgrade -y | |
| @@ -216,11 +215,11 @@ sudo apt install -y podman ufw | |||
| 216 | 215 | ||
| 217 | 216 | # Configure firewall | |
| 218 | 217 | sudo ufw allow 22/tcp 80/tcp 443/tcp | |
| 219 | - | sudo default deny | |
| 218 | + | sudo ufw default deny incoming | |
| 220 | 219 | sudo ufw --force enable | |
| 221 | 220 | ``` | |
| 222 | 221 | ||
| 223 | - | ### 2. Enable Port Forwarding | |
| 222 | + | ### 2. Port Forwarding (rootless requires this) | |
| 224 | 223 | ```bash | |
| 225 | 224 | # Forward 80/443 to unprivileged port 8080 | |
| 226 | 225 | sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 | |
| @@ -231,21 +230,19 @@ sudo apt install -y iptables-persistent | |||
| 231 | 230 | sudo netfilter-persistent save | |
| 232 | 231 | ``` | |
| 233 | 232 | ||
| 234 | - | ### 3. Configure Rootless Containers | |
| 233 | + | ### 3. Rootless Container Setup | |
| 235 | 234 | ```bash | |
| 236 | 235 | # Enable user namespaces | |
| 237 | 236 | echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf | |
| 238 | 237 | sudo sysctl -p | |
| 239 | 238 | ||
| 240 | - | # Setup subuid/subgid | |
| 239 | + | # Setup subuid/subgid for your user | |
| 241 | 240 | sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER | |
| 242 | 241 | ||
| 243 | 242 | # Enable podman socket | |
| 244 | 243 | systemctl --user enable --now podman.socket | |
| 245 | - | ``` | |
| 246 | 244 | ||
| 247 | - | ### 4. Registry Configuration | |
| 248 | - | ```bash | |
| 245 | + | # Allow localhost registry (podman config) | |
| 249 | 246 | mkdir -p ~/.config/containers | |
| 250 | 247 | cat > ~/.config/containers/registries.conf <<EOF | |
| 251 | 248 | [registries.insecure] | |
| @@ -253,9 +250,40 @@ registries = ['localhost:5000'] | |||
| 253 | 250 | EOF | |
| 254 | 251 | ``` | |
| 255 | 252 | ||
| 256 | - | ### 5. Create Systemd Service | |
| 253 | + | ### 4. Install Gordon | |
| 254 | + | ```bash | |
| 255 | + | # Download from releases | |
| 256 | + | wget https://github.com/bnema/gordon/releases/latest/download/gordon_linux_amd64.tar.gz | |
| 257 | + | tar -xzf gordon_linux_amd64.tar.gz | |
| 258 | + | chmod +x gordon | |
| 259 | + | sudo mv gordon /usr/local/bin/ | |
| 260 | + | ``` | |
| 261 | + | ||
| 262 | + | ### 5. Configure Gordon | |
| 263 | + | ```bash | |
| 264 | + | # Create config directory and file | |
| 265 | + | mkdir -p ~/.config/gordon | |
| 266 | + | cat > ~/.config/gordon/gordon.toml <<EOF | |
| 267 | + | [server] | |
| 268 | + | port = 8080 | |
| 269 | + | registry_port = 5000 | |
| 270 | + | registry_domain = "registry.yourdomain.com" | |
| 271 | + | ||
| 272 | + | [secrets] | |
| 273 | + | backend = "unsafe" # Use "pass" or "sops" for production | |
| 274 | + | ||
| 275 | + | [registry_auth] | |
| 276 | + | enabled = false # Enable after testing | |
| 277 | + | ||
| 278 | + | [routes] | |
| 279 | + | "app.yourdomain.com" = "myapp:latest" | |
| 280 | + | EOF | |
| 281 | + | ``` | |
| 282 | + | ||
| 283 | + | Data is stored in `~/.gordon/` (registry, env files, logs, secrets). | |
| 284 | + | ||
| 285 | + | ### 6. Create Systemd Service | |
| 257 | 286 | ```bash | |
| 258 | - | # Create user service | |
| 259 | 287 | mkdir -p ~/.config/systemd/user | |
| 260 | 288 | cat > ~/.config/systemd/user/gordon.service <<EOF | |
| 261 | 289 | [Unit] | |
| @@ -267,19 +295,19 @@ Type=simple | |||
| 267 | 295 | Restart=always | |
| 268 | 296 | RestartSec=5 | |
| 269 | 297 | ExecStart=/usr/local/bin/gordon start | |
| 270 | - | WorkingDirectory=%h | |
| 271 | 298 | ||
| 272 | 299 | [Install] | |
| 273 | 300 | WantedBy=default.target | |
| 274 | 301 | EOF | |
| 275 | 302 | ||
| 276 | - | # Enable and start service | |
| 303 | + | # Enable and start | |
| 277 | 304 | systemctl --user daemon-reload | |
| 278 | 305 | systemctl --user enable --now gordon | |
| 279 | 306 | sudo loginctl enable-linger $USER | |
| 280 | 307 | ||
| 281 | - | # Verify it's running | |
| 308 | + | # Verify | |
| 282 | 309 | systemctl --user status gordon | |
| 310 | + | journalctl --user -u gordon -f # Watch logs | |
| 283 | 311 | ``` | |
| 284 | 312 | ||
| 285 | 313 | ## Deployment Strategies | |
| @@ -40,20 +40,19 @@ Build on your machine, push to deploy. Works from your laptop or CI. | |||||
| 40 | ### Installation | 40 | ### Installation | ||
| 41 | 41 | ||||
| 42 | ```bash | 42 | ```bash | ||
| 43 | - | # Download Gordon from the releases page | 43 | + | # Download from releases |
| 44 | - | # https://github.com/bnema/gordon/releases/latest | 44 | + | wget https://github.com/bnema/gordon/releases/latest/download/gordon_linux_amd64.tar.gz |
| 45 | - | # Example: wget https://github.com/bnema/gordon/releases/download/v2.0.0/gordon_2.0.0_linux_amd64.tar.gz | 45 | + | tar -xzf gordon_linux_amd64.tar.gz |
| 46 | - | # Then extract: | |||
| 47 | - | tar -xzf gordon_*.tar.gz | |||
| 48 | chmod +x gordon | 46 | chmod +x gordon | ||
| 49 | sudo mv gordon /usr/local/bin/ | 47 | sudo mv gordon /usr/local/bin/ | ||
| 50 | 48 | ||||
| 51 | - | # Create initial config | 49 | + | # Create config |
| 52 | - | gordon start | 50 | + | mkdir -p ~/.config/gordon |
| 53 | - | # Press Ctrl+C after config is created | 51 | + | cp examples/minimal.toml ~/.config/gordon/gordon.toml |
| 52 | + | # Edit gordon.toml with your domain | |||
| 54 | ``` | 53 | ``` | ||
| 55 | 54 | ||||
| 56 | - | **Important:** This is just the binary installation. For a complete working setup including networking, firewall, and systemd service, follow the [detailed setup guide](#detailed-setup-guide-podman-rootless-mode) below. | 55 | + | **Important:** For a complete working setup including networking, firewall, and systemd service, follow the [detailed setup guide](#detailed-setup-guide-podman-rootless-mode) below. |
| 57 | 56 | ||||
| 58 | ## Core Concepts | 57 | ## Core Concepts | ||
| 59 | 58 | ||||
| @@ -86,7 +85,7 @@ VOLUME ["/app/uploads", "/app/data"] # Multiple volumes supported | |||||
| 86 | ### Environment Configuration | 85 | ### Environment Configuration | ||
| 87 | Gordon automatically creates and loads env files based on domain names: | 86 | Gordon automatically creates and loads env files based on domain names: | ||
| 88 | ```bash | 87 | ```bash | ||
| 89 | - | # Auto-created on first run: ~/.local/share/gordon/env/app_mydomain_com.env | 88 | + | # Auto-created on first run: ~/.gordon/env/app_mydomain_com.env |
| 90 | # Just add your variables: | 89 | # Just add your variables: | ||
| 91 | DATABASE_URL=postgresql://localhost:5432/myapp | 90 | DATABASE_URL=postgresql://localhost:5432/myapp | ||
| 92 | API_KEY=sk-1234567890 | 91 | API_KEY=sk-1234567890 | ||
| @@ -107,7 +106,7 @@ Gordon also supports network groups, see [examples/](examples/) for advanced usa | |||||
| 107 | - **Volumes**: Automatically created from Dockerfile VOLUME directives, persist across updates | 106 | - **Volumes**: Automatically created from Dockerfile VOLUME directives, persist across updates | ||
| 108 | 107 | ||||
| 109 | ### Production ready logging | 108 | ### Production ready logging | ||
| 110 | - | - **Logs**: All container output saved to `~/.local/share/gordon/logs/containers/` | 109 | + | - **Logs**: All container output saved to `~/.gordon/logs/containers/` |
| 111 | - **Rotation**: Logs rotate at 100MB, keeping 3 backups for 28 days | 110 | - **Rotation**: Logs rotate at 100MB, keeping 3 backups for 28 days | ||
| 112 | 111 | ||||
| 113 | ## Configuration | 112 | ## Configuration | ||
| @@ -204,9 +203,9 @@ See [examples/github-workflow.yml](examples/github-workflow.yml) and [.github/ac | |||||
| 204 | 203 | ||||
| 205 | ## Detailed Setup Guide (Podman Rootless Mode) | 204 | ## Detailed Setup Guide (Podman Rootless Mode) | ||
| 206 | 205 | ||||
| 207 | - | > **Note:** This guide uses Podman in rootless mode for enhanced security. Docker users can follow similar steps - Gordon works with any OCI-compatible runtime. Refer to Quick Start for the binary installation. | 206 | + | > **Note:** This guide uses Podman in rootless mode for enhanced security. Docker users can follow similar steps. Gordon works with any OCI-compatible runtime. |
| 208 | 207 | ||||
| 209 | - | ### 1. VPS Preparation | 208 | + | ### 1. System Preparation |
| 210 | ```bash | 209 | ```bash | ||
| 211 | # Update system | 210 | # Update system | ||
| 212 | sudo apt update && sudo apt upgrade -y | 211 | sudo apt update && sudo apt upgrade -y | ||
| @@ -216,11 +215,11 @@ sudo apt install -y podman ufw | |||||
| 216 | 215 | ||||
| 217 | # Configure firewall | 216 | # Configure firewall | ||
| 218 | sudo ufw allow 22/tcp 80/tcp 443/tcp | 217 | sudo ufw allow 22/tcp 80/tcp 443/tcp | ||
| 219 | - | sudo default deny | 218 | + | sudo ufw default deny incoming |
| 220 | sudo ufw --force enable | 219 | sudo ufw --force enable | ||
| 221 | ``` | 220 | ``` | ||
| 222 | 221 | ||||
| 223 | - | ### 2. Enable Port Forwarding | 222 | + | ### 2. Port Forwarding (rootless requires this) |
| 224 | ```bash | 223 | ```bash | ||
| 225 | # Forward 80/443 to unprivileged port 8080 | 224 | # Forward 80/443 to unprivileged port 8080 | ||
| 226 | sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 | 225 | sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 | ||
| @@ -231,21 +230,19 @@ sudo apt install -y iptables-persistent | |||||
| 231 | sudo netfilter-persistent save | 230 | sudo netfilter-persistent save | ||
| 232 | ``` | 231 | ``` | ||
| 233 | 232 | ||||
| 234 | - | ### 3. Configure Rootless Containers | 233 | + | ### 3. Rootless Container Setup |
| 235 | ```bash | 234 | ```bash | ||
| 236 | # Enable user namespaces | 235 | # Enable user namespaces | ||
| 237 | echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf | 236 | echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf | ||
| 238 | sudo sysctl -p | 237 | sudo sysctl -p | ||
| 239 | 238 | ||||
| 240 | - | # Setup subuid/subgid | 239 | + | # Setup subuid/subgid for your user |
| 241 | sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER | 240 | sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER | ||
| 242 | 241 | ||||
| 243 | # Enable podman socket | 242 | # Enable podman socket | ||
| 244 | systemctl --user enable --now podman.socket | 243 | systemctl --user enable --now podman.socket | ||
| 245 | - | ``` | |||
| 246 | 244 | ||||
| 247 | - | ### 4. Registry Configuration | 245 | + | # Allow localhost registry (podman config) |
| 248 | - | ```bash | |||
| 249 | mkdir -p ~/.config/containers | 246 | mkdir -p ~/.config/containers | ||
| 250 | cat > ~/.config/containers/registries.conf <<EOF | 247 | cat > ~/.config/containers/registries.conf <<EOF | ||
| 251 | [registries.insecure] | 248 | [registries.insecure] | ||
| @@ -253,9 +250,40 @@ registries = ['localhost:5000'] | |||||
| 253 | EOF | 250 | EOF | ||
| 254 | ``` | 251 | ``` | ||
| 255 | 252 | ||||
| 256 | - | ### 5. Create Systemd Service | 253 | + | ### 4. Install Gordon |
| 254 | + | ```bash | |||
| 255 | + | # Download from releases | |||
| 256 | + | wget https://github.com/bnema/gordon/releases/latest/download/gordon_linux_amd64.tar.gz | |||
| 257 | + | tar -xzf gordon_linux_amd64.tar.gz | |||
| 258 | + | chmod +x gordon | |||
| 259 | + | sudo mv gordon /usr/local/bin/ | |||
| 260 | + | ``` | |||
| 261 | + | ||||
| 262 | + | ### 5. Configure Gordon | |||
| 263 | + | ```bash | |||
| 264 | + | # Create config directory and file | |||
| 265 | + | mkdir -p ~/.config/gordon | |||
| 266 | + | cat > ~/.config/gordon/gordon.toml <<EOF | |||
| 267 | + | [server] | |||
| 268 | + | port = 8080 | |||
| 269 | + | registry_port = 5000 | |||
| 270 | + | registry_domain = "registry.yourdomain.com" | |||
| 271 | + | ||||
| 272 | + | [secrets] | |||
| 273 | + | backend = "unsafe" # Use "pass" or "sops" for production | |||
| 274 | + | ||||
| 275 | + | [registry_auth] | |||
| 276 | + | enabled = false # Enable after testing | |||
| 277 | + | ||||
| 278 | + | [routes] | |||
| 279 | + | "app.yourdomain.com" = "myapp:latest" | |||
| 280 | + | EOF | |||
| 281 | + | ``` | |||
| 282 | + | ||||
| 283 | + | Data is stored in `~/.gordon/` (registry, env files, logs, secrets). | |||
| 284 | + | ||||
| 285 | + | ### 6. Create Systemd Service | |||
| 257 | ```bash | 286 | ```bash | ||
| 258 | - | # Create user service | |||
| 259 | mkdir -p ~/.config/systemd/user | 287 | mkdir -p ~/.config/systemd/user | ||
| 260 | cat > ~/.config/systemd/user/gordon.service <<EOF | 288 | cat > ~/.config/systemd/user/gordon.service <<EOF | ||
| 261 | [Unit] | 289 | [Unit] | ||
| @@ -267,19 +295,19 @@ Type=simple | |||||
| 267 | Restart=always | 295 | Restart=always | ||
| 268 | RestartSec=5 | 296 | RestartSec=5 | ||
| 269 | ExecStart=/usr/local/bin/gordon start | 297 | ExecStart=/usr/local/bin/gordon start | ||
| 270 | - | WorkingDirectory=%h | |||
| 271 | 298 | ||||
| 272 | [Install] | 299 | [Install] | ||
| 273 | WantedBy=default.target | 300 | WantedBy=default.target | ||
| 274 | EOF | 301 | EOF | ||
| 275 | 302 | ||||
| 276 | - | # Enable and start service | 303 | + | # Enable and start |
| 277 | systemctl --user daemon-reload | 304 | systemctl --user daemon-reload | ||
| 278 | systemctl --user enable --now gordon | 305 | systemctl --user enable --now gordon | ||
| 279 | sudo loginctl enable-linger $USER | 306 | sudo loginctl enable-linger $USER | ||
| 280 | 307 | ||||
| 281 | - | # Verify it's running | 308 | + | # Verify |
| 282 | systemctl --user status gordon | 309 | systemctl --user status gordon | ||
| 310 | + | journalctl --user -u gordon -f # Watch logs | |||
| 283 | ``` | 311 | ``` | ||
| 284 | 312 | ||||
| 285 | ## Deployment Strategies | 313 | ## Deployment Strategies | ||
M internal/adapters/in/cli/auth.go +3 -10
| @@ -15,6 +15,7 @@ import ( | |||
| 15 | 15 | "golang.org/x/term" | |
| 16 | 16 | ||
| 17 | 17 | "gordon/internal/adapters/out/tokenstore" | |
| 18 | + | "gordon/internal/app" | |
| 18 | 19 | "gordon/internal/domain" | |
| 19 | 20 | "gordon/internal/usecase/auth" | |
| 20 | 21 | ) | |
| @@ -318,18 +319,10 @@ func loadAuthConfig(configPath string) (*cliConfig, error) { | |||
| 318 | 319 | v := viper.New() | |
| 319 | 320 | ||
| 320 | 321 | // Set defaults | |
| 321 | - | v.SetDefault("server.data_dir", "/var/lib/gordon") | |
| 322 | + | v.SetDefault("server.data_dir", app.DefaultDataDir()) | |
| 322 | 323 | v.SetDefault("secrets.backend", "unsafe") | |
| 323 | 324 | ||
| 324 | - | if configPath != "" { | |
| 325 | - | v.SetConfigFile(configPath) | |
| 326 | - | } else { | |
| 327 | - | v.SetConfigName("gordon") | |
| 328 | - | v.SetConfigType("yaml") | |
| 329 | - | v.AddConfigPath("/etc/gordon") | |
| 330 | - | v.AddConfigPath("$HOME/.gordon") | |
| 331 | - | v.AddConfigPath(".") | |
| 332 | - | } | |
| 325 | + | app.ConfigureViper(v, configPath) | |
| 333 | 326 | ||
| 334 | 327 | if err := v.ReadInConfig(); err != nil { | |
| 335 | 328 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | |
| @@ -15,6 +15,7 @@ import ( | |||||
| 15 | "golang.org/x/term" | 15 | "golang.org/x/term" | ||
| 16 | 16 | ||||
| 17 | "gordon/internal/adapters/out/tokenstore" | 17 | "gordon/internal/adapters/out/tokenstore" | ||
| 18 | + | "gordon/internal/app" | |||
| 18 | "gordon/internal/domain" | 19 | "gordon/internal/domain" | ||
| 19 | "gordon/internal/usecase/auth" | 20 | "gordon/internal/usecase/auth" | ||
| 20 | ) | 21 | ) | ||
| @@ -318,18 +319,10 @@ func loadAuthConfig(configPath string) (*cliConfig, error) { | |||||
| 318 | v := viper.New() | 319 | v := viper.New() | ||
| 319 | 320 | ||||
| 320 | // Set defaults | 321 | // Set defaults | ||
| 321 | - | v.SetDefault("server.data_dir", "/var/lib/gordon") | 322 | + | v.SetDefault("server.data_dir", app.DefaultDataDir()) |
| 322 | v.SetDefault("secrets.backend", "unsafe") | 323 | v.SetDefault("secrets.backend", "unsafe") | ||
| 323 | 324 | ||||
| 324 | - | if configPath != "" { | 325 | + | app.ConfigureViper(v, configPath) |
| 325 | - | v.SetConfigFile(configPath) | |||
| 326 | - | } else { | |||
| 327 | - | v.SetConfigName("gordon") | |||
| 328 | - | v.SetConfigType("yaml") | |||
| 329 | - | v.AddConfigPath("/etc/gordon") | |||
| 330 | - | v.AddConfigPath("$HOME/.gordon") | |||
| 331 | - | v.AddConfigPath(".") | |||
| 332 | - | } | |||
| 333 | 326 | ||||
| 334 | if err := v.ReadInConfig(); err != nil { | 327 | if err := v.ReadInConfig(); err != nil { | ||
| 335 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | 328 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | ||
A internal/app/paths.go +33 -0
| @@ -0,0 +1,33 @@ | |||
| 1 | + | // Package app provides the application initialization and wiring. | |
| 2 | + | package app | |
| 3 | + | ||
| 4 | + | import ( | |
| 5 | + | "os" | |
| 6 | + | "path/filepath" | |
| 7 | + | ||
| 8 | + | "github.com/spf13/viper" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // DefaultDataDir returns the default data directory path. | |
| 12 | + | // Uses ~/.gordon for user installations, /var/lib/gordon as fallback. | |
| 13 | + | func DefaultDataDir() string { | |
| 14 | + | if homeDir, err := os.UserHomeDir(); err == nil { | |
| 15 | + | return filepath.Join(homeDir, ".gordon") | |
| 16 | + | } | |
| 17 | + | return "/var/lib/gordon" | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | // ConfigureViper sets up viper with standard config file search paths. | |
| 21 | + | // Config file: gordon.toml | |
| 22 | + | // Search paths (in order): /etc/gordon, ~/.config/gordon, current directory | |
| 23 | + | func ConfigureViper(v *viper.Viper, configPath string) { | |
| 24 | + | if configPath != "" { | |
| 25 | + | v.SetConfigFile(configPath) | |
| 26 | + | } else { | |
| 27 | + | v.SetConfigName("gordon") | |
| 28 | + | v.SetConfigType("toml") | |
| 29 | + | v.AddConfigPath("/etc/gordon") | |
| 30 | + | v.AddConfigPath("$HOME/.config/gordon") | |
| 31 | + | v.AddConfigPath(".") | |
| 32 | + | } | |
| 33 | + | } | |
| @@ -0,0 +1,33 @@ | |||||
| 1 | + | // Package app provides the application initialization and wiring. | |||
| 2 | + | package app | |||
| 3 | + | ||||
| 4 | + | import ( | |||
| 5 | + | "os" | |||
| 6 | + | "path/filepath" | |||
| 7 | + | ||||
| 8 | + | "github.com/spf13/viper" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // DefaultDataDir returns the default data directory path. | |||
| 12 | + | // Uses ~/.gordon for user installations, /var/lib/gordon as fallback. | |||
| 13 | + | func DefaultDataDir() string { | |||
| 14 | + | if homeDir, err := os.UserHomeDir(); err == nil { | |||
| 15 | + | return filepath.Join(homeDir, ".gordon") | |||
| 16 | + | } | |||
| 17 | + | return "/var/lib/gordon" | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | // ConfigureViper sets up viper with standard config file search paths. | |||
| 21 | + | // Config file: gordon.toml | |||
| 22 | + | // Search paths (in order): /etc/gordon, ~/.config/gordon, current directory | |||
| 23 | + | func ConfigureViper(v *viper.Viper, configPath string) { | |||
| 24 | + | if configPath != "" { | |||
| 25 | + | v.SetConfigFile(configPath) | |||
| 26 | + | } else { | |||
| 27 | + | v.SetConfigName("gordon") | |||
| 28 | + | v.SetConfigType("toml") | |||
| 29 | + | v.AddConfigPath("/etc/gordon") | |||
| 30 | + | v.AddConfigPath("$HOME/.config/gordon") | |||
| 31 | + | v.AddConfigPath(".") | |||
| 32 | + | } | |||
| 33 | + | } | |||
M internal/app/run.go +7 -15
| @@ -276,7 +276,7 @@ func createOutputAdapters(ctx context.Context, log zerowrap.Logger) (*docker.Run | |||
| 276 | 276 | func createStorage(cfg Config, log zerowrap.Logger) (*filesystem.BlobStorage, *filesystem.ManifestStorage, error) { | |
| 277 | 277 | dataDir := cfg.Server.DataDir | |
| 278 | 278 | if dataDir == "" { | |
| 279 | - | dataDir = "/var/lib/gordon" | |
| 279 | + | dataDir = DefaultDataDir() | |
| 280 | 280 | } | |
| 281 | 281 | ||
| 282 | 282 | registryDir := filepath.Join(dataDir, "registry") | |
| @@ -298,7 +298,7 @@ func createStorage(cfg Config, log zerowrap.Logger) (*filesystem.BlobStorage, *f | |||
| 298 | 298 | func createEnvLoader(cfg Config, log zerowrap.Logger) (*envloader.FileLoader, error) { | |
| 299 | 299 | dataDir := cfg.Server.DataDir | |
| 300 | 300 | if dataDir == "" { | |
| 301 | - | dataDir = "/var/lib/gordon" | |
| 301 | + | dataDir = DefaultDataDir() | |
| 302 | 302 | } | |
| 303 | 303 | ||
| 304 | 304 | envDir := cfg.Env.Dir | |
| @@ -339,7 +339,7 @@ func createLogWriter(cfg Config, log zerowrap.Logger) (*logwriter.LogWriter, err | |||
| 339 | 339 | if logDir == "" { | |
| 340 | 340 | dataDir := cfg.Server.DataDir | |
| 341 | 341 | if dataDir == "" { | |
| 342 | - | dataDir = "/var/lib/gordon" | |
| 342 | + | dataDir = DefaultDataDir() | |
| 343 | 343 | } | |
| 344 | 344 | logDir = filepath.Join(dataDir, "logs", "containers") | |
| 345 | 345 | } | |
| @@ -384,7 +384,7 @@ func createAuthService(ctx context.Context, cfg Config, log zerowrap.Logger) (ou | |||
| 384 | 384 | // Get data directory | |
| 385 | 385 | dataDir := cfg.Server.DataDir | |
| 386 | 386 | if dataDir == "" { | |
| 387 | - | dataDir = "/var/lib/gordon" | |
| 387 | + | dataDir = DefaultDataDir() | |
| 388 | 388 | } | |
| 389 | 389 | ||
| 390 | 390 | // Create token store (needed for token auth or to store revocations) | |
| @@ -723,7 +723,7 @@ func findPidFile() string { | |||
| 723 | 723 | func loadConfig(v *viper.Viper, configPath string) error { | |
| 724 | 724 | v.SetDefault("server.port", 80) | |
| 725 | 725 | v.SetDefault("server.registry_port", 5000) | |
| 726 | - | v.SetDefault("server.data_dir", "/var/lib/gordon") | |
| 726 | + | v.SetDefault("server.data_dir", DefaultDataDir()) | |
| 727 | 727 | v.SetDefault("logging.level", "info") | |
| 728 | 728 | v.SetDefault("logging.format", "console") | |
| 729 | 729 | v.SetDefault("logging.file.enabled", false) | |
| @@ -735,21 +735,13 @@ func loadConfig(v *viper.Viper, configPath string) error { | |||
| 735 | 735 | v.SetDefault("logging.container_logs.max_size", 100) | |
| 736 | 736 | v.SetDefault("logging.container_logs.max_backups", 3) | |
| 737 | 737 | v.SetDefault("logging.container_logs.max_age", 28) | |
| 738 | - | v.SetDefault("env.dir", "/var/lib/gordon/env") | |
| 738 | + | v.SetDefault("env.dir", "") // defaults to {data_dir}/env when empty | |
| 739 | 739 | v.SetDefault("secrets.backend", "unsafe") | |
| 740 | 740 | v.SetDefault("registry_auth.enabled", false) | |
| 741 | 741 | v.SetDefault("registry_auth.type", "password") | |
| 742 | 742 | v.SetDefault("registry_auth.token_expiry", "720h") | |
| 743 | 743 | ||
| 744 | - | if configPath != "" { | |
| 745 | - | v.SetConfigFile(configPath) | |
| 746 | - | } else { | |
| 747 | - | v.SetConfigName("gordon") | |
| 748 | - | v.SetConfigType("yaml") | |
| 749 | - | v.AddConfigPath("/etc/gordon") | |
| 750 | - | v.AddConfigPath("$HOME/.gordon") | |
| 751 | - | v.AddConfigPath(".") | |
| 752 | - | } | |
| 744 | + | ConfigureViper(v, configPath) | |
| 753 | 745 | ||
| 754 | 746 | if err := v.ReadInConfig(); err != nil { | |
| 755 | 747 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | |
| @@ -276,7 +276,7 @@ func createOutputAdapters(ctx context.Context, log zerowrap.Logger) (*docker.Run | |||||
| 276 | func createStorage(cfg Config, log zerowrap.Logger) (*filesystem.BlobStorage, *filesystem.ManifestStorage, error) { | 276 | func createStorage(cfg Config, log zerowrap.Logger) (*filesystem.BlobStorage, *filesystem.ManifestStorage, error) { | ||
| 277 | dataDir := cfg.Server.DataDir | 277 | dataDir := cfg.Server.DataDir | ||
| 278 | if dataDir == "" { | 278 | if dataDir == "" { | ||
| 279 | - | dataDir = "/var/lib/gordon" | 279 | + | dataDir = DefaultDataDir() |
| 280 | } | 280 | } | ||
| 281 | 281 | ||||
| 282 | registryDir := filepath.Join(dataDir, "registry") | 282 | registryDir := filepath.Join(dataDir, "registry") | ||
| @@ -298,7 +298,7 @@ func createStorage(cfg Config, log zerowrap.Logger) (*filesystem.BlobStorage, *f | |||||
| 298 | func createEnvLoader(cfg Config, log zerowrap.Logger) (*envloader.FileLoader, error) { | 298 | func createEnvLoader(cfg Config, log zerowrap.Logger) (*envloader.FileLoader, error) { | ||
| 299 | dataDir := cfg.Server.DataDir | 299 | dataDir := cfg.Server.DataDir | ||
| 300 | if dataDir == "" { | 300 | if dataDir == "" { | ||
| 301 | - | dataDir = "/var/lib/gordon" | 301 | + | dataDir = DefaultDataDir() |
| 302 | } | 302 | } | ||
| 303 | 303 | ||||
| 304 | envDir := cfg.Env.Dir | 304 | envDir := cfg.Env.Dir | ||
| @@ -339,7 +339,7 @@ func createLogWriter(cfg Config, log zerowrap.Logger) (*logwriter.LogWriter, err | |||||
| 339 | if logDir == "" { | 339 | if logDir == "" { | ||
| 340 | dataDir := cfg.Server.DataDir | 340 | dataDir := cfg.Server.DataDir | ||
| 341 | if dataDir == "" { | 341 | if dataDir == "" { | ||
| 342 | - | dataDir = "/var/lib/gordon" | 342 | + | dataDir = DefaultDataDir() |
| 343 | } | 343 | } | ||
| 344 | logDir = filepath.Join(dataDir, "logs", "containers") | 344 | logDir = filepath.Join(dataDir, "logs", "containers") | ||
| 345 | } | 345 | } | ||
| @@ -384,7 +384,7 @@ func createAuthService(ctx context.Context, cfg Config, log zerowrap.Logger) (ou | |||||
| 384 | // Get data directory | 384 | // Get data directory | ||
| 385 | dataDir := cfg.Server.DataDir | 385 | dataDir := cfg.Server.DataDir | ||
| 386 | if dataDir == "" { | 386 | if dataDir == "" { | ||
| 387 | - | dataDir = "/var/lib/gordon" | 387 | + | dataDir = DefaultDataDir() |
| 388 | } | 388 | } | ||
| 389 | 389 | ||||
| 390 | // Create token store (needed for token auth or to store revocations) | 390 | // Create token store (needed for token auth or to store revocations) | ||
| @@ -723,7 +723,7 @@ func findPidFile() string { | |||||
| 723 | func loadConfig(v *viper.Viper, configPath string) error { | 723 | func loadConfig(v *viper.Viper, configPath string) error { | ||
| 724 | v.SetDefault("server.port", 80) | 724 | v.SetDefault("server.port", 80) | ||
| 725 | v.SetDefault("server.registry_port", 5000) | 725 | v.SetDefault("server.registry_port", 5000) | ||
| 726 | - | v.SetDefault("server.data_dir", "/var/lib/gordon") | 726 | + | v.SetDefault("server.data_dir", DefaultDataDir()) |
| 727 | v.SetDefault("logging.level", "info") | 727 | v.SetDefault("logging.level", "info") | ||
| 728 | v.SetDefault("logging.format", "console") | 728 | v.SetDefault("logging.format", "console") | ||
| 729 | v.SetDefault("logging.file.enabled", false) | 729 | v.SetDefault("logging.file.enabled", false) | ||
| @@ -735,21 +735,13 @@ func loadConfig(v *viper.Viper, configPath string) error { | |||||
| 735 | v.SetDefault("logging.container_logs.max_size", 100) | 735 | v.SetDefault("logging.container_logs.max_size", 100) | ||
| 736 | v.SetDefault("logging.container_logs.max_backups", 3) | 736 | v.SetDefault("logging.container_logs.max_backups", 3) | ||
| 737 | v.SetDefault("logging.container_logs.max_age", 28) | 737 | v.SetDefault("logging.container_logs.max_age", 28) | ||
| 738 | - | v.SetDefault("env.dir", "/var/lib/gordon/env") | 738 | + | v.SetDefault("env.dir", "") // defaults to {data_dir}/env when empty |
| 739 | v.SetDefault("secrets.backend", "unsafe") | 739 | v.SetDefault("secrets.backend", "unsafe") | ||
| 740 | v.SetDefault("registry_auth.enabled", false) | 740 | v.SetDefault("registry_auth.enabled", false) | ||
| 741 | v.SetDefault("registry_auth.type", "password") | 741 | v.SetDefault("registry_auth.type", "password") | ||
| 742 | v.SetDefault("registry_auth.token_expiry", "720h") | 742 | v.SetDefault("registry_auth.token_expiry", "720h") | ||
| 743 | 743 | ||||
| 744 | - | if configPath != "" { | 744 | + | ConfigureViper(v, configPath) |
| 745 | - | v.SetConfigFile(configPath) | |||
| 746 | - | } else { | |||
| 747 | - | v.SetConfigName("gordon") | |||
| 748 | - | v.SetConfigType("yaml") | |||
| 749 | - | v.AddConfigPath("/etc/gordon") | |||
| 750 | - | v.AddConfigPath("$HOME/.gordon") | |||
| 751 | - | v.AddConfigPath(".") | |||
| 752 | - | } | |||
| 753 | 745 | ||||
| 754 | if err := v.ReadInConfig(); err != nil { | 746 | if err := v.ReadInConfig(); err != nil { | ||
| 755 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | 747 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | ||