npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@agile-north/dockship

v1.15.0

Published

Docker container versioning, building, and publishing CLI for opinionated CI/CD

Readme

dockship

Opinionated Docker image versioning, building, and publishing for CI/CD pipelines.

npm version npm downloads license node CI Release

Source repository: github.com/agile-north/dockship

dockshiprepeat

What is dockship?

@agile-north/dockship is a lightweight CLI tool that standardizes Docker container builds across projects. It handles:

  • Semantic versioning from multiple sources (Node.js, .NET, GitVersion, NBGV, custom providers)
  • Multi-tag Docker builds (version, major, major.minor, optional latest)
  • Prerelease-aware tags: major and major.minor tags preserve prerelease suffixes when present (1.2.3-beta.11-beta.1, 1.2-beta.1)
  • Registry push with consistent tagging strategy
  • Polyglot support – Node.js, C#/.NET, GitVersion, NBGV, and extensible to any language
  • Git-height versioning – automatic build number from commit count

Perfect for microservices, full-stack apps, and standardized CI/CD across teams.

Installation

You can use dockship in two ways:

  1. Install as a dev dependency (recommended for repeatable team workflows)
  2. Run directly with npx (no local install required)

Option 1: Install in your project

npm install -D @agile-north/dockship

Then add to package.json scripts:

{
  "scripts": {
    "dock:build": "dock build",
    "dock:ship": "dock ship",
    "dock:all": "dock build && dock ship"
  }
}

Option 2: Run directly with npx (no install)

# Run without adding dockship to package.json
npx @agile-north/dockship version
npx @agile-north/dockship build
npx @agile-north/dockship ship
npx @agile-north/dockship all
npx @agile-north/dockship tags

For reproducible CI/local behavior, you can pin a version:

npx @agile-north/[email protected] version

Automated npm publish (GitHub Actions)

This repo includes a release workflow at .github/workflows/release.yml.

  • Triggers on pushes to main and develop
  • Can also be run manually with workflow_dispatch
  • Skips publishing when that exact package version already exists on npm

Version management

Versioning is automated by release-please via .github/workflows/release.yml.

How it works:

  1. Merge PRs into main using Conventional Commits (see CONTRIBUTING.md)
  2. release-please detects feat:, fix:, perf: commits and opens a Release PR that bumps package.json version and updates CHANGELOG.md
  3. Review and merge the Release PR
  4. release-please sets release_created=true, which runs the inline publish job and pushes to npm latest

Version bump rules (SemVer):

  • feat: commits → minor bump (0.1.00.2.0)
  • fix: / perf: commits → patch bump (0.1.00.1.1)
  • feat!: / BREAKING CHANGE: footer → major bump (0.1.01.0.0)

Branch/channel behavior:

  • Release PR merged to main with release_created=true → stable publish to npm latest
  • develop push → prerelease publish to npm next (X.Y.Z-dev.<run>.<sha>)
  • manual workflow_dispatch → only main (stable) or develop (prerelease); other refs are rejected

Allowed publish routes (enforced):

  • Release PR merged on main (release_created=true) → stable publish
  • Push to develop → prerelease publish
  • Manual run on main or develop only → stable/prerelease respectively
  • Direct push to main → not a publish route

