Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ jobs:
pnpm lint:markdown
pnpm lint:types
pnpm lint:schema
pnpm lint:tools
pnpm lint:versions

- name: shellcheck
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ node_modules/
/bin/
/tmp/
/dist/
/packages/*/dist/
/coverage
/html/

Expand Down
2 changes: 2 additions & 0 deletions docs/new-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Files to change:
```

1. [`.github/renovate.json`](../.github/renovate.json) - add the tool name to **both** `matchDepNames` lists (the "Don't separate minor and patch updates in tests" rule and the "Automerge test selected minor updates in tests" rule).
1. [`packages/base`](../packages/base/) - run `pnpm tools` and commit the regenerated files, so the new tool shows up in the published tool list. `pnpm lint:tools` fails if you forget.

Note the arm64 test files use one image stage per tool, terminating in a `COPY --from=test-<tool> /.dummy /.dummy` line in the final stage - add both halves.

Expand Down Expand Up @@ -221,6 +222,7 @@ Resolvers are bound with `container.bind(TOOL_VERSION_RESOLVER).to(...)` in the
```

1. [`.github/renovate.json`](../.github/renovate.json) - add the tool name to both `matchDepNames` lists.
1. [`packages/base`](../packages/base/) - run `pnpm tools` and commit the regenerated files, so the new tool shows up in the published tool list. `pnpm lint:tools` fails if you forget.

> [!NOTE]
> You'll notice that we lean on integration tests with Docker instead of unit tests.
Expand Down
6 changes: 6 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default tseslint.config(
{
ignores: [
'dist/',
'packages/*/dist/',
'tmp/',
'bin/',
'coverage/',
Expand Down Expand Up @@ -228,6 +229,11 @@ export default tseslint.config(
'max-classes-per-file': 0,
'class-methods-use-this': 0,
'no-console': 0,
// type tests only assert with `expectTypeOf`
'vitest/expect-expect': [
'error',
{ assertFunctionNames: ['expect', 'expectTypeOf'] },
],
},
},
{
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
"scripts": {
"bats": "node tools/bats.js --timing --verbose-run",
"build": "run-s 'build:*'",
"build:base": "tsc -p packages/base",
Comment thread
viceice marked this conversation as resolved.
"build:cli": "node tools/build.js",
"eslint": "eslint --cache .",
"eslint-fix": "eslint --cache --fix .",
"lint": "run-s prettier eslint lint:types lint:markdown lint:schema lint:versions",
"lint": "run-s prettier eslint lint:types lint:markdown lint:schema lint:tools lint:versions",
"lint:markdown": "markdownlint-cli2 '**/*.md'",
"lint:schema": "pnpm schema && git diff --exit-code docs/list-tools.schema.json",
"lint:tools": "pnpm tools && git diff --exit-code packages/base",
"lint:types": "run-p 'lint:types:*'",
"lint:types:default": "tsc",
"lint:types:dist": "tsc -p tsconfig.dist.json",
Expand All @@ -34,7 +36,8 @@
"test": "run-s 'test:*'",
"test:bats": "node tools/bats.js --timing --verbose-run test/bash/ test/bash/v2",
"test:docker": "node tools/test.js",
"test:vitest": "vitest run --coverage"
"test:vitest": "vitest run --coverage",
"tools": "tsx tools/tools.ts"
},
"dependencies": {
"@nodable/compact-builder": "2.0.0",
Expand Down
50 changes: 50 additions & 0 deletions packages/base/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# `@containerbase/base`

Metadata about the tools that [Containerbase](https://github.com/containerbase/base) supports.

The data is generated from the Containerbase sources and released with the same version as Containerbase itself.
This allows a consumer of this library to determine which tools can be used with a given version of Containerbase (via `/usr/local/containerbase/version`): if the image has the same major version as this package and a version greater than or equal to the package version, every tool listed here can be installed in that image.
A new major version may remove tools, so compare against the package of the image's major version.

## Usage

```ts
import { toolNames, tools, type ToolName } from '@containerbase/base';

tools.composer; // { parent: 'php' }
tools.kas; // { type: 'pip', parent: 'python' }

function install(tool: ToolName): void {
// `ToolName` is a union of all supported names
}
```

The raw data and its json schema are also exported, for consumers which don't use TypeScript:

```ts
import tools from '@containerbase/base/tools.json' with { type: 'json' };
```

The zod schemas live behind a separate entry point, so the default export stays dependency free:

```ts
import { SupportedTools, ToolName } from '@containerbase/base/zod';
```

## Contents

Each entry may carry the following metadata:

| Field | Description |
| ------------ | --------------------------------------------------------------------------------------------------------------- |
| `type` | the installer used for the tool, one of `gem`, `npm` or `pip` |
| `parent` | the tool it depends on, eg. `composer` depends on `php`, which requires `php` to be installed before `composer` |
| `deprecated` | the tool should not be used any more |
| `root` | the tool can only be installed as root, so at image build time |

Only the names `install-tool` accepts are listed.
Packages installed with an arbitrary name via `install-gem`, `install-npm` or `install-pip` are not, as that list is unbounded.

## Development

The files in `data/` and `src/data.ts` are generated, run `pnpm tools` in the repository root to update them.
81 changes: 81 additions & 0 deletions packages/base/data/tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"tools": {
"android-sdk-cmdline-tools": { "parent": "java" },
"apko": {},
"apm": {},
"bazelisk": {},
"bower": { "type": "npm", "parent": "node", "deprecated": true },
"buf": {},
"buildx": { "parent": "docker" },
"bun": {},
"bundler": { "type": "gem", "parent": "ruby" },
"cabal": {},
"checkov": { "type": "pip", "parent": "python" },
"cocoapods": { "parent": "ruby" },
"composer": { "parent": "php" },
"conan": { "parent": "python" },
"copier": { "type": "pip", "parent": "python" },
"corepack": { "type": "npm", "parent": "node" },
"dart": {},
"deno": {},
"devbox": {},
"docker": {},
"docker-compose": { "parent": "docker" },
"dotnet": {},
"elixir": { "parent": "erlang" },
"erlang": {},
"flutter": {},
"flux": {},
"gh": {},
"ghc": {},
"git": { "root": true },
"git-lfs": { "parent": "git" },
"gleam": {},
"golang": {},
"gradle": { "parent": "java" },
"hashin": { "type": "pip", "parent": "python" },
"helm": {},
"helmfile": {},
"java": {},
"java-jdk": {},
"java-jre": {},
"jb": {},
"kas": { "type": "pip", "parent": "python" },
"kubectl": {},
"kustomize": {},
"lerna": { "type": "npm", "parent": "node", "deprecated": true },
"maven": { "parent": "java" },
"mise": {},
"mono": {},
"nix": {},
"node": {},
"npm": { "type": "npm", "parent": "node" },
"nuget": { "parent": "mono" },
"paket": { "parent": "dotnet" },
"pdm": { "type": "pip", "parent": "python" },
"php": {},
"pip-tools": { "type": "pip", "parent": "python" },
"pipenv": { "type": "pip", "parent": "python" },
"pixi": {},
"pnpm": { "type": "npm", "parent": "node" },
"poetry": { "type": "pip", "parent": "python" },
"powershell": {},
"protoc": {},
"python": {},
"renovate": { "parent": "node" },
"ruby": {},
"rust": {},
"sbt": { "parent": "java" },
"scala": { "parent": "java" },
"skopeo": {},
"sops": {},
"swift": {},
"terraform": {},
"tofu": {},
"uv": { "type": "pip", "parent": "python" },
"vendir": {},
"wally": {},
"yarn": { "parent": "node" },
"yarn-slim": { "parent": "node" }
}
}
38 changes: 38 additions & 0 deletions packages/base/data/tools.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"tools": {
"type": "object",
"propertyNames": { "type": "string" },
"additionalProperties": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["gem", "npm", "pip"],
"description": "the installer used for this tool, only set for dynamically installed tools"
},
"parent": {
"type": "string",
"description": "the tool this tool depends on, eg. composer depends on php"
},
"deprecated": {
"type": "boolean",
"const": true,
"description": "deprecated tools should not be used any more"
},
"root": {
"type": "boolean",
"const": true,
"description": "the tool can only be installed as root, so only at image build time"
}
},
"additionalProperties": false
},
"description": "all supported tools, keyed by tool name"
}
},
"required": ["tools"],
"additionalProperties": false
}
48 changes: 48 additions & 0 deletions packages/base/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@containerbase/base",
"version": "0.0.0-semantic-release",
"description": "Metadata about the tools supported by containerbase",
"keywords": [
"containerbase",
"tools"
],
"homepage": "https://github.com/containerbase/base#readme",
"repository": {
"type": "git",
"url": "https://github.com/containerbase/base.git",
"directory": "packages/base"
},
"license": "MIT",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./zod": {
"types": "./dist/zod.d.ts",
"default": "./dist/zod.js"
},
"./tools.json": "./data/tools.json",
"./tools.schema.json": "./data/tools.schema.json"
},
"files": [
"data",
"dist"
],
"devDependencies": {
"zod": "4.6.5"
},
"peerDependencies": {
"zod": "^4.0.0"
},
"peerDependenciesMeta": {
"zod": {
"optional": true
}
},
"publishConfig": {
"access": "public",
"provenance": true
}
}
85 changes: 85 additions & 0 deletions packages/base/src/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// generated by `pnpm tools`, do not edit
import type { ToolMetadata } from './types.ts';

