magdev 661de2f3d8 feat: initial implementation of hubmanager v0.1.0
Add a Bash CLI tool to manage Docker Registry images remotely.
Supports Docker Hub and self-hosted Docker Registry v2 API with
automatic auth detection (bearer token or HTTP basic auth).

Subcommands: login, list, tags, inspect, delete, copy, prune
Dependencies: curl, jq, bash 4+

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 14:37:31 +01:00

hubmanager

A Bash CLI tool to manage Docker Registry images remotely. Supports Docker Hub and any self-hosted Docker Registry v2 API, with flexible authentication.

Requirements

  • Bash 4.0+
  • curl
  • jq

Installation

# System-wide
sudo install -m 755 hubmanager /usr/local/bin/hubmanager

# User-local
install -m 755 hubmanager ~/bin/hubmanager

Quick Start

# Test your credentials and save them
hubmanager login --registry https://registry.example.com \
    --user admin --password secret --save

# List all repositories
hubmanager list

# List tags for an image
hubmanager tags myuser/myapp

# Inspect an image
hubmanager inspect myuser/myapp:latest

# Delete an old tag
hubmanager delete myuser/myapp:v1.0.0

# Prune old tags (keep the 3 most recent)
hubmanager prune myuser/myapp --keep 3 --dry-run

Configuration

Credentials and registry settings are stored in ~/.hubmanager.conf. The file uses a simple KEY=VALUE format:

# ~/.hubmanager.conf
# chmod 600 ~/.hubmanager.conf

# Default registry and credentials
REGISTRY=https://registry.example.com
USERNAME=admin
PASSWORD=mysecretpassword

# Named registry aliases (use with --registry <alias>)
REGISTRY_STAGING_URL=https://staging-registry.example.com
REGISTRY_STAGING_USERNAME=deploy
REGISTRY_STAGING_PASSWORD=deploytoken

REGISTRY_HUB_URL=https://registry-1.docker.io
REGISTRY_HUB_USERNAME=myuser
REGISTRY_HUB_PASSWORD=myhubtoken

The file must be readable only by the owner (chmod 600). hubmanager will warn if permissions are too open.

Named aliases let you switch registries with a short name:

hubmanager --registry staging list
hubmanager --registry hub tags myuser/myapp

Global Options

hubmanager [OPTIONS] <command> [COMMAND OPTIONS]

  -r, --registry <url>    Registry base URL
                          Default: https://registry-1.docker.io
  -u, --user <username>   Username (overrides config file)
  -p, --password <pass>   Password or token (overrides config file)
      --config <file>     Config file path (default: ~/.hubmanager.conf)
      --json              Output raw JSON (pipe-friendly)
      --no-color          Disable ANSI color
  -v, --verbose           Show HTTP request details (with auth redacted)
  -q, --quiet             Suppress all non-error output
  -h, --help              Show help
      --version           Show version

Commands

login — Test and save credentials

hubmanager login [--registry URL] [--user USER] [--password PASS] [--save]

Validates credentials against the registry. Use --save to write them to the config file.

hubmanager login --registry https://registry.example.com \
    --user admin --password secret --save
# Login Succeeded — bearer auth, registry: https://registry.example.com
# Credentials saved to /home/user/.hubmanager.conf

list — List repositories

hubmanager list [--limit N] [--last REPO]

Lists all repositories in the registry. On Docker Hub, lists repositories for the authenticated user (Docker Hub restricts the _catalog endpoint).

hubmanager list
# REPOSITORY
# myuser/myapp
# myuser/myapi
# myuser/nginx-custom

# Paginate
hubmanager list --limit 50 --last myuser/myapi

# JSON output
hubmanager list --json | jq '.repositories[]'

tags — List tags for an image

hubmanager tags <image> [--limit N] [--last TAG]
hubmanager tags myuser/myapp
# TAG
# latest
# v1.2.3
# v1.2.2
# develop

# Official Docker Hub images
hubmanager tags nginx

# JSON
hubmanager tags myuser/myapp --json | jq '.tags[]'

inspect — Show image details

hubmanager inspect <image>:<tag|digest> [--platform OS/ARCH]

Shows manifest digest, size, OS/arch, creation date, labels, and layer breakdown.

hubmanager inspect myuser/myapp:latest
# Image:            myuser/myapp:latest
# Digest:           sha256:abc123...
# MediaType:        application/vnd.docker.distribution.manifest.v2+json
# CompressedSize:   32.7 MB (34299597 bytes)
# OS/Arch:          linux/amd64
# Created:          2024-01-15T10:30:00.000000000Z
# Labels:
#   maintainer=dev@example.com
#   version=1.2.3
# Layers:           3
#   [0] sha256:1111... (27.8 MB)
#   [1] sha256:2222... (4.4 MB)
#   [2] sha256:3333... (1000.0 KB)

# Multi-arch image — shows all platforms
hubmanager inspect nginx:latest

# Multi-arch — drill into a specific platform
hubmanager inspect nginx:latest --platform linux/arm64

# Inspect by digest
hubmanager inspect myuser/myapp@sha256:abc123...

# JSON output (includes _digest field)
hubmanager inspect myuser/myapp:latest --json | jq .

delete — Delete a tag or manifest

hubmanager delete <image>:<tag|digest> [--yes]

Deletes a manifest by resolving the tag to its content-addressable digest, then issuing a DELETE. Requires REGISTRY_STORAGE_DELETE_ENABLED=true on self-hosted registries. Not supported on Docker Hub.