CI validation:

  • .github/workflows/ci.yml runs on PRs and pushes to main, develop, and feature/**
  • It runs npm test and validates installability and npm package metadata using dry-run checks
  • Configure branch protection on main and require status checks CI / validate and PR Title / conventional-pr-title before merge

Local validation:

npm run lint:md
npm test

Optional (recommended) one-time setup to enforce markdown lint before commits:

npm run hooks:install

Hooks are installed automatically on npm install via the prepare script.

Authentication options:

  • Trusted publishing: configure this GitHub repo as a trusted publisher in npm settings; no NPM_TOKEN secret is required for package publish.

Quick Start

1. Optional: create .dockship/dockship.json

{
  "strictConfig": false,                  // Optional: fail fast on invalid config/legacy keys
  "docker": {
    "file": "Dockerfile",
    "context": ".",
    "target": {
      "registry": "registry.example.com",
      "repository": "my-org/my-app"
    },
    "login": {
      "registry": "registry.example.com"
    },
    "push": {
      "enabled": false,
      "branches": ["main", "develop"],
      "denyNonPublicPush": false
    },
    "tags": {
      "latest": false
    },
    "tagPolicy": {
      "public": ["full", "majorMinor", "major"],
      "nonPublic": ["full", "majorMinor", "major"]
    },
    "nonPublicMode": "",
    "aliases": {
      "branch": false,
      "sanitizedBranch": false
    }
  },
  "git": {
    "publicBranches": ["main", "release/*"],
    "nonPublicBranches": ["feature/*", "hotfix/*"]
  },

  "version": {
    "provider": "auto",
    "nodejs": {
      "packageJsonPath": "package.json",
      "mode": "git-height"
    }
  }
}

2. Create Dockerfile

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm ci && npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]

3. Run commands

# Show resolved version
npx dock version

# Build image with computed version
npx dock build

# Ship image to registry
npx dock ship

# Build and ship
npx dock all

# Show generated tags
npx dock tags

# Apply computed policy tags to local image references
npx dock tag

# Preview deterministic tags and push policy outcome
npx dock plan

# Emit machine-readable envelope output
npx dock version --json
npx dock tags --output json
npx dock all --json

JSON Output

Dockship supports structured output for automation and CI pipelines.

  • --json is shorthand for --output json
  • --output supports human (default) and json

Output-to-file flags (all equivalent):

  • --json-file <path>
  • --output-file <path>
  • --output-json-file <path>

When any output-file flag is used, dockship writes the JSON envelope to that file and keeps stdout clean.

In json mode:

  • stdout contains exactly one JSON object
  • stderr may contain diagnostic command output
  • exit codes follow command status semantics

JSON contract reference:

  • docs/output.md

Examples:

# Full envelope for version resolution
npx dock version --json

# Computed tags and fully-qualified references
npx dock tags --output json

# Build + push with step-level results
npx dock all --json

# Write envelope to file (aliases supported)
npx dock tags --json-file dock-tags.json
npx dock tags --output-file dock-tags.json
npx dock tags --output-json-file dock-tags.json

Extract values:

# deployable image reference
jq -r '.result.artifact.image.reference'

# immutable artifact identifier
jq -r '.result.artifact.id'

Documentation

Keep README focused on onboarding and common usage. Detailed feature contracts live under docs/.

  • docs/output.md - JSON output contract and status semantics

Configuration: dockship.json

Docker Settings

The config file is optional. If .dockship/dockship.json is missing, dockship uses built-in defaults:

  • strictConfig = false
  • docker.file = "Dockerfile"
  • docker.context = "."
  • docker.push.enabled = false
  • docker.push.branches = []
  • docker.push.branchesShortcut = ""
  • docker.push.denyNonPublicPush = false
  • docker.tags.latest = false
  • docker.tagPolicy.public = ["full", "majorMinor", "major"]
  • docker.tagPolicy.nonPublic = ["full", "majorMinor", "major"]
  • docker.nonPublicMode = ""
  • docker.aliases.branch = false
  • docker.aliases.sanitizedBranch = false
  • docker.aliases.nonPublicPrefix = ""
  • docker.aliases.rules = []
  • docker.cleanup.local = "auto"
  • docker.runner = "build"
  • git.publicBranches = []
  • git.publicBranchesShortcut = ""
  • git.nonPublicBranches = []
  • git.nonPublicBranchesShortcut = ""

For dock build, dock ship, and dock all, image target settings still need to come from config or env:

  • DOCKER_TARGET_REGISTRY
  • DOCKER_TARGET_REPOSITORY

Configuration precedence is:

  1. CI/runtime environment variables
  2. .env values at the repo root
  3. .dockship/dockship.json
  4. built-in defaults
{
  "strictConfig": false,
  "docker": {
    "file": "Dockerfile",                  // Path to Dockerfile
    "context": ".",                        // Build context
    "target": {
      "registry": "registry.io",           // Docker registry
      "repository": "org/app"              // Image repository
    },
    "login": {
      "registry": "registry.io"            // Optional: login registry override
    },
    "push": {
      "enabled": false,                     // Enable/disable push
      "branches": ["main", "develop"],    // Optional: allowed branches, supports *
      "branchesShortcut": "main,develop",  // Optional: comma/semicolon/newline-delimited shortcut
      "denyNonPublicPush": false             // Optional: deny pushes for non-public classified builds
    },
    "tags": {
      "latest": false                       // Add 'latest' tag
    },
    "tagPolicy": {
      "public": ["full", "majorMinor", "major"],
      "nonPublic": ["full", "majorMinor", "major"]
    },
    "nonPublicMode": "",                   // Optional: "full-only" limits non-public tags to full version
    "aliases": {
      "branch": false,                      // Optional: add branch alias tag
      "sanitizedBranch": false              // Optional: add lowercase sanitized branch alias tag
    },
    "cleanup": {
      "local": "auto"                    // Auto: clean in CI, keep locally for dev
    },
    "runner": "build",                  // Optional: build, buildx, or auto
    "platform": "linux/amd64",            // Optional: build platform
    "buildTarget": "export-stage",        // Optional: docker build --target
    "buildOutput": "type=local,dest=./out", // Optional: docker build --output
    "buildArgs": { "ENV": "prod" },       // Optional: extra docker build args
    "secrets": {
      "npm_token": { "env": "NPM_TOKEN" },
      "signing_key": { "file": "./secrets/signing.key" }
    },
    "stages": {
      "validate": {
        "target": "validate",
        "output": "type=local,dest=./stage-validate"
      },
      "test": {
        "target": "test",
        "output": "type=local,dest=./stage-test"
      }
    }
  },
  "git": {
    "publicBranches": ["main", "release/*"],
    "publicBranchesShortcut": "main,release/*",
    "nonPublicBranches": ["feature/*", "hotfix/*"],
    "nonPublicBranchesShortcut": "feature/*,hotfix/*"
  }
}

Docker option reference

| Setting | Type | Default | Env override | Notes | | --- | --- | --- | --- | --- | | strictConfig | boolean | false | DOCKSHIP_STRICT_CONFIG | When true, fails fast on invalid .dockship/dockship.json and on legacy flat compatibility keys | | docker.file | string | Dockerfile | DOCKERFILE_PATH | Path to the Dockerfile relative to the repo root | | docker.context | string | . | DOCKER_CONTEXT | Docker build context | | docker.target.registry | string | empty | DOCKER_TARGET_REGISTRY | Required for dock build, dock ship, and dock all | | docker.target.repository | string | empty | DOCKER_TARGET_REPOSITORY | Required for dock build, dock ship, and dock all | | docker.login.registry | string | empty | DOCKER_LOGIN_REGISTRY | Optional login registry override; defaults to docker.target.registry | | docker.push.enabled | boolean | false | DOCKER_PUSH_ENABLED | Enables pushing for dock ship and dock all | | docker.push.branches | string[] | [] | DOCKER_PUSH_BRANCHES | Branch allow-list; supports * wildcards | | docker.push.branchesShortcut | string | empty | none | Shortcut for push branch allow-list using comma/semicolon/newline delimiters | | docker.push.denyNonPublicPush | boolean | false | none | When true, non-public classified builds are not pushed | | docker.tags.latest | boolean | false | DOCKER_TAG_LATEST | Adds the latest tag in addition to semantic tags | | docker.tags.stripPreReleaseNumber | boolean | true | none | When true, strips the trailing commit-count number from the prerelease suffix on moving tags only. The full version tag is unaffected. Example: 1.2.3-beta.2 produces tags 1.2.3-beta.2, 1-beta, 1.2-beta. Set to false to preserve 1-beta.2 and 1.2-beta.2. | | docker.tagPolicy.public | string[] | full,majorMinor,major | none | Tag kinds for public builds (full, majorMinor, major, latest) | | docker.tagPolicy.nonPublic | string[] | full,majorMinor,major | none | Tag kinds for non-public builds | | docker.nonPublicMode | string | empty | none | full-only emits only the full version tag for non-public builds | | docker.aliases.branch | boolean | false | none | Adds branch alias tags (e.g., Feature/demo -> Feature-demo) | | docker.aliases.sanitizedBranch | boolean | false | none | Adds lowercase sanitized branch alias tags (e.g., Feature/demo_branch -> feature-demo-branch) | | docker.aliases.sanitize | boolean | false | none | When true, sanitizes the full emitted alias value after any prefix/suffix and rule formatting are applied | | docker.aliases.prefix | string | empty | none | Global alias prefix applied after alias generation | | docker.aliases.suffix | string | empty | none | Global alias suffix applied after alias generation | | docker.aliases.maxLength | number | 0 | none | Deterministic max alias length (0 = unlimited) | | docker.aliases.nonPublicPrefix | string | empty | none | Optional prefix applied to alias tags when the build is classified non-public. Set to np- to prefix non-public aliases (for example feature-demo -> np-feature-demo) | | docker.aliases.rules | object[] | [] | none | Ordered alias rules, first-match-wins (match, matchBranch, matchVersion, template, aliases, sanitize, optional per-rule formatting) | | docker.tags.aliases | object | none | none | Alternate location for alias settings; merged into docker.aliases for compatibility | | docker.cleanup.local | auto | boolean | auto | DOCKER_CLEANUP_LOCAL | auto cleans up in CI and keeps images locally for dev. true always removes built tags after dock build/dock all; false never removes them. | | docker.runner | build | buildx | auto | build | DOCKER_RUNNER | Selects Docker runner command. auto prefers docker buildx build and falls back to docker build | | docker.platform | string | empty | DOCKER_PLATFORM | Passed to docker build --platform | | docker.buildTarget | string | empty | DOCKER_BUILD_TARGET | Passed to docker build --target | | docker.buildOutput | string or string[] | empty | DOCKER_BUILD_OUTPUT | Passed to docker build --output; this may be type=local,dest=... or other buildx output spec | | docker.buildArgs | object | {} | DOCKER_BUILD_ARGS, DOCKER_BUILD_ARGS_FILE | Key/value pairs passed as --build-arg KEY=value; appended before the auto-generated APP_VERSION build arg. DOCKER_BUILD_ARGS accepts JSON ({"K":"v"}) or semicolon-delimited KEY=value;KEY2=value2. DOCKER_BUILD_ARGS_FILE points to a JSON file containing a build-args object | | docker.secrets | object | {} | DOCKER_SECRETS, DOCKER_SECRET_<ID> | BuildKit secrets mapped to --secret. Each entry supports { "env": "ENV_NAME" } or { "file": "./path" }. Requires docker.runner buildx (or auto resolving to buildx). DOCKER_SECRETS accepts JSON object and DOCKER_SECRET_<ID> overrides by id | | docker.stages | object | {} | n/a | Stage definitions for dock stage <name> and dock stage all. Each stage may set target/output/runner to override docker.buildTarget, docker.buildOutput, docker.runner | | docker.stageFallback | boolean | true | n/a | When true, dock stage all runs a final no-target build after configured stages (default). | | git.publicBranches | string[] | [] | n/a | Optional patterns classifying builds as public | | git.publicBranchesShortcut | string | empty | n/a | Shortcut for public branch patterns using comma/semicolon/newline delimiters | | git.nonPublicBranches | string[] | [] | n/a | Optional patterns classifying builds as non-public | | git.nonPublicBranchesShortcut | string | empty | n/a | Shortcut for non-public branch patterns using comma/semicolon/newline delimiters |

Alias Tags

Alias tags are additional Docker tags layered on top of semantic version tags. You can use simple toggles, rule-based aliases, or both together.

Some rule-based settings can also rewrite the generated semantic tags themselves using tagPrefix, tagSuffix, tagMaxLength, or tagNonPublicPrefix.

Simple options:

  • docker.aliases.branch: true adds a branch alias (sanitized path separators)
  • docker.aliases.sanitizedBranch: true adds a lowercase sanitized branch alias
  • docker.aliases.sanitize: true makes built-in branch alias output lowercase and sanitized by default

Examples for branch Feature/customer-west:

  • branch: true -> Feature-customer-west
  • sanitizedBranch: true -> feature-customer-west

Non-public prefix behavior:

  • docker.aliases.nonPublicPrefix is optional and defaults to empty
  • when set (for example np-), it is prepended to aliases only for non-public builds
  • non-public classification comes from branch classification (git.nonPublicBranches) or suffix/guardrail behavior

Global alias formatting options:

  • prefix: prepend to all generated aliases
  • suffix: append to all generated aliases
  • maxLength: deterministic truncation (0 means unlimited)

Rule-based aliases (docker.aliases.rules) are evaluated in order with first-match-wins selection. A matching rule can either emit additional alias tags from template/alias/aliases, transform the generated semantic tags with tag* settings, or both.

Important behavior:

  • If a rule only provides tagPrefix/tagSuffix/tagMaxLength/tagNonPublicPrefix, it rewrites semantic tags but does not emit a branch alias by itself.
  • If a rule provides template/alias/aliases, it emits alias tags in addition to any semantic tag transform.
  • caseInsensitive controls rule matching behavior and does not affect emitted tag formatting.

Rule shape:

  • id: stable identifier for plan trace output
  • match: wildcard (*) or regex (regex: prefix) pattern against the branch name, or an object with branch and version entries
  • matchBranch: wildcard or regex branch pattern shortcut
  • matchVersion: wildcard or regex version pattern shortcut, matched against semVer2 when available or version + suffix otherwise
  • caseInsensitive: optional boolean to evaluate this rule case-insensitively
  • template: supports $BRANCH, $BRANCH_SANITIZED, $0 (entire matched value), and capture references $1..$9
  • alias/aliases: static alias values
  • sanitize: none, branch, sanitized, or boolean (true => sanitized, false => none). When set on a rule, this sanitization applies to the complete alias value after prefix/suffix and rule formatting are combined, and also applies to semantic tag transforms produced by tag* settings.
  • tagMode: append or replace. When replace, semantic transforms from tagPrefix/tagSuffix wrap the original semantic tag rather than discarding it. Default is replace.
  • prefix/suffix/maxLength/nonPublicPrefix: optional per-rule alias formatting overrides
  • tagPrefix/tagSuffix/tagMaxLength/tagNonPublicPrefix: optional per-rule semantic tag transforms that rewrite the generated semantic tags

Template tokens:

  • $BRANCH: raw normalized branch name
  • $BRANCH_SANITIZED: lowercase sanitized branch name
  • $0: entire matched value for wildcard/plain/regex patterns
  • $1..$9: regex capture groups from the matching rule pattern

Matching behavior:

  • alias rules are case-sensitive by default
  • set caseInsensitive: true on a rule to match wildcard/regex patterns without case sensitivity

Examples:

  • Transform-only rule:
{
  "match": "topic/*",
  "tagSuffix": "-$0",
  "sanitize": true
}

This rewrites semantic tags to 1.2.3-topic-auth and does not emit a separate branch alias.

  • Alias-emitting rule:
{
  "match": "release/*",
  "template": "release-$0",
  "sanitize": false
}

This emits release-release/1.2 as an alias (or sanitized form if sanitize is enabled).

  • Case-insensitive match:
{
  "match": "Topic/*",
  "caseInsensitive": true,
  "template": "lane-$0",
  "sanitize": true
}

This matches topic/Auth_Branch as well as Topic/Auth_Branch.

  • Branch and version match:
{
  "match": {
    "branch": "topic/*",
    "version": "*-g*"
  },
  "tagPrefix": "release-",
  "tagSuffix": "-$0",
  "tagMode": "replace",
  "sanitize": true
}

This matches only when both the branch and the resolved version suffix match, letting rules apply specifically to NBGV-style -g... versions.

Example alias config:

{
  "docker": {
    "aliases": {
      "rules": [
        {
          "id": "release-qa",
          "match": "release/current-qa",
          "tagSuffix": "-qa"
        }
      ]
    }
  }
}

With branch release/current-qa and version 1.1.323, this would produce 1-qa, 1.1-qa, and 1.1.323-qa.

Alias rule config example:

{
  "docker": {
    "aliases": {
      "branch": true,
      "sanitizedBranch": false,
      "nonPublicPrefix": "np-",
      "rules": [
        {
          "id": "release-lane",
          "match": "regex:^release\\/(\\d+)\\.(\\d+)$",
          "template": "rel-$1-$2",
          "sanitize": "sanitized"
        },
        {
          "id": "feature-lane",
          "match": "feature/*",
          "template": "feat-$BRANCH_SANITIZED"
        }
      ]
    }
  }
}

branch/sanitizedBranch toggles and rules are additive: both can be enabled at the same time.

To export files or artifacts from intermediate Dockerfile stages, set the runner to buildx (or auto) and configure target/output using DOCKER_BUILD_TARGET and DOCKER_BUILD_OUTPUT.

Example:

DOCKER_RUNNER=buildx \
DOCKER_BUILD_TARGET=export-stage \
DOCKER_BUILD_OUTPUT='type=local,dest=./out' \
npx dock build

BuildKit Secrets (env and file)

Dockship maps docker.secrets (or DOCKER_SECRETS) to Docker BuildKit --secret flags.

  • { "env": "ENV_NAME" } becomes --secret id=<id>,env=ENV_NAME
  • { "file": "./path" } becomes --secret id=<id>,src=./path

Important behavior:

  • secrets require docker.runner buildx (or auto resolving to buildx)
  • secret values are provided from host/CI environment or files at build time
  • secrets are not copied into the Docker build context by dockship

Example dockship.json:

{
  "docker": {
    "runner": "buildx",
    "secrets": {
      "npm_token": { "env": "NPM_TOKEN" },
      "signing_key": { "file": "./secrets/signing.key" }
    }
  }
}

GitHub Actions note: GitHub does not automatically expose secrets as ./secrets/* files. Secrets are exposed as workflow expressions and environment variables unless you explicitly write them to files.

GitHub Actions (env-backed secret):

- name: Build with dockship and BuildKit secret
  run: npx dock build
  env:
    DOCKER_RUNNER: buildx
    DOCKER_TARGET_REGISTRY: ${{ secrets.REGISTRY }}
    DOCKER_TARGET_REPOSITORY: my-org/my-app
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Azure DevOps (file-backed secret):

- script: |
    mkdir -p .secrets
    printf "%s" "$(SigningKey)" > .secrets/signing.key
    npx dock build
  env:
    DOCKER_RUNNER: buildx
    DOCKER_TARGET_REGISTRY: $(DockerRegistry)
    DOCKER_TARGET_REPOSITORY: my-org/my-app

Example:

DOCKER_RUNNER=buildx \
DOCKER_SECRETS='{"npm_token":{"env":"NPM_TOKEN"}}' \
NPM_TOKEN='token-value' \
npx dock build

Optional registry login environment variables:

  • DOCKER_LOGIN_USERNAME
  • DOCKER_LOGIN_PASSWORD
  • DOCKER_LOGIN_REGISTRY (defaults to docker.login.registry, then DOCKER_TARGET_REGISTRY)

When DOCKER_LOGIN_USERNAME and DOCKER_LOGIN_PASSWORD are set, dockship creates a temporary isolated Docker config, runs docker login for that invocation, and removes the temporary config afterward. This avoids changing the user's normal Docker login state.

Legacy aliases remain supported for compatibility:

  • DOCKER_AUTH_USERNAME
  • DOCKER_AUTH_PASSWORD
  • DOCKER_AUTH_REGISTRY

dock ship and dock all only push when both of these are true:

  • docker.push.enabled is true
  • the current branch matches docker.push.branches when a branch list is configured

If docker.push.branches is omitted or empty, any branch may push.

If docker.push.denyNonPublicPush is true, pushes are skipped for builds classified as non-public.

Branch matching details:

  • patterns are matched against the full normalized branch name
  • * matches any sequence of characters
  • regex patterns are supported with regex: prefix, for example regex:^release\/\d+\.\d+$
  • examples: main, develop, release/*, feature/*
  • branch name detection uses common CI branch variables first, then falls back to git rev-parse --abbrev-ref HEAD
  • alias rules use ordered first-match-wins selection and still emit full plan trace for matched and non-matched rules

Legacy flat keys are still accepted for compatibility:

  • docker.dockerfiledocker.file
  • docker.targetRegistrydocker.target.registry
  • docker.targetRepositorydocker.target.repository
  • docker.loginRegistrydocker.login.registry
  • docker.pushEnableddocker.push.enabled
  • docker.pushBranchesdocker.push.branches
  • docker.tagLatestdocker.tags.latest
  • git.qaBranchesgit.nonPublicBranches
  • git.nextBranchesgit.nonPublicBranches

When legacy flat keys are present, dockship emits a warning on stderr and continues using compatibility behavior.

If strictConfig (or DOCKSHIP_STRICT_CONFIG=true) is enabled, dockship fails instead of warning when legacy flat keys are present.

Non-public guardrail behavior:

  • if a build resolves as non-public and the resolved version has no suffix, dockship applies -pre to generated major and majorMinor tags
  • the full version tag remains unchanged
  • this is visible in dock plan --json as nonPublicGuardrailApplied: true

Branch source precedence for current branch detection:

  1. GITHUB_HEAD_REF
  2. GITHUB_REF_NAME
  3. BUILD_SOURCEBRANCHNAME
  4. BUILD_SOURCEBRANCH
  5. BRANCH_NAME
  6. CI_COMMIT_REF_NAME
  7. TEAMCITY_BUILD_BRANCH
  8. GIT_BRANCH
  9. fallback: git rev-parse --abbrev-ref HEAD

docker.cleanup.local: "auto" resolves to true only when a CI signal is detected. Supported CI signals are:

  • CI
  • GITHUB_ACTIONS
  • GITLAB_CI
  • TF_BUILD
  • BUILDKITE
  • TEAMCITY_VERSION
  • JENKINS_URL

Environment variable overrides (CI):

  • DOCKER_TARGET_REGISTRY
  • DOCKER_TARGET_REPOSITORY
  • DOCKER_PUSH_ENABLED
  • DOCKER_PUSH_BRANCHES
  • DOCKER_TAG_LATEST
  • DOCKER_CLEANUP_LOCAL
  • DOCKER_RUNNER
  • DOCKSHIP_STRICT_CONFIG
  • DOCKER_LOGIN_USERNAME
  • DOCKER_LOGIN_PASSWORD
  • DOCKER_LOGIN_REGISTRY

Version Providers

version.provider supports "auto", "nodejs", "dotnet", "gitversion", "nbgv", "env", and any custom provider name.

When version.provider is "auto", dockship uses this order:

  1. version.jsonnbgv
  2. GitVersion.ymlgitversion
  3. package.jsonnodejs
  4. MSBuild and assembly metadata files → dotnet
  5. DOCKSHIP_VERSION env var → env

If a provider selected by auto-detection fails to resolve a valid version, dockship will retry with env when DOCKSHIP_VERSION (or version.env.version) is available.

The built-in dotnet provider auto-discovers:

  • .csproj, .vbproj, and .fsproj
  • Directory.Build.props and Directory.Build.targets
  • VersionInfo.cs, VersionInfo.vb, VersionInfo.fs
  • AssemblyInfo.cs, AssemblyInfo.vb, AssemblyInfo.fs

When dotnet is available, project files are evaluated with dotnet msbuild preprocessing first so imported props and centrally managed version properties are respected. If that evaluation path is unavailable or does not yield a version, dockship falls back to scanning the discovered files directly.

If no config file exists, dockship uses "auto" by default.

Version option reference

| Setting | Type | Default | Description | | --- | --- | --- | --- | | version.provider | string | auto | Version provider name. Use auto, nodejs, dotnet, gitversion, nbgv, env, or a custom provider name | | version.<provider>.providerPackage | string | empty | npm package name or path to load for custom providers. Relative paths (starting with ./ or ../) are resolved from the client repo root. |

Node.js provider options

| Setting | Type | Default | Env override | Description | | --- | --- | --- | --- | --- | | version.nodejs.packageJsonPath | string | package.json | PACKAGE_JSON_PATH | Path to the package file used for version resolution | | version.nodejs.mode | string | fixed | NODEJS_VERSION_MODE | fixed uses package.json version as-is; git-height appends commit count |

Node.js (npm)

{
  "version": {
    "provider": "auto",
    "nodejs": {
      "packageJsonPath": "package.json",
      "mode": "fixed"  // or "git-height"
    }
  }
}

.NET provider options

| Setting | Type | Default | Env override | Description | | --- | --- | --- | --- | --- | | version.dotnet.mode | string | fixed | DOTNET_VERSION_MODE | fixed uses discovered version as-is; git-height appends commit count | | version.dotnet.mainAssemblyInfoFilePath | string | empty | MAIN_ASSEMBLY_INFO_FILE_PATH | Preferred explicit AssemblyInfo file | | version.dotnet.assemblyInfoFilePaths | string[] | [] | ASSEMBLY_INFO_FILE_PATHS | Additional AssemblyInfo file paths | | version.dotnet.versionInfoFilePaths | string[] | [] | VERSION_INFO_FILE_PATHS | Explicit VersionInfo file paths | | version.dotnet.projectFilePaths | string[] | [] | PROJECT_FILE_PATHS | Explicit project file paths for .csproj, .vbproj, or .fsproj | | version.dotnet.csprojFilePaths | string[] | [] | CSPROJ_FILE_PATHS | Legacy alias for explicit C# project file paths | | version.dotnet.autoDiscover | boolean | true | none | When true, scans the repo for supported MSBuild project files, Directory.Build.props, Directory.Build.targets, VersionInfo.*, and AssemblyInfo.* |

.NET / MSBuild

{
  "version": {
    "provider": "dotnet",
    "dotnet": {
      "mode": "git-height",
      "autoDiscover": true
    }
  }
}

GitVersion provider options

The gitversion provider executes the GitVersion CLI and maps its JSON output into dockship version fields.

When allowGlobalCommand is enabled, dockship will use either gitversion or dotnet-gitversion from the host PATH.

| Setting | Type | Default | Description | | --- | --- | --- | --- | | version.gitversion.configFile | string | GitVersion.yml | File used to detect and run GitVersion in auto mode | | version.gitversion.allowGlobalCommand | boolean | true | Allows the global gitversion command on the host | | version.gitversion.useDocker | boolean | false | Allows Docker fallback execution when host gitversion is unavailable | | version.gitversion.dockerImage | string | gittools/gitversion:6.0.0 | Docker image used when useDocker is true | | version.gitversion.additionalArgs | string[] | [] | Additional CLI args passed to GitVersion |

GitVersion

{
  "version": {
    "provider": "gitversion",
    "gitversion": {
      "configFile": "GitVersion.yml",
      "allowGlobalCommand": true,
      "useDocker": false,
      "dockerImage": "gittools/gitversion:6.0.0",
      "additionalArgs": []
    }
  }
}

Env provider options

The env provider reads a pre-computed version from an environment variable or inline config. It is the simplest way to supply a version when CI tooling (e.g. GitHub Actions, TeamCity, GitLab CI) has already computed one.

| Setting | Type | Default | Description | | --- | --- | --- | --- | | version.env.versionVar | string | DOCKSHIP_VERSION | Name of the environment variable that holds the version string | | version.env.version | string | empty | Inline version override — takes priority over the env var |

Env provider

{
  "version": {
    "provider": "env",
    "env": {
      "versionVar": "DOCKSHIP_VERSION"
    }
  }
}

Or supply a static version at build time:

{
  "version": {
    "provider": "env",
    "env": {
      "version": "1.0.0"
    }
  }
}

In auto mode the env provider activates when DOCKSHIP_VERSION is set (or version.env.version is non-empty). It is evaluated last during detection, and also serves as a recovery fallback when an earlier detected provider fails to resolve a valid version.

NBGV provider options

| Setting | Type | Default | Description | | --- | --- | --- | --- | | version.nbgv.useDocker | boolean | true | Run nbgv inside a Docker container — no .NET SDK required on the host | | version.nbgv.dockerImage | string | mcr.microsoft.com/dotnet/sdk:latest | Docker image used when useDocker is true | | version.nbgv.versionJsonFileName | string | version.json | File used to detect and run NBGV | | version.nbgv.dotnetToolManifestRelativePath | string | .config/dotnet-tools.json | Tool manifest used when restoring local dotnet tools | | version.nbgv.allowToolRestore | boolean | true | Allows dotnet tool restore before retrying (host execution only) | | version.nbgv.allowGlobalCommand | boolean | true | Allows the global nbgv command as a fallback (host execution only) | | version.nbgv.requireVersionJson | boolean | true | When true, version.json must exist for the repo to be considered an NBGV repo |

NBGV

{
  "version": {
    "provider": "nbgv",
    "nbgv": {
      "useDocker": true,
      "dockerImage": "mcr.microsoft.com/dotnet/sdk:latest",
      "versionJsonFileName": "version.json",
      "allowToolRestore": true,
      "allowGlobalCommand": true
    }
  }
}

Git-Height Versioning

Appends Git commit count to semantic versions for unique build identifiers:

# With mode: "git-height"
# package.json: "version": "0.1.0"
# Computed as: 0.1.0.<git-height>
# Output: 0.1.0.78

Commands

| Command | Description | | ------- | ----------- | | dock build | Build Docker image with computed version | | dock ship | Push built image to registry | | dock push | Alias for dock ship | | dock all | Build and ship | | dock version | Show resolved version (JSON) | | dock tags | Show generated image tags | | dock help | Show available commands |

Provider Catalog

Built-in Providers

| Name | Source | Auto-detected when | | --- | --- | --- | | nodejs | package.json | package.json exists at repo root | | dotnet | .csproj / .vbproj / .fsproj / Directory.Build.props / AssemblyInfo.* / VersionInfo.* | Any of those files are found | | gitversion | GitVersion CLI | GitVersion.yml exists at repo root | | nbgv | Nerdbank.GitVersioning | version.json exists at repo root | | env | DOCKSHIP_VERSION env var or inline config | DOCKSHIP_VERSION is set (last in chain), or a previously selected auto provider fails and env version input is available |

External Providers

A third-party provider can be published as an npm package named @agile-north/docker-ci-provider-<name>. Install it and set version.provider to <name> — no providerPackage config needed:

npm install -D @agile-north/dockship-provider-python
{
  "version": {
    "provider": "python",
    "python": {
      "mode": "git-height"
    }
  }
}

Writing Your Own Provider

A provider is a CommonJS module that exports a single resolveVersion(context) function. You can ship it as an npm package or drop a file directly in your repo.

Provider contract

// my-version-provider.cjs
"use strict";

module.exports = {
  resolveVersion(context) {
    // context fields:
    //   cwd           {string}  – working directory where dock was invoked
    //   repoRoot      {string}  – absolute path to the repo root (.git parent)
    //   buildConfig   {object}  – full merged dockship.json config
    //   providerName  {string}  – resolved name of this provider ("myprovider")
    //   providerConfig {object} – buildConfig.version.<providerName>
    //   env           {object}  – process.env (or injected env in tests)

    const version = context.env.MY_VERSION || "1.0.0";
    const [major, minor, patch = "0"] = version.split(".");

    return {
      source: "myprovider",         // required – identifies the provider
      version,                      // required – full version string (semver)
      full: `${major}.${minor}.${patch}`, // required – numeric part only
      major,                        // required
      minor,                        // required
      build: patch,
      suffix: "",                   // prerelease label incl. leading "-"
      semVer2: version,
      assemblyVersion: "",
      informationalVersion: "",
      nuGetPackageVersion: ""
    };
  }
};

All five required fields (source, version, full, major, minor) must be non-empty strings or dockship will throw. Use normalizeVersionInfo from @agile-north/dockship/lib/version/model.cjs to fill in defaults automatically:

"use strict";
const { normalizeVersionInfo } = require("@agile-north/dockship/lib/version/model.cjs");

module.exports = {
  resolveVersion(context) {
    const version = context.providerConfig.version || context.env.MY_VERSION;
    if (!version) throw new Error("MY_VERSION is not set");

    return normalizeVersionInfo({ source: "myprovider", version });
  }
};

normalizeVersionInfo parses major, minor, build, suffix, and semVer2 from version so you only need to supply what differs.

Provider config is passed through

Any keys you add under version.<providerName> in dockship.json are forwarded as context.providerConfig with no validation — use them for options specific to your provider:

{
  "version": {
    "provider": "myprovider",
    "myprovider": {
      "providerPackage": "./.dockship/my-version-provider.cjs",
      "versionFile": "version.txt"
    }
  }
}

Provider resolution order

When loadProvider runs for a given name it tries in order:

  1. Bundledlib/version/providers/<name>/index.cjs inside dockship (built-ins only)
  2. Scoped npm@agile-north/docker-ci-provider-<name> (installed in the consumer repo)
  3. providerPackage – value of version.<name>.providerPackage:
    • Relative path (./… or ../…) → resolved from the client repo root
    • npm package name → resolved from node_modules as normal

Local file in the client repo

Drop the provider file anywhere in your repo — .dockship/ is a natural home — and reference it with a relative path:

your-repo/
├── .dockship/
│   ├── dockship.json
│   └── my-version-provider.cjs   ← provider lives here
{
  "version": {
    "provider": "myprovider",
    "myprovider": {
      "providerPackage": "./.dockship/my-version-provider.cjs"
    }
  }
}

Publishing a provider package

Name the package @agile-north/docker-ci-provider-<name> so consumers can reference it by short name without providerPackage. The package entry point must export resolveVersion(context) as shown above.

Private npm Setup

GitHub Packages

Add .npmrc to your client repo:

@agile-north:registry=https://npm.pkg.github.com
always-auth=true
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}

Local Development Token

Set user-level env var (Windows PowerShell):

[Environment]::SetEnvironmentVariable("NPM_TOKEN","<your-token>","User")

Then install:

npm i -D @agile-north/dockship

CI/CD Integration

TeamCity

steps {
    script {
        name = "Docker Build & Ship"
        scriptContent = """
            #!/usr/bin/env bash
            set -euo pipefail

            docker run --rm \
              -v "$PWD:/workspace" \
              -w /workspace \
              -v /var/run/docker.sock:/var/run/docker.sock \
              -e DOCKER_TARGET_REGISTRY \
              -e DOCKER_TARGET_REPOSITORY \
              -e DOCKER_PUSH_ENABLED \
              -e DOCKER_PUSH_BRANCHES \
              node:18-alpine \
              npx dock all
        """.trimIndent()
    }
}

GitHub Actions

- name: Docker Build & Ship
  run: npx dock all
  env:
    DOCKER_TARGET_REGISTRY: ${{ secrets.REGISTRY }}
    DOCKER_TARGET_REPOSITORY: my-org/my-app
    DOCKER_PUSH_ENABLED: true
    DOCKER_PUSH_BRANCHES: main,develop
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Examples

Zero-Config Node.js Repo

If your repo has a package.json, you can often start with env only and no .dockship/dockship.json:

DOCKER_TARGET_REGISTRY=registry.example.com \
DOCKER_TARGET_REPOSITORY=my-org/my-app \
npx dock build

Branch-Restricted Pushes

{
  "docker": {
    "push": {
      "enabled": true,
      "branches": ["main", "develop", "release/*"]
    }
  }
}

CI Build Without Retaining Local Images

Useful on self-hosted runners or long-lived agents that should build for validation but not accumulate Docker images locally:

{
  "docker": {
    "target": {
      "registry": "ghcr.io",
      "repository": "my-org/my-app"
    },
    "push": {
      "enabled": false
    },
    "cleanup": {
      "local": true
    }
  }
}

Or configure it by env var in CI:

DOCKER_PUSH_ENABLED=false
DOCKER_CLEANUP_LOCAL=auto
npx dock build

DOCKER_CLEANUP_LOCAL accepts auto, true, or false.

Monorepo Services

{
  "docker": {
    "file": "services/api/Dockerfile",
    "context": "services/api",
    "target": {
      "repository": "my-org/api"
    }
  },
  "version": {
    "provider": "nodejs",
    "nodejs": {
      "packageJsonPath": "services/api/package.json"
    }
  }
}

Full-Stack (Node.js Frontend + .NET Backend)

Use dockship once with multi-stage Dockerfile:

FROM node:18 AS frontend
WORKDIR /app
COPY ClientApp .
RUN npm ci && npm run build

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS backend
WORKDIR /bff
COPY . .
RUN dotnet publish -o /publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY --from=frontend /app/dist /app/dist
COPY --from=backend /publish /app
ENTRYPOINT ["dotnet", "Portal.dll"]

Version from Node.js:

{
  "docker": {
    "target": {
      "repository": "my-org/portal"
    }
  },
  "version": {
    "provider": "nodejs",
    "nodejs": {
      "packageJsonPath": "ClientApp/package.json",
      "mode": "git-height"
    }
  }
}

.NET Services

{
  "version": {
    "provider": "dotnet",
    "dotnet": {
      "mode": "git-height",
      "autoDiscover": true
    }
  }
}

NBGV Repo

{
  "version": {
    "provider": "nbgv",
    "nbgv": {
      "useDocker": true,
      "dockerImage": "mcr.microsoft.com/dotnet/sdk:latest",
      "versionJsonFileName": "version.json",
      "allowToolRestore": true,
      "allowGlobalCommand": true
    }
  }
}

Environment Variables

Runtime

  • DOCKER_TARGET_REGISTRY – override docker.target.registry
  • DOCKER_TARGET_REPOSITORY – override docker.target.repository
  • DOCKERFILE_PATH – override docker.file
  • DOCKER_CONTEXT – override docker.context
  • DOCKER_PUSH_ENABLED – "true"/"false", override docker.push.enabled
  • DOCKER_PUSH_BRANCHES – comma/semicolon/newline separated branch patterns, supports *
  • DOCKER_TAG_LATEST – "true"/"false", override docker.tags.latest
  • DOCKER_CLEANUP_LOCALauto/true/false, remove locally built tags after dock build and dock all
  • DOCKER_RUNNERbuild/buildx/auto, override docker.runner
  • DOCKER_BUILD_TARGET – override docker.buildTarget (--target)
  • DOCKER_BUILD_OUTPUT – override docker.buildOutput (--output)
  • DOCKER_STAGES – JSON object to override docker.stages for dynamic stage pipelines
  • DOCKER_PLATFORM – override docker.platform
  • DOCKER_BUILD_ARGS – build args as JSON ({"ENV":"prod"}) or semicolon-delimited KEY=value;KEY2=value2; overrides docker.buildArgs
  • DOCKER_BUILD_ARGS_FILE – path to JSON file containing build args object; overrides docker.buildArgs
  • DOCKER_SECRETS – JSON object mapping secret ids to { "env": "ENV_NAME" } or { "file": "./path" }; overrides docker.secrets
  • DOCKER_SECRET_<ID> – direct env secret override for a secret id (DOCKER_SECRET_NPM_TOKEN=... becomes --secret id=npm_token,env=DOCKER_SECRET_NPM_TOKEN)
  • NPM_TOKEN – for private npm (if using @agile-north providers)

Provider-Specific

  • NODEJS_VERSION_MODE – override version mode (fixed/git-height)
  • PACKAGE_JSON_PATH – override package.json location
  • DOTNET_VERSION_MODE – override .NET version mode (fixed/git-height)
  • MAIN_ASSEMBLY_INFO_FILE_PATH – explicit main AssemblyInfo file path
  • ASSEMBLY_INFO_FILE_PATHS – comma/semicolon separated AssemblyInfo file paths
  • VERSION_INFO_FILE_PATHS – comma/semicolon separated VersionInfo file paths
  • CSPROJ_FILE_PATHS – comma/semicolon separated .csproj file paths
  • DOCKSHIP_VERSION – explicit version string consumed by the env provider (also triggers env auto-detection when set)

Troubleshooting

"Provider not found"

  • Check version.provider in dockship.json matches the installed provider name
  • For @agile-north scoped packages: ensure it is installed (npm ls @agile-north/docker-ci-provider-<name>)
  • For providerPackage relative paths: verify the path is relative to your repo root (e.g. ./.dockship/my-provider.cjs)
  • For providerPackage npm packages: verify the package is in node_modules

"Could not auto-detect version provider"

  • Add .dockship/dockship.json with version.provider
  • Or add a supported version source file such as version.json, package.json, or a .csproj
  • Or set the DOCKSHIP_VERSION env var to supply a version directly (activates the env provider as a last resort)

"No version found"

  • Verify version source file exists (package.json, .csproj, version.json)
  • Check version.provider config and associated path settings are correct

Docker login fails in CI

  • Ensure DOCKER_TARGET_REGISTRY is set in env
  • For private registries, either run docker login before build or provide DOCKER_LOGIN_USERNAME and DOCKER_LOGIN_PASSWORD

Git height not incrementing

  • Requires .git directory (not in Docker container by default)
  • Mount repo root and git metadata, for example: -v "$PWD:/workspace" -v "$PWD/.git:/workspace/.git"

License

MIT


Made by NRTHwww.nrth.com