/**
* All tools `install-tool` supports, sorted by name.
*/
export const tools = {
'android-sdk-cmdline-tools': { parent: 'java' },
apko: {},
apm: {},
bazelisk: {},
bower: { type: 'npm', parent: 'node', deprecated: true },
buf: {},
buildx: { parent: 'docker' },
bun: {},
bundler: { type: 'gem', parent: 'ruby' },
cabal: {},
checkov: { type: 'pip', parent: 'python' },
cocoapods: { parent: 'ruby' },
composer: { parent: 'php' },
conan: { parent: 'python' },
copier: { type: 'pip', parent: 'python' },
corepack: { type: 'npm', parent: 'node' },
dart: {},
deno: {},
devbox: {},
docker: {},
'docker-compose': { parent: 'docker' },
dotnet: {},
elixir: { parent: 'erlang' },
erlang: {},
flutter: {},
flux: {},
gh: {},
ghc: {},
git: { root: true },
Comment thread
viceice marked this conversation as resolved.
'git-lfs': { parent: 'git' },
gleam: {},
golang: {},
gradle: { parent: 'java' },
hashin: { type: 'pip', parent: 'python' },
helm: {},
helmfile: {},
java: {},
'java-jdk': {},
'java-jre': {},
jb: {},
kas: { type: 'pip', parent: 'python' },
kubectl: {},
kustomize: {},
lerna: { type: 'npm', parent: 'node', deprecated: true },
maven: { parent: 'java' },
mise: {},
mono: {},
nix: {},
node: {},
npm: { type: 'npm', parent: 'node' },
nuget: { parent: 'mono' },
paket: { parent: 'dotnet' },
pdm: { type: 'pip', parent: 'python' },
php: {},
'pip-tools': { type: 'pip', parent: 'python' },
pipenv: { type: 'pip', parent: 'python' },
pixi: {},
pnpm: { type: 'npm', parent: 'node' },
poetry: { type: 'pip', parent: 'python' },
powershell: {},
protoc: {},
python: {},
renovate: { parent: 'node' },
ruby: {},
rust: {},
sbt: { parent: 'java' },
scala: { parent: 'java' },
skopeo: {},
sops: {},
swift: {},
terraform: {},
tofu: {},
uv: { type: 'pip', parent: 'python' },
vendir: {},
wally: {},
yarn: { parent: 'node' },
'yarn-slim': { parent: 'node' },
} as const satisfies Record<string, ToolMetadata>;
14 changes: 14 additions & 0 deletions packages/base/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { tools } from './data.ts';

export { tools } from './data.ts';
export type { InstallToolType, ToolMetadata } from './types.ts';

/**
* A tool name `install-tool` accepts.
*/
export type ToolName = keyof typeof tools;

/**
* All supported tool names, sorted alphabetically.
*/
export const toolNames = Object.keys(tools) as ToolName[];
Loading
Loading