hubmanager delete myuser/myapp:v1.0.0
# About to delete: myuser/myapp @ sha256:abc123...
# Registry: https://registry.example.com
# Type 'yes' to confirm: yes
# Deleted: myuser/myapp @ sha256:abc123...

# Skip confirmation
hubmanager delete myuser/myapp:v1.0.0 --yes

# Delete by digest directly
hubmanager delete myuser/myapp@sha256:abc123... --yes

copy — Copy or retag an image

hubmanager copy <src-image>:<tag> <dst-image>:<tag> [options]

Options:
  --src-registry URL    Source registry (default: global --registry)
  --dst-registry URL    Destination registry (default: global --registry)
  --src-user USER       Source username
  --src-password PASS   Source password
  --dst-user USER       Destination username
  --dst-password PASS   Destination password
  --platform OS/ARCH    Copy only one platform from a multi-arch image

Same-registry retag — attempts cross-repo blob mount (no data transfer):

hubmanager copy myuser/myapp:v1.2.3 myuser/myapp:stable

Cross-registry copy — streams blobs from source to destination:

hubmanager copy myuser/myapp:latest \
    --src-registry https://registry-1.docker.io \
    --dst-registry https://registry.example.com \
    mycompany/myapp:latest

Copy specific platform from a multi-arch image:

hubmanager copy nginx:latest myuser/nginx-amd64:latest --platform linux/amd64

prune — Delete outdated tags

hubmanager prune <image> [options]

Options:
  --keep N             Number of recent tags to keep (default: 3)
  --older-than DAYS    Delete tags older than N days (overrides --keep)
  --exclude PATTERN    Extended regex of tags to never delete
                       Default: "^(latest|stable|main|master|release)$"
  --no-exclude         Disable the default exclusion pattern
  -n, --dry-run        Show what would be deleted without deleting
  -y, --yes            Skip confirmation prompt

Tags are sorted by image creation date (newest first). The newest N are kept; the rest are deleted. Tags matching the exclusion pattern are always preserved.

# Preview: keep 5 most recent, protect default tags
hubmanager prune myuser/myapp --keep 5 --dry-run

# Keep 3 most recent, auto-confirm
hubmanager prune myuser/myapp --keep 3 --yes

# Delete anything older than 30 days
hubmanager prune myuser/myapp --older-than 30 --dry-run

# Custom exclusion: never delete latest or any semver tag
hubmanager prune myuser/myapp --keep 5 \
    --exclude "^(latest|v[0-9]+\.[0-9]+(\.[0-9]+)?)$"

# Prune everything (no exclusions)
hubmanager prune myuser/myapp --keep 1 --no-exclude --dry-run

Authentication

hubmanager automatically detects the authentication method by probing the registry's /v2/ endpoint:

Registry type Auth method
Docker Hub Bearer tokens via auth.docker.io
Harbor, self-hosted with token server Bearer tokens via registry-configured realm
Basic-auth self-hosted HTTP Basic Auth on every request
Public/anonymous registries No auth

Bearer tokens are cached in memory for the duration of the session and refreshed automatically when they expire.

Docker Hub notes

  • list uses the Docker Hub REST API (hub.docker.com) because the _catalog endpoint is restricted on Docker Hub.
  • delete is not supported via the v2 API on Docker Hub. Use the web UI at https://hub.docker.com.
  • prune is not supported on Docker Hub for the same reason.

JSON output

All commands support --json for machine-readable output:

# Get all tags as a JSON array
hubmanager tags myapp --json | jq '.tags'

# Get digest of latest
hubmanager inspect myapp:latest --json | jq '._digest'

# Delete and capture result
hubmanager delete myapp:old --yes --json
# {"deleted":true,"digest":"sha256:..."}

# Prune and capture counts
hubmanager prune myapp --keep 3 --yes --json
# {"deleted":5,"failed":0}

Exit Codes

Code Meaning
0 Success
1 General / usage error
2 Authentication failure
3 Resource not found (404)
4 Permission denied (403)
5 Registry server error (5xx)
6 Network / connectivity error
7 Operation not supported by registry

Examples

# Mirror all tags of an image to a private registry
for tag in $(hubmanager tags nginx --json | jq -r '.tags[]'); do
    hubmanager copy nginx:$tag \
        --src-registry https://registry-1.docker.io \
        --dst-registry https://registry.example.com \
        mycompany/nginx:$tag
done

# List images older than 60 days (dry run)
hubmanager prune myuser/myapp --older-than 60 --dry-run

# Get the SHA256 digest of the production image
DIGEST=$(hubmanager inspect myuser/myapp:production --json | jq -r '._digest')
echo "Production image: $DIGEST"

# Promote staging image to production (retag)
hubmanager copy myuser/myapp:staging myuser/myapp:production

Configuration reference

Key Description
REGISTRY Default registry URL
USERNAME Default username
PASSWORD Default password or token
REGISTRY_<ALIAS>_URL URL for a named registry alias
REGISTRY_<ALIAS>_USERNAME Username for a named alias
REGISTRY_<ALIAS>_PASSWORD Password for a named alias

Aliases are case-insensitive and treat - as _. For example, alias my-reg maps to REGISTRY_MY_REG_URL.

Description
Bash CLI tool for managing Docker Registry images remotely. The tool supports both Docker Hub and self-hosted Docker Registry v2 API, with authentication via config file and/or CLI flags.
Readme 57 KiB
Languages
Shell 100%