Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7131c16f80 | |||
| 02688861f4 | |||
| 3a8b301b3e | |||
| c09bef33c3 | |||
| 32eb0d1d77 | |||
| 7cac628975 | |||
| c279dbd55e | |||
| 7b7064864e | |||
| 36f06cef09 | |||
| b0f87deb4b | |||
| 9805324746 | |||
| 808066d8c3 | |||
| 6922d19454 | |||
| e1492f8ec4 | |||
| e9a12f1c17 | |||
| 6995010a2c |
72
changelog.md
72
changelog.md
@@ -1,10 +1,80 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-02-06 - 1.7.0 - feat(cli)
|
||||||
|
add CLI version display using commitinfo
|
||||||
|
|
||||||
|
- Imported commitinfo from './00_commitinfo_data.js' and called tsdockerCli.addVersion(commitinfo.version) to surface package/commit version in the Smartcli instance
|
||||||
|
- Change made in ts/tsdocker.cli.ts — small user-facing CLI enhancement; no breaking changes
|
||||||
|
|
||||||
|
## 2026-02-06 - 1.6.0 - feat(docker)
|
||||||
|
add support for no-cache builds and tag built images for local dependency resolution
|
||||||
|
|
||||||
|
- Introduce IBuildCommandOptions.noCache to control --no-cache behavior
|
||||||
|
- Propagate noCache from CLI (via cache flag) through TsDockerManager to Dockerfile.build
|
||||||
|
- Append --no-cache to docker build/buildx commands when noCache is true
|
||||||
|
- After building an image, tag it with full base image references used by dependent Dockerfiles so their FROM lines resolve to the locally-built image
|
||||||
|
- Log tagging actions and execute docker tag via smartshellInstance
|
||||||
|
|
||||||
|
## 2026-02-06 - 1.5.0 - feat(build)
|
||||||
|
add support for selective builds, platform override and build timeout
|
||||||
|
|
||||||
|
- Introduce IBuildCommandOptions with patterns, platform and timeout to control build behavior
|
||||||
|
- Allow manager.build() to accept options and build only matching Dockerfiles (including dependencies) preserving topological order
|
||||||
|
- Add CLI parsing for build/push to accept positional Dockerfile patterns and --platform/--timeout flags
|
||||||
|
- Support single-platform override via docker buildx and multi-platform buildx detection
|
||||||
|
- Implement streaming exec with timeout to kill long-running builds and surface timeout errors
|
||||||
|
|
||||||
|
## 2026-02-04 - 1.4.3 - fix(dockerfile)
|
||||||
|
fix matching of base images to local Dockerfiles by stripping registry prefixes when comparing image references
|
||||||
|
|
||||||
|
- Added Dockerfile.extractRepoVersion(imageRef) to normalize image references by removing registry prefixes (detects registries containing '.' or ':' or 'localhost').
|
||||||
|
- Use extractRepoVersion when checking tagToDockerfile and when mapping local base dockerfiles to ensure comparisons use repo:tag keys rather than full registry-prefixed references.
|
||||||
|
- Prevents mismatches when baseImage includes a registry (e.g. "host.today/repo:version") so it correctly matches a local cleanTag like "repo:version".
|
||||||
|
|
||||||
|
## 2026-01-21 - 1.4.2 - fix(classes.dockerfile)
|
||||||
|
use a single top-level fs import instead of requiring fs inside methods
|
||||||
|
|
||||||
|
- Added top-level import: import * as fs from 'fs' in ts/classes.dockerfile.ts
|
||||||
|
- Removed inline require('fs') calls and replaced with the imported fs in constructor and test() to keep imports consistent
|
||||||
|
- No behavioral change expected; this is a cleanup/refactor to standardize module usage
|
||||||
|
|
||||||
|
## 2026-01-20 - 1.4.1 - fix(docs)
|
||||||
|
update README: expand usage, installation, quick start, features, troubleshooting and migration notes
|
||||||
|
|
||||||
|
- Expanded README content: new Quick Start, Installation examples, and detailed Features section (containerized testing, smart Docker builds, multi-registry push, multi-architecture support, zero-config start)
|
||||||
|
- Added troubleshooting and performance tips including registry login guidance and circular dependency advice
|
||||||
|
- Updated migration notes from legacy npmdocker to @git.zone/tsdocker (command and config key changes, ESM guidance)
|
||||||
|
- Documentation-only change — no source code modified
|
||||||
|
|
||||||
|
## 2026-01-20 - 1.4.0 - feat(tsdocker)
|
||||||
|
add multi-registry and multi-arch Docker build/push/pull manager, registry storage, Dockerfile handling, and new CLI commands
|
||||||
|
|
||||||
|
- Introduce TsDockerManager orchestrator to discover, sort, build, test, push and pull Dockerfiles
|
||||||
|
- Add Dockerfile class with dependency-aware build order, buildx support, push/pull and test flows (new large module)
|
||||||
|
- Add DockerRegistry and RegistryStorage classes to manage registry credentials, login/logout and environment loading
|
||||||
|
- Add CLI commands: build, push, pull, test, login, list (and integrate TsDockerManager into CLI)
|
||||||
|
- Extend configuration (ITsDockerConfig) with registries, registryRepoMap, buildArgEnvMap, platforms, push and testDir; re-export as IConfig for backwards compatibility
|
||||||
|
- Add @push.rocks/lik to dependencies and import it in tsdocker.plugins
|
||||||
|
- Remove legacy speedtest command and related package.json script
|
||||||
|
- Update README and readme.hints with new features, configuration examples and command list
|
||||||
|
|
||||||
|
## 2026-01-19 - 1.3.0 - feat(packaging)
|
||||||
|
Rename package scope to @git.zone and migrate to ESM; rename CLI/config keys, update entrypoints and imports, bump Node requirement to 18, and adjust scripts/dependencies
|
||||||
|
|
||||||
|
- Package renamed to @git.zone/tsdocker (scope change) — consumers must update package reference.
|
||||||
|
- Configuration key changed from 'npmdocker' to '@git.zone/tsdocker' in npmextra.json; update project config accordingly.
|
||||||
|
- CLI command renamed from 'npmdocker' to 'tsdocker' and entrypoint/entrypoint binary references updated.
|
||||||
|
- Project migrated to ESM: imports now use .js extensions, package main/typings point to dist_ts, and ts source uses ESM patterns — Node >=18 required.
|
||||||
|
- Build/test scripts changed to use tsx and updated test task names; CI/workflow and npmextra release registries updated.
|
||||||
|
- Dependencies/devDependencies bumped; smartfs, smartcli and tsbuild versions updated.
|
||||||
|
- Docker build command now uses '--load' and default base images/installation behavior adjusted (global install of tsdocker in image).
|
||||||
|
|
||||||
## 2025-12-13 - 1.2.43 - fix(packaging)
|
## 2025-12-13 - 1.2.43 - fix(packaging)
|
||||||
|
|
||||||
Rename package scope to @git.zone and migrate deps/CI; pin pnpm and enable ESM packaging
|
Rename package scope to @git.zone and migrate deps/CI; pin pnpm and enable ESM packaging
|
||||||
|
|
||||||
- Rename npm package scope from @gitzone/tsdocker to @git.zone/tsdocker (package.json, commitinfo, README, npmextra)
|
- Rename npm package scope from @gitzone/tsdocker to @git.zone/tsdocker (package.json, commitinfo, README, npmextra)
|
||||||
- Migrate devDependencies from @gitzone/* to @git.zone/* and ensure runtime packages use @push.rocks/* where applicable
|
- Migrate devDependencies from @gitzone/_ to @git.zone/_ and ensure runtime packages use @push.rocks/\* where applicable
|
||||||
- Replace smartfile usage with smartfs and update code to use async smartfs.file(...).write()/delete() patterns
|
- Replace smartfile usage with smartfs and update code to use async smartfs.file(...).write()/delete() patterns
|
||||||
- Add packageManager pin for pnpm, set type: "module", add files array and pnpm.overrides in package.json
|
- Add packageManager pin for pnpm, set type: "module", add files array and pnpm.overrides in package.json
|
||||||
- Add tsconfig.json with NodeNext/ES2022 settings and other ESM-related adjustments
|
- Add tsconfig.json with NodeNext/ES2022 settings and other ESM-related adjustments
|
||||||
|
|||||||
2
cli.js
Normal file → Executable file
2
cli.js
Normal file → Executable file
@@ -1,3 +1,3 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
process.env.CLI_CALL = 'true';
|
process.env.CLI_CALL = 'true';
|
||||||
require('./dist/index');
|
import('./dist_ts/index.js');
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
process.env.CLI_CALL = 'true';
|
|
||||||
require('@git.zone/tsrun');
|
|
||||||
require('./ts/index');
|
|
||||||
@@ -3,11 +3,7 @@
|
|||||||
"mode": "default",
|
"mode": "default",
|
||||||
"cli": true
|
"cli": true
|
||||||
},
|
},
|
||||||
"npmci": {
|
"@git.zone/cli": {
|
||||||
"npmGlobalTools": [],
|
|
||||||
"npmAccessLevel": "public"
|
|
||||||
},
|
|
||||||
"gitzone": {
|
|
||||||
"projectType": "npm",
|
"projectType": "npm",
|
||||||
"module": {
|
"module": {
|
||||||
"githost": "gitlab.com",
|
"githost": "gitlab.com",
|
||||||
@@ -16,6 +12,16 @@
|
|||||||
"description": "develop npm modules cross platform with docker",
|
"description": "develop npm modules cross platform with docker",
|
||||||
"npmPackagename": "@git.zone/tsdocker",
|
"npmPackagename": "@git.zone/tsdocker",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"release": {
|
||||||
|
"accessLevel": "public",
|
||||||
|
"registries": [
|
||||||
|
"https://verdaccio.lossless.digital",
|
||||||
|
"https://registry.npmjs.org"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"@ship.zone/szci": {
|
||||||
|
"npmGlobalTools": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
29
package.json
29
package.json
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tsdocker",
|
"name": "@git.zone/tsdocker",
|
||||||
"version": "1.2.43",
|
"version": "1.7.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "develop npm modules cross platform with docker",
|
"description": "develop npm modules cross platform with docker",
|
||||||
"main": "dist/index.js",
|
"main": "dist_ts/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsdocker": "cli.js"
|
"tsdocker": "cli.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "(npm run clean && npm run setupCheck && npm run testStandard && npm run testSpeed)",
|
"test": "(npm run build)",
|
||||||
"build": "(tsbuild)",
|
"build": "(tsbuild)",
|
||||||
"testStandard": "(cd test/ && node ../cli.ts.js)",
|
"testIntegration": "(npm run clean && npm run setupCheck && npm run testStandard)",
|
||||||
"testSpeed": "(cd test/ && node ../cli.ts.js speedtest)",
|
"testStandard": "(cd test/ && tsx ../ts/index.ts)",
|
||||||
"testClean": "(cd test/ && node ../cli.ts.js clean --all)",
|
"testClean": "(cd test/ && tsx ../ts/index.ts clean --all)",
|
||||||
"testVscode": "(cd test/ && node ../cli.ts.js vscode)",
|
"testVscode": "(cd test/ && tsx ../ts/index.ts vscode)",
|
||||||
"clean": "(rm -rf test/)",
|
"clean": "(rm -rf test/)",
|
||||||
"compile": "(npmts --notest)",
|
"compile": "(npmts --notest)",
|
||||||
"setupCheck": "(git clone https://gitlab.com/sandboxzone/sandbox-npmts.git test/)",
|
"setupCheck": "(git clone https://gitlab.com/sandboxzone/sandbox-npmts.git test/)",
|
||||||
@@ -34,18 +34,19 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/gitzone/tsdocker#readme",
|
"homepage": "https://gitlab.com/gitzone/tsdocker#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^3.1.0",
|
"@git.zone/tsbuild": "^4.1.2",
|
||||||
"@git.zone/tsrun": "^2.0.0",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@git.zone/tstest": "^3.1.3",
|
"@git.zone/tstest": "^3.1.6",
|
||||||
"@types/node": "^22.10.2"
|
"@types/node": "^25.0.9"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@push.rocks/lik": "^6.2.2",
|
||||||
"@push.rocks/npmextra": "^5.3.3",
|
"@push.rocks/npmextra": "^5.3.3",
|
||||||
"@push.rocks/projectinfo": "^5.0.2",
|
"@push.rocks/projectinfo": "^5.0.2",
|
||||||
"@push.rocks/qenv": "^6.1.3",
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
"@push.rocks/smartanalytics": "^2.0.15",
|
"@push.rocks/smartanalytics": "^2.0.15",
|
||||||
"@push.rocks/smartcli": "^4.0.19",
|
"@push.rocks/smartcli": "^4.0.20",
|
||||||
"@push.rocks/smartfs": "^1.1.0",
|
"@push.rocks/smartfs": "^1.3.1",
|
||||||
"@push.rocks/smartlog": "^3.1.10",
|
"@push.rocks/smartlog": "^3.1.10",
|
||||||
"@push.rocks/smartlog-destination-local": "^9.0.2",
|
"@push.rocks/smartlog-destination-local": "^9.0.2",
|
||||||
"@push.rocks/smartlog-source-ora": "^1.0.9",
|
"@push.rocks/smartlog-source-ora": "^1.0.9",
|
||||||
|
|||||||
395
pnpm-lock.yaml
generated
395
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@push.rocks/lik':
|
||||||
|
specifier: ^6.2.2
|
||||||
|
version: 6.2.2
|
||||||
'@push.rocks/npmextra':
|
'@push.rocks/npmextra':
|
||||||
specifier: ^5.3.3
|
specifier: ^5.3.3
|
||||||
version: 5.3.3
|
version: 5.3.3
|
||||||
@@ -21,11 +24,11 @@ importers:
|
|||||||
specifier: ^2.0.15
|
specifier: ^2.0.15
|
||||||
version: 2.0.15
|
version: 2.0.15
|
||||||
'@push.rocks/smartcli':
|
'@push.rocks/smartcli':
|
||||||
specifier: ^4.0.19
|
specifier: ^4.0.20
|
||||||
version: 4.0.19
|
version: 4.0.20
|
||||||
'@push.rocks/smartfs':
|
'@push.rocks/smartfs':
|
||||||
specifier: ^1.1.0
|
specifier: ^1.3.1
|
||||||
version: 1.1.0
|
version: 1.3.1
|
||||||
'@push.rocks/smartlog':
|
'@push.rocks/smartlog':
|
||||||
specifier: ^3.1.10
|
specifier: ^3.1.10
|
||||||
version: 3.1.10
|
version: 3.1.10
|
||||||
@@ -49,17 +52,17 @@ importers:
|
|||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@git.zone/tsbuild':
|
'@git.zone/tsbuild':
|
||||||
specifier: ^3.1.0
|
specifier: ^4.1.2
|
||||||
version: 3.1.0
|
version: 4.1.2
|
||||||
'@git.zone/tsrun':
|
'@git.zone/tsrun':
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.1
|
||||||
version: 2.0.0
|
version: 2.0.1
|
||||||
'@git.zone/tstest':
|
'@git.zone/tstest':
|
||||||
specifier: ^3.1.3
|
specifier: ^3.1.6
|
||||||
version: 3.1.3(socks@2.8.7)(typescript@5.9.3)
|
version: 3.1.6(socks@2.8.7)(typescript@5.9.3)
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.10.2
|
specifier: ^25.0.9
|
||||||
version: 22.19.1
|
version: 25.0.9
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -285,174 +288,330 @@ packages:
|
|||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [aix]
|
os: [aix]
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [aix]
|
||||||
|
|
||||||
'@esbuild/android-arm64@0.25.12':
|
'@esbuild/android-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
|
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
'@esbuild/android-arm@0.25.12':
|
'@esbuild/android-arm@0.25.12':
|
||||||
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
|
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.27.2':
|
||||||
|
resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
'@esbuild/android-x64@0.25.12':
|
'@esbuild/android-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
|
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
'@esbuild/darwin-arm64@0.25.12':
|
'@esbuild/darwin-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
|
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@esbuild/darwin-x64@0.25.12':
|
'@esbuild/darwin-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
|
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@esbuild/freebsd-arm64@0.25.12':
|
'@esbuild/freebsd-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
|
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
'@esbuild/freebsd-x64@0.25.12':
|
'@esbuild/freebsd-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
|
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
'@esbuild/linux-arm64@0.25.12':
|
'@esbuild/linux-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
|
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-arm@0.25.12':
|
'@esbuild/linux-arm@0.25.12':
|
||||||
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
|
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.27.2':
|
||||||
|
resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-ia32@0.25.12':
|
'@esbuild/linux-ia32@0.25.12':
|
||||||
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
|
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.27.2':
|
||||||
|
resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-loong64@0.25.12':
|
'@esbuild/linux-loong64@0.25.12':
|
||||||
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
|
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-mips64el@0.25.12':
|
'@esbuild/linux-mips64el@0.25.12':
|
||||||
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
|
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [mips64el]
|
cpu: [mips64el]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.27.2':
|
||||||
|
resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-ppc64@0.25.12':
|
'@esbuild/linux-ppc64@0.25.12':
|
||||||
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
|
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-riscv64@0.25.12':
|
'@esbuild/linux-riscv64@0.25.12':
|
||||||
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
|
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-s390x@0.25.12':
|
'@esbuild/linux-s390x@0.25.12':
|
||||||
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
|
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.27.2':
|
||||||
|
resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/linux-x64@0.25.12':
|
'@esbuild/linux-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
|
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@esbuild/netbsd-arm64@0.25.12':
|
'@esbuild/netbsd-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
|
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [netbsd]
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
'@esbuild/netbsd-x64@0.25.12':
|
'@esbuild/netbsd-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
|
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [netbsd]
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
'@esbuild/openbsd-arm64@0.25.12':
|
'@esbuild/openbsd-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
|
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [openbsd]
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
'@esbuild/openbsd-x64@0.25.12':
|
'@esbuild/openbsd-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
|
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [openbsd]
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
'@esbuild/openharmony-arm64@0.25.12':
|
'@esbuild/openharmony-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
|
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [openharmony]
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
'@esbuild/sunos-x64@0.25.12':
|
'@esbuild/sunos-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
|
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [sunos]
|
os: [sunos]
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
|
||||||
'@esbuild/win32-arm64@0.25.12':
|
'@esbuild/win32-arm64@0.25.12':
|
||||||
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
|
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.27.2':
|
||||||
|
resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@esbuild/win32-ia32@0.25.12':
|
'@esbuild/win32-ia32@0.25.12':
|
||||||
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
|
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.27.2':
|
||||||
|
resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@esbuild/win32-x64@0.25.12':
|
'@esbuild/win32-x64@0.25.12':
|
||||||
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
|
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@git.zone/tsbuild@3.1.0':
|
'@esbuild/win32-x64@0.27.2':
|
||||||
resolution: {integrity: sha512-j8lMd84pmzWiU6NG3e+pyu0o41oo6mQVfcZv8kDsCrQwZMhoQV9Jp87MlU0i/XI5IZkqDjelG8Kx1QhOmbK+iQ==}
|
resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@git.zone/tsbuild@4.1.2':
|
||||||
|
resolution: {integrity: sha512-S518ulKveO76pS6jrAELrnFaCw5nDAIZD9j6QzVmLYDiZuJmlRwPK3/2E8ugQ+b7ffpkwJ9MT685ooEGDcWQ4Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@git.zone/tsbundle@2.5.2':
|
'@git.zone/tsbundle@2.5.2':
|
||||||
resolution: {integrity: sha512-EYTCfunqoxhxkowREZ+cJnww6eDh9cL18HJbHbSZ+vxzNeyS9x8mT9aqRlWkI7zgpvgDlGIYlyRUlUISXkQO6Q==}
|
resolution: {integrity: sha512-EYTCfunqoxhxkowREZ+cJnww6eDh9cL18HJbHbSZ+vxzNeyS9x8mT9aqRlWkI7zgpvgDlGIYlyRUlUISXkQO6Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@git.zone/tspublish@1.10.3':
|
'@git.zone/tspublish@1.11.0':
|
||||||
resolution: {integrity: sha512-o2/jvNsdLC8SRdH1kQ7JjNOQNu9el0FpJ/QOW3mgiC5C9reuTp18iU4kijsVVLgvw4KZv6Z289SoKPh3HPsS0g==}
|
resolution: {integrity: sha512-dkgaDBTzZJ53lAV72r7OW/W7l/KqpkncFuPojr11JO35OKAbjjDhZbAwPv4oGX9NplyXrhC5VJRPNX/orqNTHA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@git.zone/tsrun@2.0.0':
|
'@git.zone/tsrun@2.0.1':
|
||||||
resolution: {integrity: sha512-yA6zCjL+kn7xfZe6sL/m4K+zYqgkznG/pF6++i/E17iwzpG6dHmW+VZmYldHe86sW4DcLMvqM6CxM+KlgaEpKw==}
|
resolution: {integrity: sha512-NEcnsjvlC1o3Z6SS3VhKCf6Ev+Sh4EAinmggslrIR/ppMrvjDbXNFXoyr3PB+GLeSAR0JRZ1fGvVYjpEzjBdIg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@git.zone/tstest@3.1.3':
|
'@git.zone/tstest@3.1.6':
|
||||||
resolution: {integrity: sha512-t+/cKV21JHK8X7NGAmihs5M/eMm+V+jn4R5rzfwGG97WJFAcP5qE1Os9VYtyZw3tx/NZXA2yA4abo/ELluTuRA==}
|
resolution: {integrity: sha512-xRGc6wO4rJ6mohPCMIBDRH+oNjiIvX6Jeo8v/Y5o5VyKSHFmqol7FCKSBrojMcqgBpESnLHFPJAAOmT9W3JV8Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@happy-dom/global-registrator@15.11.7':
|
'@happy-dom/global-registrator@15.11.7':
|
||||||
@@ -624,9 +783,10 @@ packages:
|
|||||||
|
|
||||||
'@push.rocks/smartchok@1.1.1':
|
'@push.rocks/smartchok@1.1.1':
|
||||||
resolution: {integrity: sha512-WmNigGmn1muBJMANVuJb4F8x3TzgYrnn6YZm6ixTsG+0WFbYevivEwp+J4S7npobLHsR7ynf+Ky8LxRYmsL50A==}
|
resolution: {integrity: sha512-WmNigGmn1muBJMANVuJb4F8x3TzgYrnn6YZm6ixTsG+0WFbYevivEwp+J4S7npobLHsR7ynf+Ky8LxRYmsL50A==}
|
||||||
|
deprecated: This package has been renamed. Please use @push.rocks/smartwatch instead.
|
||||||
|
|
||||||
'@push.rocks/smartcli@4.0.19':
|
'@push.rocks/smartcli@4.0.20':
|
||||||
resolution: {integrity: sha512-s1jZSgDZWi/az26AY4TJ2HPuG1qZzGC5R9fKWaECLmwnSpk6y9JXL5dnJAUohcdu50kdXCWEcRmLfYxOt81vEA==}
|
resolution: {integrity: sha512-gCo4ItvsPj8WoVAJw/6vkuoGA5FtIoACux2ktcCeH0nrFe7/xGR6waJ1aZcYAi7QN4gi52TlsgwuKz7BzXqhmQ==}
|
||||||
|
|
||||||
'@push.rocks/smartclickhouse@2.0.17':
|
'@push.rocks/smartclickhouse@2.0.17':
|
||||||
resolution: {integrity: sha512-IYO8Obor/Ruam2KQ2B/+5uQ+rL0exU5KZoSgOc3jkkrfjn+zZenN2xoV8lVqavAtxZVfG7MfxFrcv6I7I9ZMmA==}
|
resolution: {integrity: sha512-IYO8Obor/Ruam2KQ2B/+5uQ+rL0exU5KZoSgOc3jkkrfjn+zZenN2xoV8lVqavAtxZVfG7MfxFrcv6I7I9ZMmA==}
|
||||||
@@ -670,8 +830,11 @@ packages:
|
|||||||
'@push.rocks/smartfile@11.2.7':
|
'@push.rocks/smartfile@11.2.7':
|
||||||
resolution: {integrity: sha512-8Yp7/sAgPpWJBHohV92ogHWKzRomI5MEbSG6b5W2n18tqwfAmjMed0rQvsvGrSBlnEWCKgoOrYIIZbLO61+J0Q==}
|
resolution: {integrity: sha512-8Yp7/sAgPpWJBHohV92ogHWKzRomI5MEbSG6b5W2n18tqwfAmjMed0rQvsvGrSBlnEWCKgoOrYIIZbLO61+J0Q==}
|
||||||
|
|
||||||
'@push.rocks/smartfs@1.1.0':
|
'@push.rocks/smartfile@13.1.2':
|
||||||
resolution: {integrity: sha512-fg8JIjFUPPX5laRoBpTaGwhMfZ3Y8mFT4fUaW54Y4J/BfOBa/y0+rIFgvgvqcOZgkQlyZU+FIfL8Z6zezqxyTg==}
|
resolution: {integrity: sha512-DaEhwmnGEpX4coeeToaw4cZe3pNBhH7CY1iGr+d3pIXihozREvzzAR9/0i2r7bUXXL5+Lgy8YYIk5ZS+fwxMKA==}
|
||||||
|
|
||||||
|
'@push.rocks/smartfs@1.3.1':
|
||||||
|
resolution: {integrity: sha512-ZSduVS8tM+/erbyCTvRRvc9gLWwbpqN5xdIIkMr+gub7fowSeJb7tR2rnGwySa63DyimU0q2KTp79VV9YqGLeg==}
|
||||||
|
|
||||||
'@push.rocks/smartguard@3.1.0':
|
'@push.rocks/smartguard@3.1.0':
|
||||||
resolution: {integrity: sha512-J23q84f1O+TwFGmd4lrO9XLHUh2DaLXo9PN/9VmTWYzTkQDv5JehmifXVI0esophXcCIfbdIu6hbt7/aHlDF4A==}
|
resolution: {integrity: sha512-J23q84f1O+TwFGmd4lrO9XLHUh2DaLXo9PN/9VmTWYzTkQDv5JehmifXVI0esophXcCIfbdIu6hbt7/aHlDF4A==}
|
||||||
@@ -1363,6 +1526,9 @@ packages:
|
|||||||
'@types/node@22.19.1':
|
'@types/node@22.19.1':
|
||||||
resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==}
|
resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==}
|
||||||
|
|
||||||
|
'@types/node@25.0.9':
|
||||||
|
resolution: {integrity: sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==}
|
||||||
|
|
||||||
'@types/ping@0.4.4':
|
'@types/ping@0.4.4':
|
||||||
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
|
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
|
||||||
|
|
||||||
@@ -1914,6 +2080,11 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
esbuild@0.27.2:
|
||||||
|
resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
escalade@3.2.0:
|
escalade@3.2.0:
|
||||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3301,8 +3472,8 @@ packages:
|
|||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
tsx@4.20.6:
|
tsx@4.21.0:
|
||||||
resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==}
|
resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -3353,6 +3524,9 @@ packages:
|
|||||||
undici-types@6.21.0:
|
undici-types@6.21.0:
|
||||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
|
undici-types@7.16.0:
|
||||||
|
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||||
|
|
||||||
unified@11.0.5:
|
unified@11.0.5:
|
||||||
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
|
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
|
||||||
|
|
||||||
@@ -4153,88 +4327,167 @@ snapshots:
|
|||||||
'@esbuild/aix-ppc64@0.25.12':
|
'@esbuild/aix-ppc64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/android-arm64@0.25.12':
|
'@esbuild/android-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/android-arm@0.25.12':
|
'@esbuild/android-arm@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/android-x64@0.25.12':
|
'@esbuild/android-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/darwin-arm64@0.25.12':
|
'@esbuild/darwin-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/darwin-x64@0.25.12':
|
'@esbuild/darwin-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/freebsd-arm64@0.25.12':
|
'@esbuild/freebsd-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/freebsd-x64@0.25.12':
|
'@esbuild/freebsd-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-arm64@0.25.12':
|
'@esbuild/linux-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-arm@0.25.12':
|
'@esbuild/linux-arm@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-ia32@0.25.12':
|
'@esbuild/linux-ia32@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-loong64@0.25.12':
|
'@esbuild/linux-loong64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-mips64el@0.25.12':
|
'@esbuild/linux-mips64el@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-ppc64@0.25.12':
|
'@esbuild/linux-ppc64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-riscv64@0.25.12':
|
'@esbuild/linux-riscv64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-s390x@0.25.12':
|
'@esbuild/linux-s390x@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/linux-x64@0.25.12':
|
'@esbuild/linux-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/netbsd-arm64@0.25.12':
|
'@esbuild/netbsd-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/netbsd-x64@0.25.12':
|
'@esbuild/netbsd-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/openbsd-arm64@0.25.12':
|
'@esbuild/openbsd-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/openbsd-x64@0.25.12':
|
'@esbuild/openbsd-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/openharmony-arm64@0.25.12':
|
'@esbuild/openharmony-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/sunos-x64@0.25.12':
|
'@esbuild/sunos-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/win32-arm64@0.25.12':
|
'@esbuild/win32-arm64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/win32-ia32@0.25.12':
|
'@esbuild/win32-ia32@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@esbuild/win32-x64@0.25.12':
|
'@esbuild/win32-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@git.zone/tsbuild@3.1.0':
|
'@esbuild/win32-x64@0.27.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@git.zone/tsbuild@4.1.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@git.zone/tspublish': 1.10.3
|
'@git.zone/tspublish': 1.11.0
|
||||||
'@push.rocks/early': 4.0.4
|
'@push.rocks/early': 4.0.4
|
||||||
'@push.rocks/smartcli': 4.0.19
|
'@push.rocks/smartcli': 4.0.20
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartfile': 11.2.7
|
'@push.rocks/smartfile': 13.1.2
|
||||||
|
'@push.rocks/smartfs': 1.3.1
|
||||||
'@push.rocks/smartlog': 3.1.10
|
'@push.rocks/smartlog': 3.1.10
|
||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
@@ -4251,7 +4504,7 @@ snapshots:
|
|||||||
'@git.zone/tsbundle@2.5.2':
|
'@git.zone/tsbundle@2.5.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/early': 4.0.4
|
'@push.rocks/early': 4.0.4
|
||||||
'@push.rocks/smartcli': 4.0.19
|
'@push.rocks/smartcli': 4.0.20
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartfile': 11.2.7
|
'@push.rocks/smartfile': 11.2.7
|
||||||
'@push.rocks/smartlog': 3.1.10
|
'@push.rocks/smartlog': 3.1.10
|
||||||
@@ -4269,16 +4522,18 @@ snapshots:
|
|||||||
- '@swc/helpers'
|
- '@swc/helpers'
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@git.zone/tspublish@1.10.3':
|
'@git.zone/tspublish@1.11.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/consolecolor': 2.0.3
|
'@push.rocks/consolecolor': 2.0.3
|
||||||
'@push.rocks/smartcli': 4.0.19
|
'@push.rocks/npmextra': 5.3.3
|
||||||
|
'@push.rocks/smartcli': 4.0.20
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartfile': 11.2.7
|
'@push.rocks/smartfile': 13.1.2
|
||||||
|
'@push.rocks/smartfs': 1.3.1
|
||||||
'@push.rocks/smartlog': 3.1.10
|
'@push.rocks/smartlog': 3.1.10
|
||||||
'@push.rocks/smartnpm': 2.0.6
|
'@push.rocks/smartnpm': 2.0.6
|
||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
'@push.rocks/smartrequest': 4.4.2
|
'@push.rocks/smartrequest': 5.0.1
|
||||||
'@push.rocks/smartshell': 3.3.0
|
'@push.rocks/smartshell': 3.3.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
@@ -4289,17 +4544,17 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@git.zone/tsrun@2.0.0':
|
'@git.zone/tsrun@2.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartfile': 11.2.7
|
'@push.rocks/smartfile': 13.1.2
|
||||||
'@push.rocks/smartshell': 3.3.0
|
'@push.rocks/smartshell': 3.3.0
|
||||||
tsx: 4.20.6
|
tsx: 4.21.0
|
||||||
|
|
||||||
'@git.zone/tstest@3.1.3(socks@2.8.7)(typescript@5.9.3)':
|
'@git.zone/tstest@3.1.6(socks@2.8.7)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedserver': 3.0.80
|
'@api.global/typedserver': 3.0.80
|
||||||
'@git.zone/tsbundle': 2.5.2
|
'@git.zone/tsbundle': 2.5.2
|
||||||
'@git.zone/tsrun': 2.0.0
|
'@git.zone/tsrun': 2.0.1
|
||||||
'@push.rocks/consolecolor': 2.0.3
|
'@push.rocks/consolecolor': 2.0.3
|
||||||
'@push.rocks/qenv': 6.1.3
|
'@push.rocks/qenv': 6.1.3
|
||||||
'@push.rocks/smartbrowser': 2.0.8(typescript@5.9.3)
|
'@push.rocks/smartbrowser': 2.0.8(typescript@5.9.3)
|
||||||
@@ -4727,7 +4982,7 @@ snapshots:
|
|||||||
chokidar: 4.0.3
|
chokidar: 4.0.3
|
||||||
picomatch: 4.0.3
|
picomatch: 4.0.3
|
||||||
|
|
||||||
'@push.rocks/smartcli@4.0.19':
|
'@push.rocks/smartcli@4.0.20':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/lik': 6.2.2
|
'@push.rocks/lik': 6.2.2
|
||||||
'@push.rocks/smartlog': 3.1.10
|
'@push.rocks/smartlog': 3.1.10
|
||||||
@@ -4870,7 +5125,24 @@ snapshots:
|
|||||||
glob: 11.1.0
|
glob: 11.1.0
|
||||||
js-yaml: 4.1.1
|
js-yaml: 4.1.1
|
||||||
|
|
||||||
'@push.rocks/smartfs@1.1.0':
|
'@push.rocks/smartfile@13.1.2':
|
||||||
|
dependencies:
|
||||||
|
'@push.rocks/lik': 6.2.2
|
||||||
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
|
'@push.rocks/smartfile-interfaces': 1.0.7
|
||||||
|
'@push.rocks/smartfs': 1.3.1
|
||||||
|
'@push.rocks/smarthash': 3.2.6
|
||||||
|
'@push.rocks/smartjson': 5.2.0
|
||||||
|
'@push.rocks/smartmime': 2.0.4
|
||||||
|
'@push.rocks/smartpath': 6.0.0
|
||||||
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
|
'@push.rocks/smartrequest': 4.4.2
|
||||||
|
'@push.rocks/smartstream': 3.2.5
|
||||||
|
'@types/js-yaml': 4.0.9
|
||||||
|
glob: 11.1.0
|
||||||
|
js-yaml: 4.1.1
|
||||||
|
|
||||||
|
'@push.rocks/smartfs@1.3.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
|
|
||||||
@@ -5936,6 +6208,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.21.0
|
undici-types: 6.21.0
|
||||||
|
|
||||||
|
'@types/node@25.0.9':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 7.16.0
|
||||||
|
|
||||||
'@types/ping@0.4.4': {}
|
'@types/ping@0.4.4': {}
|
||||||
|
|
||||||
'@types/qs@6.14.0': {}
|
'@types/qs@6.14.0': {}
|
||||||
@@ -6504,6 +6780,35 @@ snapshots:
|
|||||||
'@esbuild/win32-ia32': 0.25.12
|
'@esbuild/win32-ia32': 0.25.12
|
||||||
'@esbuild/win32-x64': 0.25.12
|
'@esbuild/win32-x64': 0.25.12
|
||||||
|
|
||||||
|
esbuild@0.27.2:
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/aix-ppc64': 0.27.2
|
||||||
|
'@esbuild/android-arm': 0.27.2
|
||||||
|
'@esbuild/android-arm64': 0.27.2
|
||||||
|
'@esbuild/android-x64': 0.27.2
|
||||||
|
'@esbuild/darwin-arm64': 0.27.2
|
||||||
|
'@esbuild/darwin-x64': 0.27.2
|
||||||
|
'@esbuild/freebsd-arm64': 0.27.2
|
||||||
|
'@esbuild/freebsd-x64': 0.27.2
|
||||||
|
'@esbuild/linux-arm': 0.27.2
|
||||||
|
'@esbuild/linux-arm64': 0.27.2
|
||||||
|
'@esbuild/linux-ia32': 0.27.2
|
||||||
|
'@esbuild/linux-loong64': 0.27.2
|
||||||
|
'@esbuild/linux-mips64el': 0.27.2
|
||||||
|
'@esbuild/linux-ppc64': 0.27.2
|
||||||
|
'@esbuild/linux-riscv64': 0.27.2
|
||||||
|
'@esbuild/linux-s390x': 0.27.2
|
||||||
|
'@esbuild/linux-x64': 0.27.2
|
||||||
|
'@esbuild/netbsd-arm64': 0.27.2
|
||||||
|
'@esbuild/netbsd-x64': 0.27.2
|
||||||
|
'@esbuild/openbsd-arm64': 0.27.2
|
||||||
|
'@esbuild/openbsd-x64': 0.27.2
|
||||||
|
'@esbuild/openharmony-arm64': 0.27.2
|
||||||
|
'@esbuild/sunos-x64': 0.27.2
|
||||||
|
'@esbuild/win32-arm64': 0.27.2
|
||||||
|
'@esbuild/win32-ia32': 0.27.2
|
||||||
|
'@esbuild/win32-x64': 0.27.2
|
||||||
|
|
||||||
escalade@3.2.0: {}
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
escape-html@1.0.3: {}
|
escape-html@1.0.3: {}
|
||||||
@@ -8259,9 +8564,9 @@ snapshots:
|
|||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
tsx@4.20.6:
|
tsx@4.21.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.25.12
|
esbuild: 0.27.2
|
||||||
get-tsconfig: 4.13.0
|
get-tsconfig: 4.13.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
@@ -8298,6 +8603,8 @@ snapshots:
|
|||||||
|
|
||||||
undici-types@6.21.0: {}
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
|
undici-types@7.16.0: {}
|
||||||
|
|
||||||
unified@11.0.5:
|
unified@11.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
|
|||||||
121
readme.hints.md
121
readme.hints.md
@@ -2,39 +2,108 @@
|
|||||||
|
|
||||||
## Module Purpose
|
## Module Purpose
|
||||||
|
|
||||||
tsdocker is a tool for developing npm modules cross-platform using Docker. It allows testing in clean, reproducible Linux environments locally.
|
tsdocker is a comprehensive Docker development and building tool. It provides:
|
||||||
|
- Testing npm modules in clean Docker environments (legacy feature)
|
||||||
|
- Building Dockerfiles with dependency ordering
|
||||||
|
- Multi-registry push/pull support
|
||||||
|
- Multi-architecture builds (amd64/arm64)
|
||||||
|
|
||||||
## Recent Upgrades (2025-11-22)
|
## New CLI Commands (2026-01-19)
|
||||||
|
|
||||||
- Updated all @git.zone/_ dependencies to @git.zone/_ scope (latest versions)
|
| Command | Description |
|
||||||
- Updated all @pushrocks/_ dependencies to @push.rocks/_ scope (latest versions)
|
|---------|-------------|
|
||||||
- Migrated from smartfile v8 to smartfs v1.1.0
|
| `tsdocker` | Run tests in container (legacy default behavior) |
|
||||||
- All filesystem operations now use smartfs fluent API
|
| `tsdocker build` | Build all Dockerfiles with dependency ordering |
|
||||||
- Operations are now async (smartfs is async-only)
|
| `tsdocker push [registry]` | Push images to configured registries |
|
||||||
- Updated dev dependencies:
|
| `tsdocker pull <registry>` | Pull images from registry |
|
||||||
- @git.zone/tsbuild: ^3.1.0
|
| `tsdocker test` | Run container tests (test scripts) |
|
||||||
- @git.zone/tsrun: ^2.0.0
|
| `tsdocker login` | Login to configured registries |
|
||||||
- @git.zone/tstest: ^3.1.3
|
| `tsdocker list` | List discovered Dockerfiles and dependencies |
|
||||||
- Removed @pushrocks/tapbundle (now use @git.zone/tstest/tapbundle)
|
| `tsdocker clean --all` | Clean up Docker environment |
|
||||||
- Updated @types/node to ^22.10.2
|
| `tsdocker vscode` | Start VS Code in Docker |
|
||||||
- Removed tslint and tslint-config-prettier (no longer needed)
|
|
||||||
|
|
||||||
## SmartFS Migration Details
|
## Configuration
|
||||||
|
|
||||||
The following operations were converted:
|
Configure in `package.json` under `@git.zone/tsdocker`:
|
||||||
|
|
||||||
- `smartfile.fs.fileExistsSync()` → Node.js `fs.existsSync()` (for sync needs)
|
```json
|
||||||
- `smartfile.fs.ensureDirSync()` → Node.js `fs.mkdirSync(..., { recursive: true })`
|
{
|
||||||
- `smartfile.memory.toFsSync()` → `smartfs.file(path).write(content)` (async)
|
"@git.zone/tsdocker": {
|
||||||
- `smartfile.fs.removeSync()` → `smartfs.file(path).delete()` (async)
|
"registries": ["registry.gitlab.com", "docker.io"],
|
||||||
|
"registryRepoMap": {
|
||||||
|
"registry.gitlab.com": "host.today/ht-docker-node"
|
||||||
|
},
|
||||||
|
"buildArgEnvMap": {
|
||||||
|
"NODE_VERSION": "NODE_VERSION"
|
||||||
|
},
|
||||||
|
"platforms": ["linux/amd64", "linux/arm64"],
|
||||||
|
"push": false,
|
||||||
|
"testDir": "./test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Test Status
|
### Configuration Options
|
||||||
|
|
||||||
- Build: ✅ Passes
|
- `baseImage`: Base Docker image for testing (legacy)
|
||||||
- The integration test requires cloning an external test repository (sandbox-npmts)
|
- `command`: Command to run in container (legacy)
|
||||||
- The external test repo uses top-level await which requires ESM module handling
|
- `dockerSock`: Mount Docker socket (legacy)
|
||||||
- This is not a tsdocker issue but rather the test repository's structure
|
- `registries`: Array of registry URLs to push to
|
||||||
|
- `registryRepoMap`: Map registry URLs to different repo paths
|
||||||
|
- `buildArgEnvMap`: Map Docker build ARGs to environment variables
|
||||||
|
- `platforms`: Target architectures for buildx
|
||||||
|
- `push`: Auto-push after build
|
||||||
|
- `testDir`: Directory containing test scripts
|
||||||
|
|
||||||
|
## Registry Authentication
|
||||||
|
|
||||||
|
Set environment variables for registry login:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pipe-delimited format (numbered 1-10)
|
||||||
|
export DOCKER_REGISTRY_1="registry.gitlab.com|username|password"
|
||||||
|
export DOCKER_REGISTRY_2="docker.io|username|password"
|
||||||
|
|
||||||
|
# Or individual registry format
|
||||||
|
export DOCKER_REGISTRY_URL="registry.gitlab.com"
|
||||||
|
export DOCKER_REGISTRY_USER="username"
|
||||||
|
export DOCKER_REGISTRY_PASSWORD="password"
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
ts/
|
||||||
|
├── index.ts (entry point)
|
||||||
|
├── tsdocker.cli.ts (CLI commands)
|
||||||
|
├── tsdocker.config.ts (configuration)
|
||||||
|
├── tsdocker.plugins.ts (plugin imports)
|
||||||
|
├── tsdocker.docker.ts (legacy test runner)
|
||||||
|
├── tsdocker.snippets.ts (Dockerfile generation)
|
||||||
|
├── classes.dockerfile.ts (Dockerfile management)
|
||||||
|
├── classes.dockerregistry.ts (registry authentication)
|
||||||
|
├── classes.registrystorage.ts (registry storage)
|
||||||
|
├── classes.tsdockermanager.ts (orchestrator)
|
||||||
|
└── interfaces/
|
||||||
|
└── index.ts (type definitions)
|
||||||
|
```
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
All dependencies are now at their latest versions compatible with Node.js without introducing new Node.js-specific dependencies.
|
- `@push.rocks/lik`: Object mapping utilities
|
||||||
|
- `@push.rocks/smartfs`: Filesystem operations
|
||||||
|
- `@push.rocks/smartshell`: Shell command execution
|
||||||
|
- `@push.rocks/smartcli`: CLI framework
|
||||||
|
- `@push.rocks/projectinfo`: Project metadata
|
||||||
|
|
||||||
|
## Build Status
|
||||||
|
|
||||||
|
- Build: ✅ Passes
|
||||||
|
- Legacy test functionality preserved
|
||||||
|
- New Docker build functionality added
|
||||||
|
|
||||||
|
## Previous Upgrades (2025-11-22)
|
||||||
|
|
||||||
|
- Updated all @git.zone/_ dependencies to @git.zone/_ scope
|
||||||
|
- Updated all @pushrocks/_ dependencies to @push.rocks/_ scope
|
||||||
|
- Migrated from smartfile v8 to smartfs v1.1.0
|
||||||
|
|||||||
602
readme.md
602
readme.md
@@ -1,6 +1,6 @@
|
|||||||
# @git.zone/tsdocker
|
# @git.zone/tsdocker
|
||||||
|
|
||||||
> 🐳 Cross-platform npm module development with Docker — test your packages in clean, reproducible Linux environments every time.
|
> 🐳 The ultimate Docker development toolkit for TypeScript projects — build, test, and ship containerized applications with ease.
|
||||||
|
|
||||||
## Issue Reporting and Security
|
## Issue Reporting and Security
|
||||||
|
|
||||||
@@ -8,324 +8,472 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|||||||
|
|
||||||
## What is tsdocker?
|
## What is tsdocker?
|
||||||
|
|
||||||
**tsdocker** provides containerized testing environments for npm packages, ensuring your code works consistently across different systems. It's perfect for:
|
**tsdocker** is a comprehensive Docker development and building tool that handles everything from testing npm packages in clean environments to building and pushing multi-architecture Docker images across multiple registries.
|
||||||
|
|
||||||
- 🧪 **Testing in clean environments** — Every test run starts fresh, just like CI
|
### 🎯 Key Capabilities
|
||||||
- 🔄 **Reproducing CI behavior locally** — No more "works on my machine" surprises
|
|
||||||
- 🐧 **Cross-platform development** — Develop on macOS/Windows, test on Linux
|
|
||||||
- 🚀 **Quick validation** — Spin up isolated containers for testing without polluting your system
|
|
||||||
|
|
||||||
## Features
|
- 🧪 **Containerized Testing** — Run your tests in pristine Docker environments
|
||||||
|
- 🏗️ **Smart Docker Builds** — Automatically discover, sort, and build Dockerfiles by dependency
|
||||||
✨ **Works Everywhere Docker Does**
|
- 🚀 **Multi-Registry Push** — Ship to Docker Hub, GitLab, GitHub Container Registry, and more
|
||||||
|
- 🔧 **Multi-Architecture** — Build for `amd64` and `arm64` with Docker Buildx
|
||||||
- Docker Toolbox
|
- ⚡ **Zero Config Start** — Works out of the box, scales with your needs
|
||||||
- Native Docker Desktop
|
|
||||||
- Docker-in-Docker (DinD)
|
|
||||||
- Mounted docker.sock scenarios
|
|
||||||
|
|
||||||
🔧 **Flexible Configuration**
|
|
||||||
|
|
||||||
- Custom base images
|
|
||||||
- Configurable test commands
|
|
||||||
- Environment variable injection via qenv
|
|
||||||
- Optional docker.sock mounting for nested container tests
|
|
||||||
|
|
||||||
📦 **TypeScript-First**
|
|
||||||
|
|
||||||
- Full TypeScript support with excellent IntelliSense
|
|
||||||
- Type-safe configuration
|
|
||||||
- Modern async/await patterns throughout
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @git.zone/tsdocker
|
# Global installation (recommended for CLI usage)
|
||||||
# or
|
npm install -g @git.zone/tsdocker
|
||||||
|
|
||||||
|
# Or project-local installation
|
||||||
pnpm install --save-dev @git.zone/tsdocker
|
pnpm install --save-dev @git.zone/tsdocker
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### 1. Configure Your Project
|
### 🧪 Run Tests in Docker
|
||||||
|
|
||||||
Create an `npmextra.json` file in your project root:
|
The simplest use case — run your tests in a clean container:
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"npmdocker": {
|
|
||||||
"baseImage": "hosttoday/ht-docker-node:npmts",
|
|
||||||
"command": "npmci test stable",
|
|
||||||
"dockerSock": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Run Your Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx tsdocker
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! tsdocker will:
|
|
||||||
|
|
||||||
1. ✅ Verify Docker is available
|
|
||||||
2. 🏗️ Build a test container with your specified base image
|
|
||||||
3. 📂 Mount your project directory
|
|
||||||
4. 🚀 Execute your test command
|
|
||||||
5. 🧹 Clean up automatically
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
| Option | Type | Description |
|
|
||||||
| ------------ | --------- | ---------------------------------------------------------------------- |
|
|
||||||
| `baseImage` | `string` | Docker image to use as the test environment base |
|
|
||||||
| `command` | `string` | CLI command to execute inside the container |
|
|
||||||
| `dockerSock` | `boolean` | Whether to mount `/var/run/docker.sock` for Docker-in-Docker scenarios |
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
If you have a `qenv.yml` file in your project, tsdocker automatically loads and injects those environment variables into your test container.
|
|
||||||
|
|
||||||
Example `qenv.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
demoKey: demoValue
|
|
||||||
API_KEY: your-key-here
|
|
||||||
```
|
|
||||||
|
|
||||||
## CLI Commands
|
|
||||||
|
|
||||||
### Standard Test Run
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tsdocker
|
tsdocker
|
||||||
```
|
```
|
||||||
|
|
||||||
Runs your configured test command in a fresh Docker container.
|
This pulls your configured base image, mounts your project, and executes your test command in isolation.
|
||||||
|
|
||||||
### Clean Docker Environment
|
### 🏗️ Build Docker Images
|
||||||
|
|
||||||
|
Got `Dockerfile` files? Build them all with automatic dependency ordering:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tsdocker clean --all
|
tsdocker build
|
||||||
```
|
```
|
||||||
|
|
||||||
⚠️ **WARNING**: This aggressively cleans your Docker environment by:
|
tsdocker will:
|
||||||
|
1. 🔍 Discover all `Dockerfile*` files in your project
|
||||||
|
2. 📊 Analyze `FROM` dependencies between them
|
||||||
|
3. 🔄 Sort them topologically
|
||||||
|
4. 🏗️ Build each image in the correct order
|
||||||
|
|
||||||
- Killing all running containers
|
### 📤 Push to Registries
|
||||||
- Removing all stopped containers
|
|
||||||
- Removing dangling images
|
|
||||||
- Removing all images
|
|
||||||
- Removing dangling volumes
|
|
||||||
|
|
||||||
Use with caution!
|
Ship your images to one or all configured registries:
|
||||||
|
|
||||||
### VSCode in Docker
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tsdocker vscode
|
# Push to all configured registries
|
||||||
|
tsdocker push
|
||||||
|
|
||||||
|
# Push to a specific registry
|
||||||
|
tsdocker push registry.gitlab.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Launches a containerized VS Code instance accessible via browser at `testing-vscode.git.zone:8443`.
|
## CLI Commands
|
||||||
|
|
||||||
### Speed Test
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `tsdocker` | Run tests in a fresh Docker container |
|
||||||
|
| `tsdocker build` | Build all Dockerfiles with dependency ordering |
|
||||||
|
| `tsdocker push [registry]` | Push images to configured registries |
|
||||||
|
| `tsdocker pull <registry>` | Pull images from a specific registry |
|
||||||
|
| `tsdocker test` | Run container test scripts (test_*.sh) |
|
||||||
|
| `tsdocker login` | Authenticate with configured registries |
|
||||||
|
| `tsdocker list` | Display discovered Dockerfiles and their dependencies |
|
||||||
|
| `tsdocker clean --all` | ⚠️ Aggressively clean Docker environment |
|
||||||
|
| `tsdocker vscode` | Launch containerized VS Code in browser |
|
||||||
|
|
||||||
```bash
|
## Configuration
|
||||||
tsdocker speedtest
|
|
||||||
```
|
|
||||||
|
|
||||||
Runs a network speed test inside a Docker container.
|
Configure tsdocker in your `package.json` or `npmextra.json`:
|
||||||
|
|
||||||
## Advanced Usage
|
|
||||||
|
|
||||||
### Docker-in-Docker Testing
|
|
||||||
|
|
||||||
If you need to run Docker commands inside your test container (e.g., testing Docker-related tools):
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"npmdocker": {
|
"@git.zone/tsdocker": {
|
||||||
|
"baseImage": "node:20",
|
||||||
|
"command": "npm test",
|
||||||
|
"dockerSock": false,
|
||||||
|
"registries": ["registry.gitlab.com", "docker.io"],
|
||||||
|
"registryRepoMap": {
|
||||||
|
"registry.gitlab.com": "myorg/myproject"
|
||||||
|
},
|
||||||
|
"buildArgEnvMap": {
|
||||||
|
"NODE_VERSION": "NODE_VERSION"
|
||||||
|
},
|
||||||
|
"platforms": ["linux/amd64", "linux/arm64"],
|
||||||
|
"push": false,
|
||||||
|
"testDir": "./test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Options
|
||||||
|
|
||||||
|
#### Testing Options (Legacy)
|
||||||
|
|
||||||
|
| Option | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| `baseImage` | `string` | Docker image for test environment (default: `hosttoday/ht-docker-node:npmdocker`) |
|
||||||
|
| `command` | `string` | Command to run inside container (default: `npmci npm test`) |
|
||||||
|
| `dockerSock` | `boolean` | Mount Docker socket for DinD scenarios (default: `false`) |
|
||||||
|
|
||||||
|
#### Build & Push Options
|
||||||
|
|
||||||
|
| Option | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| `registries` | `string[]` | Registry URLs to push to |
|
||||||
|
| `registryRepoMap` | `object` | Map registries to different repository paths |
|
||||||
|
| `buildArgEnvMap` | `object` | Map Docker build ARGs to environment variables |
|
||||||
|
| `platforms` | `string[]` | Target architectures (default: `["linux/amd64"]`) |
|
||||||
|
| `push` | `boolean` | Auto-push after build (default: `false`) |
|
||||||
|
| `testDir` | `string` | Directory containing test scripts |
|
||||||
|
|
||||||
|
## Registry Authentication
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pipe-delimited format (supports DOCKER_REGISTRY_1 through DOCKER_REGISTRY_10)
|
||||||
|
export DOCKER_REGISTRY_1="registry.gitlab.com|username|password"
|
||||||
|
export DOCKER_REGISTRY_2="docker.io|username|password"
|
||||||
|
|
||||||
|
# Individual registry format
|
||||||
|
export DOCKER_REGISTRY_URL="registry.gitlab.com"
|
||||||
|
export DOCKER_REGISTRY_USER="username"
|
||||||
|
export DOCKER_REGISTRY_PASSWORD="password"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Login Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tsdocker login
|
||||||
|
```
|
||||||
|
|
||||||
|
Authenticates with all configured registries.
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### 🔀 Multi-Architecture Builds
|
||||||
|
|
||||||
|
Build for multiple platforms using Docker Buildx:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@git.zone/tsdocker": {
|
||||||
|
"platforms": ["linux/amd64", "linux/arm64"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
tsdocker automatically sets up a Buildx builder when multiple platforms are specified.
|
||||||
|
|
||||||
|
### 📦 Dockerfile Naming Conventions
|
||||||
|
|
||||||
|
tsdocker discovers files matching `Dockerfile*`:
|
||||||
|
|
||||||
|
| File Name | Version Tag |
|
||||||
|
|-----------|-------------|
|
||||||
|
| `Dockerfile` | `latest` |
|
||||||
|
| `Dockerfile_v1.0.0` | `v1.0.0` |
|
||||||
|
| `Dockerfile_alpine` | `alpine` |
|
||||||
|
| `Dockerfile_##version##` | Uses `package.json` version |
|
||||||
|
|
||||||
|
### 🔗 Dependency-Aware Builds
|
||||||
|
|
||||||
|
If you have multiple Dockerfiles that depend on each other:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Dockerfile_base
|
||||||
|
FROM node:20-alpine
|
||||||
|
RUN npm install -g typescript
|
||||||
|
|
||||||
|
# Dockerfile_app
|
||||||
|
FROM myproject:base
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
tsdocker automatically detects that `Dockerfile_app` depends on `Dockerfile_base` and builds them in the correct order.
|
||||||
|
|
||||||
|
### 🧪 Container Test Scripts
|
||||||
|
|
||||||
|
Create test scripts in your test directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# test/test_latest.sh
|
||||||
|
#!/bin/bash
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
echo "Container tests passed!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tsdocker test
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔧 Build Args from Environment
|
||||||
|
|
||||||
|
Pass environment variables as Docker build arguments:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@git.zone/tsdocker": {
|
||||||
|
"buildArgEnvMap": {
|
||||||
|
"NPM_TOKEN": "NPM_TOKEN",
|
||||||
|
"NODE_VERSION": "NODE_VERSION"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
ARG NPM_TOKEN
|
||||||
|
ARG NODE_VERSION=20
|
||||||
|
FROM node:${NODE_VERSION}
|
||||||
|
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🐳 Docker-in-Docker Testing
|
||||||
|
|
||||||
|
Test Docker-related tools by mounting the Docker socket:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@git.zone/tsdocker": {
|
||||||
"baseImage": "docker:latest",
|
"baseImage": "docker:latest",
|
||||||
"command": "docker run hello-world",
|
"command": "docker version && docker ps",
|
||||||
"dockerSock": true
|
"dockerSock": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Setting `"dockerSock": true` mounts the host's Docker socket into the container.
|
### 📋 Listing Dockerfiles
|
||||||
|
|
||||||
### Custom Base Images
|
Inspect your project's Dockerfiles and their relationships:
|
||||||
|
|
||||||
You can use any Docker image as your base:
|
```bash
|
||||||
|
tsdocker list
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
Discovered Dockerfiles:
|
||||||
|
========================
|
||||||
|
|
||||||
|
1. Dockerfile_base
|
||||||
|
Tag: myproject:base
|
||||||
|
Base Image: node:20-alpine
|
||||||
|
Version: base
|
||||||
|
|
||||||
|
2. Dockerfile_app
|
||||||
|
Tag: myproject:app
|
||||||
|
Base Image: myproject:base
|
||||||
|
Version: app
|
||||||
|
Depends on: myproject:base
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🗺️ Registry Repo Mapping
|
||||||
|
|
||||||
|
Use different repository names for different registries:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"npmdocker": {
|
"@git.zone/tsdocker": {
|
||||||
"baseImage": "node:20-alpine",
|
"registries": ["registry.gitlab.com", "docker.io"],
|
||||||
"command": "npm test"
|
"registryRepoMap": {
|
||||||
|
"registry.gitlab.com": "mygroup/myproject",
|
||||||
|
"docker.io": "myuser/myproject"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Popular choices:
|
## Environment Variables
|
||||||
|
|
||||||
- `node:20` — Official Node.js images
|
### qenv Integration
|
||||||
- `node:20-alpine` — Lightweight Alpine-based images
|
|
||||||
- `hosttoday/ht-docker-node:npmts` — Pre-configured with npmts tooling
|
|
||||||
|
|
||||||
### CI Integration
|
tsdocker automatically loads environment variables from `qenv.yml`:
|
||||||
|
|
||||||
tsdocker automatically detects CI environments (via `CI=true` env var) and adjusts behavior:
|
```yaml
|
||||||
|
# qenv.yml
|
||||||
- Skips mounting project directory in CI (assumes code is already in container)
|
API_KEY: your-api-key
|
||||||
- Optimizes for CI execution patterns
|
DATABASE_URL: postgres://localhost/test
|
||||||
|
|
||||||
## Why tsdocker?
|
|
||||||
|
|
||||||
### The Problem
|
|
||||||
|
|
||||||
Local development environments drift over time. You might have:
|
|
||||||
|
|
||||||
- Stale global packages
|
|
||||||
- Modified system configurations
|
|
||||||
- Cached dependencies
|
|
||||||
- Different Node.js versions
|
|
||||||
|
|
||||||
Your tests pass locally but fail in CI — or vice versa.
|
|
||||||
|
|
||||||
### The Solution
|
|
||||||
|
|
||||||
tsdocker ensures every test run happens in a **clean, reproducible environment**, just like your CI pipeline. This means:
|
|
||||||
|
|
||||||
✅ Consistent behavior between local and CI
|
|
||||||
✅ No dependency pollution between test runs
|
|
||||||
✅ Easy cross-platform testing
|
|
||||||
✅ Reproducible bug investigations
|
|
||||||
|
|
||||||
## TypeScript Usage
|
|
||||||
|
|
||||||
tsdocker is built with TypeScript and provides full type definitions:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { IConfig } from '@git.zone/tsdocker/dist/tsdocker.config';
|
|
||||||
|
|
||||||
const config: IConfig = {
|
|
||||||
baseImage: 'node:20',
|
|
||||||
command: 'npm test',
|
|
||||||
dockerSock: false,
|
|
||||||
keyValueObject: {
|
|
||||||
NODE_ENV: 'test',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Requirements
|
These are injected into your test container automatically.
|
||||||
|
|
||||||
- **Docker**: Docker must be installed and accessible via CLI
|
|
||||||
- **Node.js**: Version 14 or higher recommended
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
Under the hood, tsdocker:
|
|
||||||
|
|
||||||
1. 📋 Reads your `npmextra.json` configuration
|
|
||||||
2. 🔍 Optionally loads environment variables from `qenv.yml`
|
|
||||||
3. 🐳 Generates a temporary Dockerfile
|
|
||||||
4. 🏗️ Builds a Docker image with your base image
|
|
||||||
5. 📦 Mounts your project directory (unless in CI)
|
|
||||||
6. ▶️ Runs your test command inside the container
|
|
||||||
7. 📊 Captures the exit code
|
|
||||||
8. 🧹 Cleans up containers and images
|
|
||||||
9. ✅ Exits with the same code as your tests
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "docker not found on this machine"
|
|
||||||
|
|
||||||
Make sure Docker is installed and the `docker` command is in your PATH:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker --version
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tests fail in container but work locally
|
|
||||||
|
|
||||||
This often indicates environment-specific issues. Check:
|
|
||||||
|
|
||||||
- Are all dependencies in `package.json`? (not relying on global packages)
|
|
||||||
- Does your code have hardcoded paths?
|
|
||||||
- Are environment variables set correctly?
|
|
||||||
|
|
||||||
### Permission errors with docker.sock
|
|
||||||
|
|
||||||
If using `dockerSock: true`, ensure your user has permissions to access `/var/run/docker.sock`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo usermod -aG docker $USER
|
|
||||||
# Then log out and back in
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Basic npm test
|
### Basic Test Configuration
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"npmdocker": {
|
"@git.zone/tsdocker": {
|
||||||
"baseImage": "node:20",
|
"baseImage": "node:20",
|
||||||
"command": "npm test"
|
"command": "npm test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using npmci for multiple Node versions
|
### Full Production Setup
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"npmdocker": {
|
"@git.zone/tsdocker": {
|
||||||
"baseImage": "hosttoday/ht-docker-node:npmts",
|
"baseImage": "node:20-alpine",
|
||||||
"command": "npmci test stable"
|
"command": "pnpm test",
|
||||||
|
"registries": ["registry.gitlab.com", "ghcr.io", "docker.io"],
|
||||||
|
"registryRepoMap": {
|
||||||
|
"registry.gitlab.com": "myorg/myapp",
|
||||||
|
"ghcr.io": "myorg/myapp",
|
||||||
|
"docker.io": "myuser/myapp"
|
||||||
|
},
|
||||||
|
"buildArgEnvMap": {
|
||||||
|
"NPM_TOKEN": "NPM_TOKEN"
|
||||||
|
},
|
||||||
|
"platforms": ["linux/amd64", "linux/arm64"],
|
||||||
|
"testDir": "./docker-tests"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing Docker-based tools
|
### CI/CD Integration
|
||||||
|
|
||||||
```json
|
```yaml
|
||||||
{
|
# .gitlab-ci.yml
|
||||||
"npmdocker": {
|
build:
|
||||||
"baseImage": "docker:latest",
|
stage: build
|
||||||
"command": "sh -c 'docker version && docker ps'",
|
script:
|
||||||
"dockerSock": true
|
- npm install -g @git.zone/tsdocker
|
||||||
}
|
- tsdocker build
|
||||||
}
|
- tsdocker push
|
||||||
|
|
||||||
|
# GitHub Actions
|
||||||
|
- name: Build and Push
|
||||||
|
run: |
|
||||||
|
npm install -g @git.zone/tsdocker
|
||||||
|
tsdocker login
|
||||||
|
tsdocker build
|
||||||
|
tsdocker push
|
||||||
|
env:
|
||||||
|
DOCKER_REGISTRY_1: "ghcr.io|${{ github.actor }}|${{ secrets.GITHUB_TOKEN }}"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **Docker** — Docker Engine or Docker Desktop must be installed
|
||||||
|
- **Node.js** — Version 18 or higher (ESM support required)
|
||||||
|
- **Docker Buildx** — Required for multi-architecture builds (included in Docker Desktop)
|
||||||
|
|
||||||
|
## Why tsdocker?
|
||||||
|
|
||||||
|
### 🎯 The Problem
|
||||||
|
|
||||||
|
Managing Docker workflows manually is tedious:
|
||||||
|
- Remembering build order for dependent images
|
||||||
|
- Pushing to multiple registries with different credentials
|
||||||
|
- Setting up Buildx for multi-arch builds
|
||||||
|
- Ensuring consistent test environments
|
||||||
|
|
||||||
|
### ✨ The Solution
|
||||||
|
|
||||||
|
tsdocker automates the entire workflow:
|
||||||
|
- **One command** to build all images in dependency order
|
||||||
|
- **One command** to push to all registries
|
||||||
|
- **Automatic** Buildx setup for multi-platform builds
|
||||||
|
- **Consistent** containerized test environments
|
||||||
|
|
||||||
|
## TypeScript API
|
||||||
|
|
||||||
|
tsdocker exposes its types for programmatic use:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { ITsDockerConfig } from '@git.zone/tsdocker/dist_ts/interfaces/index.js';
|
||||||
|
import { TsDockerManager } from '@git.zone/tsdocker/dist_ts/classes.tsdockermanager.js';
|
||||||
|
|
||||||
|
const config: ITsDockerConfig = {
|
||||||
|
baseImage: 'node:20',
|
||||||
|
command: 'npm test',
|
||||||
|
dockerSock: false,
|
||||||
|
keyValueObject: {},
|
||||||
|
registries: ['docker.io'],
|
||||||
|
platforms: ['linux/amd64'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
await manager.build();
|
||||||
|
await manager.push();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "docker not found"
|
||||||
|
|
||||||
|
Ensure Docker is installed and in your PATH:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-arch build fails
|
||||||
|
|
||||||
|
Make sure Docker Buildx is available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker buildx version
|
||||||
|
docker buildx create --use
|
||||||
|
```
|
||||||
|
|
||||||
|
### Registry authentication fails
|
||||||
|
|
||||||
|
Check your environment variables are set correctly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo $DOCKER_REGISTRY_1
|
||||||
|
tsdocker login
|
||||||
|
```
|
||||||
|
|
||||||
|
### Circular dependency detected
|
||||||
|
|
||||||
|
Review your Dockerfiles' `FROM` statements — you have images depending on each other in a loop.
|
||||||
|
|
||||||
## Performance Tips
|
## Performance Tips
|
||||||
|
|
||||||
🚀 **Use specific base images**: `node:20-alpine` is much faster to pull than `node:latest`
|
🚀 **Use specific tags**: `node:20-alpine` is smaller and faster than `node:latest`
|
||||||
🚀 **Layer caching**: Docker caches image layers — your base image only downloads once
|
|
||||||
🚀 **Prune regularly**: Run `docker system prune` periodically to reclaim disk space
|
|
||||||
|
|
||||||
## Migration from legacy npmdocker scope
|
🚀 **Leverage caching**: Docker layers are cached — your builds get faster over time
|
||||||
|
|
||||||
This package was previously published under the `npmdocker` name in the old scope. It is now available as `@git.zone/tsdocker` for better naming consistency. Functionality remains the same.
|
🚀 **Prune regularly**: `docker system prune` reclaims disk space
|
||||||
|
|
||||||
|
🚀 **Use .dockerignore**: Exclude `node_modules`, `.git`, etc. from build context
|
||||||
|
|
||||||
|
## Migration from Legacy
|
||||||
|
|
||||||
|
Previously published as `npmdocker`, now `@git.zone/tsdocker`:
|
||||||
|
|
||||||
|
| Old | New |
|
||||||
|
|-----|-----|
|
||||||
|
| `npmdocker` command | `tsdocker` command |
|
||||||
|
| `"npmdocker"` config key | `"@git.zone/tsdocker"` config key |
|
||||||
|
| CommonJS | ESM with `.js` imports |
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
|
||||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
|
||||||
|
|
||||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
### Trademarks
|
### Trademarks
|
||||||
|
|
||||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
||||||
|
|
||||||
|
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
||||||
|
|
||||||
### Company Information
|
### Company Information
|
||||||
|
|
||||||
Task Venture Capital GmbH
|
Task Venture Capital GmbH
|
||||||
Registered at District court Bremen HRB 35230 HB, Germany
|
Registered at District Court Bremen HRB 35230 HB, Germany
|
||||||
|
|
||||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
||||||
|
|
||||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tsdocker',
|
name: '@git.zone/tsdocker',
|
||||||
version: '1.2.43',
|
version: '1.7.0',
|
||||||
description: 'develop npm modules cross platform with docker'
|
description: 'develop npm modules cross platform with docker'
|
||||||
}
|
}
|
||||||
|
|||||||
533
ts/classes.dockerfile.ts
Normal file
533
ts/classes.dockerfile.ts
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
|
import * as paths from './tsdocker.paths.js';
|
||||||
|
import { logger } from './tsdocker.logging.js';
|
||||||
|
import { DockerRegistry } from './classes.dockerregistry.js';
|
||||||
|
import type { IDockerfileOptions, ITsDockerConfig, IBuildCommandOptions } from './interfaces/index.js';
|
||||||
|
import type { TsDockerManager } from './classes.tsdockermanager.js';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Dockerfile represents a Dockerfile on disk
|
||||||
|
*/
|
||||||
|
export class Dockerfile {
|
||||||
|
// STATIC METHODS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates instances of class Dockerfile for all Dockerfiles in cwd
|
||||||
|
*/
|
||||||
|
public static async readDockerfiles(managerRef: TsDockerManager): Promise<Dockerfile[]> {
|
||||||
|
const entries = await plugins.smartfs.directory(paths.cwd).filter('Dockerfile*').list();
|
||||||
|
const fileTree = entries
|
||||||
|
.filter(entry => entry.isFile)
|
||||||
|
.map(entry => plugins.path.join(paths.cwd, entry.name));
|
||||||
|
|
||||||
|
const readDockerfilesArray: Dockerfile[] = [];
|
||||||
|
logger.log('info', `found ${fileTree.length} Dockerfiles:`);
|
||||||
|
console.log(fileTree);
|
||||||
|
|
||||||
|
for (const dockerfilePath of fileTree) {
|
||||||
|
const myDockerfile = new Dockerfile(managerRef, {
|
||||||
|
filePath: dockerfilePath,
|
||||||
|
read: true,
|
||||||
|
});
|
||||||
|
readDockerfilesArray.push(myDockerfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return readDockerfilesArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts Dockerfiles into a build order based on dependencies (topological sort)
|
||||||
|
*/
|
||||||
|
public static async sortDockerfiles(dockerfiles: Dockerfile[]): Promise<Dockerfile[]> {
|
||||||
|
logger.log('info', 'Sorting Dockerfiles based on dependencies...');
|
||||||
|
|
||||||
|
// Map from cleanTag to Dockerfile instance for quick lookup
|
||||||
|
const tagToDockerfile = new Map<string, Dockerfile>();
|
||||||
|
dockerfiles.forEach((dockerfile) => {
|
||||||
|
tagToDockerfile.set(dockerfile.cleanTag, dockerfile);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build the dependency graph
|
||||||
|
const graph = new Map<Dockerfile, Dockerfile[]>();
|
||||||
|
dockerfiles.forEach((dockerfile) => {
|
||||||
|
const dependencies: Dockerfile[] = [];
|
||||||
|
const baseImage = dockerfile.baseImage;
|
||||||
|
|
||||||
|
// Extract repo:version from baseImage for comparison with cleanTag
|
||||||
|
// baseImage may include a registry prefix (e.g., "host.today/repo:version")
|
||||||
|
// but cleanTag is just "repo:version", so we strip the registry prefix
|
||||||
|
const baseImageKey = Dockerfile.extractRepoVersion(baseImage);
|
||||||
|
|
||||||
|
// Check if the baseImage is among the local Dockerfiles
|
||||||
|
if (tagToDockerfile.has(baseImageKey)) {
|
||||||
|
const baseDockerfile = tagToDockerfile.get(baseImageKey)!;
|
||||||
|
dependencies.push(baseDockerfile);
|
||||||
|
dockerfile.localBaseImageDependent = true;
|
||||||
|
dockerfile.localBaseDockerfile = baseDockerfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.set(dockerfile, dependencies);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Perform topological sort
|
||||||
|
const sortedDockerfiles: Dockerfile[] = [];
|
||||||
|
const visited = new Set<Dockerfile>();
|
||||||
|
const tempMarked = new Set<Dockerfile>();
|
||||||
|
|
||||||
|
const visit = (dockerfile: Dockerfile) => {
|
||||||
|
if (tempMarked.has(dockerfile)) {
|
||||||
|
throw new Error(`Circular dependency detected involving ${dockerfile.cleanTag}`);
|
||||||
|
}
|
||||||
|
if (!visited.has(dockerfile)) {
|
||||||
|
tempMarked.add(dockerfile);
|
||||||
|
const dependencies = graph.get(dockerfile) || [];
|
||||||
|
dependencies.forEach((dep) => visit(dep));
|
||||||
|
tempMarked.delete(dockerfile);
|
||||||
|
visited.add(dockerfile);
|
||||||
|
sortedDockerfiles.push(dockerfile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
dockerfiles.forEach((dockerfile) => {
|
||||||
|
if (!visited.has(dockerfile)) {
|
||||||
|
visit(dockerfile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', (error as Error).message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the sorted order
|
||||||
|
sortedDockerfiles.forEach((dockerfile, index) => {
|
||||||
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Build order ${index + 1}: ${dockerfile.cleanTag} with base image ${dockerfile.baseImage}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortedDockerfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps local Dockerfiles dependencies to the corresponding Dockerfile class instances
|
||||||
|
*/
|
||||||
|
public static async mapDockerfiles(sortedDockerfileArray: Dockerfile[]): Promise<Dockerfile[]> {
|
||||||
|
sortedDockerfileArray.forEach((dockerfileArg) => {
|
||||||
|
if (dockerfileArg.localBaseImageDependent) {
|
||||||
|
// Extract repo:version from baseImage for comparison with cleanTag
|
||||||
|
const baseImageKey = Dockerfile.extractRepoVersion(dockerfileArg.baseImage);
|
||||||
|
sortedDockerfileArray.forEach((dockfile2: Dockerfile) => {
|
||||||
|
if (dockfile2.cleanTag === baseImageKey) {
|
||||||
|
dockerfileArg.localBaseDockerfile = dockfile2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return sortedDockerfileArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the corresponding real docker image for each Dockerfile class instance
|
||||||
|
*/
|
||||||
|
public static async buildDockerfiles(
|
||||||
|
sortedArrayArg: Dockerfile[],
|
||||||
|
options?: { platform?: string; timeout?: number; noCache?: boolean },
|
||||||
|
): Promise<Dockerfile[]> {
|
||||||
|
for (const dockerfileArg of sortedArrayArg) {
|
||||||
|
await dockerfileArg.build(options);
|
||||||
|
|
||||||
|
// Tag the built image with the full base image references used by dependent Dockerfiles,
|
||||||
|
// so their FROM lines resolve to the locally-built image instead of pulling from a registry.
|
||||||
|
const dependentBaseImages = new Set<string>();
|
||||||
|
for (const other of sortedArrayArg) {
|
||||||
|
if (other.localBaseDockerfile === dockerfileArg && other.baseImage !== dockerfileArg.buildTag) {
|
||||||
|
dependentBaseImages.add(other.baseImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const fullTag of dependentBaseImages) {
|
||||||
|
logger.log('info', `Tagging ${dockerfileArg.buildTag} as ${fullTag} for local dependency resolution`);
|
||||||
|
await smartshellInstance.exec(`docker tag ${dockerfileArg.buildTag} ${fullTag}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sortedArrayArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests all Dockerfiles by calling Dockerfile.test()
|
||||||
|
*/
|
||||||
|
public static async testDockerfiles(sortedArrayArg: Dockerfile[]): Promise<Dockerfile[]> {
|
||||||
|
for (const dockerfileArg of sortedArrayArg) {
|
||||||
|
await dockerfileArg.test();
|
||||||
|
}
|
||||||
|
return sortedArrayArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version for a docker file
|
||||||
|
* Dockerfile_latest -> latest
|
||||||
|
* Dockerfile_v1.0.0 -> v1.0.0
|
||||||
|
* Dockerfile -> latest
|
||||||
|
*/
|
||||||
|
public static dockerFileVersion(
|
||||||
|
dockerfileInstanceArg: Dockerfile,
|
||||||
|
dockerfileNameArg: string
|
||||||
|
): string {
|
||||||
|
let versionString: string;
|
||||||
|
const versionRegex = /Dockerfile_(.+)$/;
|
||||||
|
const regexResultArray = versionRegex.exec(dockerfileNameArg);
|
||||||
|
if (regexResultArray && regexResultArray.length === 2) {
|
||||||
|
versionString = regexResultArray[1];
|
||||||
|
} else {
|
||||||
|
versionString = 'latest';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace ##version## placeholder with actual package version if available
|
||||||
|
if (dockerfileInstanceArg.managerRef?.projectInfo?.npm?.version) {
|
||||||
|
versionString = versionString.replace(
|
||||||
|
'##version##',
|
||||||
|
dockerfileInstanceArg.managerRef.projectInfo.npm.version
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the base image from a Dockerfile content
|
||||||
|
* Handles ARG substitution for variable base images
|
||||||
|
*/
|
||||||
|
public static dockerBaseImage(dockerfileContentArg: string): string {
|
||||||
|
const lines = dockerfileContentArg.split(/\r?\n/);
|
||||||
|
const args: { [key: string]: string } = {};
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
|
// Skip empty lines and comments
|
||||||
|
if (trimmedLine === '' || trimmedLine.startsWith('#')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match ARG instructions
|
||||||
|
const argMatch = trimmedLine.match(/^ARG\s+([^\s=]+)(?:=(.*))?$/i);
|
||||||
|
if (argMatch) {
|
||||||
|
const argName = argMatch[1];
|
||||||
|
const argValue = argMatch[2] !== undefined ? argMatch[2] : process.env[argName] || '';
|
||||||
|
args[argName] = argValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match FROM instructions
|
||||||
|
const fromMatch = trimmedLine.match(/^FROM\s+(.+?)(?:\s+AS\s+[^\s]+)?$/i);
|
||||||
|
if (fromMatch) {
|
||||||
|
let baseImage = fromMatch[1].trim();
|
||||||
|
|
||||||
|
// Substitute variables in the base image name
|
||||||
|
baseImage = Dockerfile.substituteVariables(baseImage, args);
|
||||||
|
|
||||||
|
return baseImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('No FROM instruction found in Dockerfile');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substitutes variables in a string, supporting default values like ${VAR:-default}
|
||||||
|
*/
|
||||||
|
private static substituteVariables(str: string, vars: { [key: string]: string }): string {
|
||||||
|
return str.replace(/\${([^}:]+)(:-([^}]+))?}/g, (_, varName, __, defaultValue) => {
|
||||||
|
if (vars[varName] !== undefined) {
|
||||||
|
return vars[varName];
|
||||||
|
} else if (defaultValue !== undefined) {
|
||||||
|
return defaultValue;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the repo:version part from a full image reference, stripping any registry prefix.
|
||||||
|
* Examples:
|
||||||
|
* "registry.example.com/repo:version" -> "repo:version"
|
||||||
|
* "repo:version" -> "repo:version"
|
||||||
|
* "host.today/ht-docker-node:npmci" -> "ht-docker-node:npmci"
|
||||||
|
*/
|
||||||
|
private static extractRepoVersion(imageRef: string): string {
|
||||||
|
const parts = imageRef.split('/');
|
||||||
|
if (parts.length === 1) {
|
||||||
|
// No registry prefix: "repo:version"
|
||||||
|
return imageRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if first part looks like a registry (contains '.' or ':' or is 'localhost')
|
||||||
|
const firstPart = parts[0];
|
||||||
|
const looksLikeRegistry =
|
||||||
|
firstPart.includes('.') || firstPart.includes(':') || firstPart === 'localhost';
|
||||||
|
|
||||||
|
if (looksLikeRegistry) {
|
||||||
|
// Strip registry: "registry.example.com/repo:version" -> "repo:version"
|
||||||
|
return parts.slice(1).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// No registry prefix, could be "org/repo:version"
|
||||||
|
return imageRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the docker tag string for a given registry and repo
|
||||||
|
*/
|
||||||
|
public static getDockerTagString(
|
||||||
|
managerRef: TsDockerManager,
|
||||||
|
registryArg: string,
|
||||||
|
repoArg: string,
|
||||||
|
versionArg: string,
|
||||||
|
suffixArg?: string
|
||||||
|
): string {
|
||||||
|
// Determine whether the repo should be mapped according to the registry
|
||||||
|
const config = managerRef.config;
|
||||||
|
const mappedRepo = config.registryRepoMap?.[registryArg];
|
||||||
|
const repo = mappedRepo || repoArg;
|
||||||
|
|
||||||
|
// Determine whether the version contains a suffix
|
||||||
|
let version = versionArg;
|
||||||
|
if (suffixArg) {
|
||||||
|
version = versionArg + '_' + suffixArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagString = `${registryArg}/${repo}:${version}`;
|
||||||
|
return tagString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets build args from environment variable mapping
|
||||||
|
*/
|
||||||
|
public static async getDockerBuildArgs(managerRef: TsDockerManager): Promise<string> {
|
||||||
|
logger.log('info', 'checking for env vars to be supplied to the docker build');
|
||||||
|
let buildArgsString: string = '';
|
||||||
|
const config = managerRef.config;
|
||||||
|
|
||||||
|
if (config.buildArgEnvMap) {
|
||||||
|
for (const dockerArgKey of Object.keys(config.buildArgEnvMap)) {
|
||||||
|
const dockerArgOuterEnvVar = config.buildArgEnvMap[dockerArgKey];
|
||||||
|
logger.log(
|
||||||
|
'note',
|
||||||
|
`docker ARG "${dockerArgKey}" maps to outer env var "${dockerArgOuterEnvVar}"`
|
||||||
|
);
|
||||||
|
const targetValue = process.env[dockerArgOuterEnvVar];
|
||||||
|
if (targetValue) {
|
||||||
|
buildArgsString = `${buildArgsString} --build-arg ${dockerArgKey}="${targetValue}"`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buildArgsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INSTANCE PROPERTIES
|
||||||
|
public managerRef: TsDockerManager;
|
||||||
|
public filePath!: string;
|
||||||
|
public repo: string;
|
||||||
|
public version: string;
|
||||||
|
public cleanTag: string;
|
||||||
|
public buildTag: string;
|
||||||
|
public pushTag!: string;
|
||||||
|
public containerName: string;
|
||||||
|
public content!: string;
|
||||||
|
public baseImage: string;
|
||||||
|
public localBaseImageDependent: boolean;
|
||||||
|
public localBaseDockerfile!: Dockerfile;
|
||||||
|
|
||||||
|
constructor(managerRefArg: TsDockerManager, options: IDockerfileOptions) {
|
||||||
|
this.managerRef = managerRefArg;
|
||||||
|
this.filePath = options.filePath!;
|
||||||
|
|
||||||
|
// Build repo name from project info or directory name
|
||||||
|
const projectInfo = this.managerRef.projectInfo;
|
||||||
|
if (projectInfo?.npm?.name) {
|
||||||
|
// Use package name, removing scope if present
|
||||||
|
const packageName = projectInfo.npm.name.replace(/^@[^/]+\//, '');
|
||||||
|
this.repo = packageName;
|
||||||
|
} else {
|
||||||
|
// Fallback to directory name
|
||||||
|
this.repo = plugins.path.basename(paths.cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.version = Dockerfile.dockerFileVersion(this, plugins.path.parse(this.filePath).base);
|
||||||
|
this.cleanTag = this.repo + ':' + this.version;
|
||||||
|
this.buildTag = this.cleanTag;
|
||||||
|
this.containerName = 'dockerfile-' + this.version;
|
||||||
|
|
||||||
|
if (options.filePath && options.read) {
|
||||||
|
this.content = fs.readFileSync(plugins.path.resolve(options.filePath), 'utf-8');
|
||||||
|
} else if (options.fileContents) {
|
||||||
|
this.content = options.fileContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.baseImage = Dockerfile.dockerBaseImage(this.content);
|
||||||
|
this.localBaseImageDependent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the Dockerfile
|
||||||
|
*/
|
||||||
|
public async build(options?: { platform?: string; timeout?: number; noCache?: boolean }): Promise<void> {
|
||||||
|
logger.log('info', 'now building Dockerfile for ' + this.cleanTag);
|
||||||
|
const buildArgsString = await Dockerfile.getDockerBuildArgs(this.managerRef);
|
||||||
|
const config = this.managerRef.config;
|
||||||
|
const platformOverride = options?.platform;
|
||||||
|
const timeout = options?.timeout;
|
||||||
|
const noCacheFlag = options?.noCache ? ' --no-cache' : '';
|
||||||
|
|
||||||
|
let buildCommand: string;
|
||||||
|
|
||||||
|
if (platformOverride) {
|
||||||
|
// Single platform override via buildx
|
||||||
|
buildCommand = `docker buildx build --platform ${platformOverride}${noCacheFlag} --load -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
|
||||||
|
} else if (config.platforms && config.platforms.length > 1) {
|
||||||
|
// Multi-platform build using buildx
|
||||||
|
const platformString = config.platforms.join(',');
|
||||||
|
buildCommand = `docker buildx build --platform ${platformString}${noCacheFlag} -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
|
||||||
|
|
||||||
|
if (config.push) {
|
||||||
|
buildCommand += ' --push';
|
||||||
|
} else {
|
||||||
|
buildCommand += ' --load';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Standard build
|
||||||
|
const versionLabel = this.managerRef.projectInfo?.npm?.version || 'unknown';
|
||||||
|
buildCommand = `docker build --label="version=${versionLabel}"${noCacheFlag} -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
// Use streaming execution with timeout
|
||||||
|
const streaming = await smartshellInstance.execStreaming(buildCommand);
|
||||||
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
streaming.childProcess.kill();
|
||||||
|
reject(new Error(`Build timed out after ${timeout}s for ${this.cleanTag}`));
|
||||||
|
}, timeout * 1000);
|
||||||
|
});
|
||||||
|
const result = await Promise.race([streaming.finalPromise, timeoutPromise]);
|
||||||
|
if (result.exitCode !== 0) {
|
||||||
|
logger.log('error', `Build failed for ${this.cleanTag}`);
|
||||||
|
throw new Error(`Build failed for ${this.cleanTag}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const result = await smartshellInstance.exec(buildCommand);
|
||||||
|
if (result.exitCode !== 0) {
|
||||||
|
logger.log('error', `Build failed for ${this.cleanTag}`);
|
||||||
|
console.log(result.stdout);
|
||||||
|
throw new Error(`Build failed for ${this.cleanTag}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('ok', `Built ${this.cleanTag}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the Dockerfile to a registry
|
||||||
|
*/
|
||||||
|
public async push(dockerRegistryArg: DockerRegistry, versionSuffix?: string): Promise<void> {
|
||||||
|
this.pushTag = Dockerfile.getDockerTagString(
|
||||||
|
this.managerRef,
|
||||||
|
dockerRegistryArg.registryUrl,
|
||||||
|
this.repo,
|
||||||
|
this.version,
|
||||||
|
versionSuffix
|
||||||
|
);
|
||||||
|
|
||||||
|
await smartshellInstance.exec(`docker tag ${this.buildTag} ${this.pushTag}`);
|
||||||
|
const pushResult = await smartshellInstance.exec(`docker push ${this.pushTag}`);
|
||||||
|
|
||||||
|
if (pushResult.exitCode !== 0) {
|
||||||
|
logger.log('error', `Push failed for ${this.pushTag}`);
|
||||||
|
throw new Error(`Push failed for ${this.pushTag}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get image digest
|
||||||
|
const inspectResult = await smartshellInstance.exec(
|
||||||
|
`docker inspect --format="{{index .RepoDigests 0}}" ${this.pushTag}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (inspectResult.exitCode === 0 && inspectResult.stdout.includes('@')) {
|
||||||
|
const imageDigest = inspectResult.stdout.split('@')[1]?.trim();
|
||||||
|
console.log(`The image ${this.pushTag} has digest ${imageDigest}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('ok', `Pushed ${this.pushTag}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pulls the Dockerfile from a registry
|
||||||
|
*/
|
||||||
|
public async pull(registryArg: DockerRegistry, versionSuffixArg?: string): Promise<void> {
|
||||||
|
const pullTag = Dockerfile.getDockerTagString(
|
||||||
|
this.managerRef,
|
||||||
|
registryArg.registryUrl,
|
||||||
|
this.repo,
|
||||||
|
this.version,
|
||||||
|
versionSuffixArg
|
||||||
|
);
|
||||||
|
|
||||||
|
await smartshellInstance.exec(`docker pull ${pullTag}`);
|
||||||
|
await smartshellInstance.exec(`docker tag ${pullTag} ${this.buildTag}`);
|
||||||
|
|
||||||
|
logger.log('ok', `Pulled and tagged ${pullTag} as ${this.buildTag}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the Dockerfile by running a test script if it exists
|
||||||
|
*/
|
||||||
|
public async test(): Promise<void> {
|
||||||
|
const testDir = this.managerRef.config.testDir || plugins.path.join(paths.cwd, 'test');
|
||||||
|
const testFile = plugins.path.join(testDir, 'test_' + this.version + '.sh');
|
||||||
|
|
||||||
|
const testFileExists = fs.existsSync(testFile);
|
||||||
|
|
||||||
|
if (testFileExists) {
|
||||||
|
logger.log('info', `Running tests for ${this.cleanTag}`);
|
||||||
|
|
||||||
|
// Run tests in container
|
||||||
|
await smartshellInstance.exec(
|
||||||
|
`docker run --name tsdocker_test_container --entrypoint="bash" ${this.buildTag} -c "mkdir /tsdocker_test"`
|
||||||
|
);
|
||||||
|
await smartshellInstance.exec(`docker cp ${testFile} tsdocker_test_container:/tsdocker_test/test.sh`);
|
||||||
|
await smartshellInstance.exec(`docker commit tsdocker_test_container tsdocker_test_image`);
|
||||||
|
|
||||||
|
const testResult = await smartshellInstance.exec(
|
||||||
|
`docker run --entrypoint="bash" tsdocker_test_image -x /tsdocker_test/test.sh`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await smartshellInstance.exec(`docker rm tsdocker_test_container`);
|
||||||
|
await smartshellInstance.exec(`docker rmi --force tsdocker_test_image`);
|
||||||
|
|
||||||
|
if (testResult.exitCode !== 0) {
|
||||||
|
throw new Error(`Tests failed for ${this.cleanTag}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('ok', `Tests passed for ${this.cleanTag}`);
|
||||||
|
} else {
|
||||||
|
logger.log('warn', `Skipping tests for ${this.cleanTag} because no test file was found at ${testFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ID of a built Docker image
|
||||||
|
*/
|
||||||
|
public async getId(): Promise<string> {
|
||||||
|
const result = await smartshellInstance.exec(
|
||||||
|
'docker inspect --type=image --format="{{.Id}}" ' + this.buildTag
|
||||||
|
);
|
||||||
|
return result.stdout.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
91
ts/classes.dockerregistry.ts
Normal file
91
ts/classes.dockerregistry.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
|
import { logger } from './tsdocker.logging.js';
|
||||||
|
import type { IDockerRegistryOptions } from './interfaces/index.js';
|
||||||
|
|
||||||
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Docker registry with authentication capabilities
|
||||||
|
*/
|
||||||
|
export class DockerRegistry {
|
||||||
|
public registryUrl: string;
|
||||||
|
public username: string;
|
||||||
|
public password: string;
|
||||||
|
|
||||||
|
constructor(optionsArg: IDockerRegistryOptions) {
|
||||||
|
this.registryUrl = optionsArg.registryUrl;
|
||||||
|
this.username = optionsArg.username;
|
||||||
|
this.password = optionsArg.password;
|
||||||
|
logger.log('info', `created DockerRegistry for ${this.registryUrl}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DockerRegistry instance from a pipe-delimited environment string
|
||||||
|
* Format: "registryUrl|username|password"
|
||||||
|
*/
|
||||||
|
public static fromEnvString(envString: string): DockerRegistry {
|
||||||
|
const dockerRegexResultArray = envString.split('|');
|
||||||
|
if (dockerRegexResultArray.length !== 3) {
|
||||||
|
logger.log('error', 'malformed docker env var...');
|
||||||
|
throw new Error('malformed docker env var, expected format: registryUrl|username|password');
|
||||||
|
}
|
||||||
|
const registryUrl = dockerRegexResultArray[0].replace('https://', '').replace('http://', '');
|
||||||
|
const username = dockerRegexResultArray[1];
|
||||||
|
const password = dockerRegexResultArray[2];
|
||||||
|
return new DockerRegistry({
|
||||||
|
registryUrl: registryUrl,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DockerRegistry from environment variables
|
||||||
|
* Looks for DOCKER_REGISTRY, DOCKER_REGISTRY_USER, DOCKER_REGISTRY_PASSWORD
|
||||||
|
* Or for a specific registry: DOCKER_REGISTRY_<NAME>, etc.
|
||||||
|
*/
|
||||||
|
public static fromEnv(registryName?: string): DockerRegistry | null {
|
||||||
|
const prefix = registryName ? `DOCKER_REGISTRY_${registryName.toUpperCase()}_` : 'DOCKER_REGISTRY_';
|
||||||
|
|
||||||
|
const registryUrl = process.env[`${prefix}URL`] || process.env['DOCKER_REGISTRY'];
|
||||||
|
const username = process.env[`${prefix}USER`] || process.env['DOCKER_REGISTRY_USER'];
|
||||||
|
const password = process.env[`${prefix}PASSWORD`] || process.env['DOCKER_REGISTRY_PASSWORD'];
|
||||||
|
|
||||||
|
if (!registryUrl || !username || !password) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DockerRegistry({
|
||||||
|
registryUrl: registryUrl.replace('https://', '').replace('http://', ''),
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs in to the Docker registry
|
||||||
|
*/
|
||||||
|
public async login(): Promise<void> {
|
||||||
|
if (this.registryUrl === 'docker.io') {
|
||||||
|
await smartshellInstance.exec(`docker login -u ${this.username} -p ${this.password}`);
|
||||||
|
logger.log('info', 'Logged in to standard docker hub');
|
||||||
|
} else {
|
||||||
|
await smartshellInstance.exec(`docker login -u ${this.username} -p ${this.password} ${this.registryUrl}`);
|
||||||
|
}
|
||||||
|
logger.log('ok', `docker authenticated for ${this.registryUrl}!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs out from the Docker registry
|
||||||
|
*/
|
||||||
|
public async logout(): Promise<void> {
|
||||||
|
if (this.registryUrl === 'docker.io') {
|
||||||
|
await smartshellInstance.exec('docker logout');
|
||||||
|
} else {
|
||||||
|
await smartshellInstance.exec(`docker logout ${this.registryUrl}`);
|
||||||
|
}
|
||||||
|
logger.log('info', `logged out from ${this.registryUrl}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
83
ts/classes.registrystorage.ts
Normal file
83
ts/classes.registrystorage.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
|
import { logger } from './tsdocker.logging.js';
|
||||||
|
import { DockerRegistry } from './classes.dockerregistry.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage class for managing multiple Docker registries
|
||||||
|
*/
|
||||||
|
export class RegistryStorage {
|
||||||
|
public objectMap = new plugins.lik.ObjectMap<DockerRegistry>();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a registry to the storage
|
||||||
|
*/
|
||||||
|
public addRegistry(registryArg: DockerRegistry): void {
|
||||||
|
this.objectMap.add(registryArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a registry by its URL
|
||||||
|
*/
|
||||||
|
public getRegistryByUrl(registryUrlArg: string): DockerRegistry | undefined {
|
||||||
|
return this.objectMap.findSync((registryArg) => {
|
||||||
|
return registryArg.registryUrl === registryUrlArg;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all registries
|
||||||
|
*/
|
||||||
|
public getAllRegistries(): DockerRegistry[] {
|
||||||
|
return this.objectMap.getArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs in to all registries
|
||||||
|
*/
|
||||||
|
public async loginAll(): Promise<void> {
|
||||||
|
await this.objectMap.forEach(async (registryArg) => {
|
||||||
|
await registryArg.login();
|
||||||
|
});
|
||||||
|
logger.log('success', 'logged in successfully into all available DockerRegistries!');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs out from all registries
|
||||||
|
*/
|
||||||
|
public async logoutAll(): Promise<void> {
|
||||||
|
await this.objectMap.forEach(async (registryArg) => {
|
||||||
|
await registryArg.logout();
|
||||||
|
});
|
||||||
|
logger.log('info', 'logged out from all DockerRegistries');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads registries from environment variables
|
||||||
|
* Looks for DOCKER_REGISTRY_1, DOCKER_REGISTRY_2, etc. (pipe-delimited format)
|
||||||
|
* Or individual registries like DOCKER_REGISTRY_GITLAB_URL, etc.
|
||||||
|
*/
|
||||||
|
public loadFromEnv(): void {
|
||||||
|
// Check for numbered registry env vars (pipe-delimited format)
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
const envVar = process.env[`DOCKER_REGISTRY_${i}`];
|
||||||
|
if (envVar) {
|
||||||
|
try {
|
||||||
|
const registry = DockerRegistry.fromEnvString(envVar);
|
||||||
|
this.addRegistry(registry);
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('warn', `Failed to parse DOCKER_REGISTRY_${i}: ${(err as Error).message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for default registry
|
||||||
|
const defaultRegistry = DockerRegistry.fromEnv();
|
||||||
|
if (defaultRegistry) {
|
||||||
|
this.addRegistry(defaultRegistry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
303
ts/classes.tsdockermanager.ts
Normal file
303
ts/classes.tsdockermanager.ts
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
|
import * as paths from './tsdocker.paths.js';
|
||||||
|
import { logger } from './tsdocker.logging.js';
|
||||||
|
import { Dockerfile } from './classes.dockerfile.js';
|
||||||
|
import { DockerRegistry } from './classes.dockerregistry.js';
|
||||||
|
import { RegistryStorage } from './classes.registrystorage.js';
|
||||||
|
import type { ITsDockerConfig, IBuildCommandOptions } from './interfaces/index.js';
|
||||||
|
|
||||||
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main orchestrator class for Docker operations
|
||||||
|
*/
|
||||||
|
export class TsDockerManager {
|
||||||
|
public registryStorage: RegistryStorage;
|
||||||
|
public config: ITsDockerConfig;
|
||||||
|
public projectInfo: any;
|
||||||
|
private dockerfiles: Dockerfile[] = [];
|
||||||
|
|
||||||
|
constructor(config: ITsDockerConfig) {
|
||||||
|
this.config = config;
|
||||||
|
this.registryStorage = new RegistryStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the manager by loading project info and registries
|
||||||
|
*/
|
||||||
|
public async prepare(): Promise<void> {
|
||||||
|
// Load project info
|
||||||
|
try {
|
||||||
|
const projectinfoInstance = new plugins.projectinfo.ProjectInfo(paths.cwd);
|
||||||
|
this.projectInfo = {
|
||||||
|
npm: {
|
||||||
|
name: projectinfoInstance.npm.name,
|
||||||
|
version: projectinfoInstance.npm.version,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('warn', 'Could not load project info');
|
||||||
|
this.projectInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load registries from environment
|
||||||
|
this.registryStorage.loadFromEnv();
|
||||||
|
|
||||||
|
// Add registries from config if specified
|
||||||
|
if (this.config.registries) {
|
||||||
|
for (const registryUrl of this.config.registries) {
|
||||||
|
// Check if already loaded from env
|
||||||
|
if (!this.registryStorage.getRegistryByUrl(registryUrl)) {
|
||||||
|
// Try to load credentials for this registry from env
|
||||||
|
const envVarName = registryUrl.replace(/\./g, '_').toUpperCase();
|
||||||
|
const envString = process.env[`DOCKER_REGISTRY_${envVarName}`];
|
||||||
|
if (envString) {
|
||||||
|
try {
|
||||||
|
const registry = DockerRegistry.fromEnvString(envString);
|
||||||
|
this.registryStorage.addRegistry(registry);
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('warn', `Could not load credentials for registry ${registryUrl}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', `Prepared TsDockerManager with ${this.registryStorage.getAllRegistries().length} registries`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs in to all configured registries
|
||||||
|
*/
|
||||||
|
public async login(): Promise<void> {
|
||||||
|
if (this.registryStorage.getAllRegistries().length === 0) {
|
||||||
|
logger.log('warn', 'No registries configured');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.registryStorage.loginAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers and sorts Dockerfiles in the current directory
|
||||||
|
*/
|
||||||
|
public async discoverDockerfiles(): Promise<Dockerfile[]> {
|
||||||
|
this.dockerfiles = await Dockerfile.readDockerfiles(this);
|
||||||
|
this.dockerfiles = await Dockerfile.sortDockerfiles(this.dockerfiles);
|
||||||
|
this.dockerfiles = await Dockerfile.mapDockerfiles(this.dockerfiles);
|
||||||
|
return this.dockerfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds discovered Dockerfiles in dependency order.
|
||||||
|
* When options.patterns is provided, only matching Dockerfiles (and their dependencies) are built.
|
||||||
|
*/
|
||||||
|
public async build(options?: IBuildCommandOptions): Promise<Dockerfile[]> {
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
await this.discoverDockerfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
logger.log('warn', 'No Dockerfiles found');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine which Dockerfiles to build
|
||||||
|
let toBuild = this.dockerfiles;
|
||||||
|
|
||||||
|
if (options?.patterns && options.patterns.length > 0) {
|
||||||
|
// Filter to matching Dockerfiles
|
||||||
|
const matched = this.dockerfiles.filter((df) => {
|
||||||
|
const basename = plugins.path.basename(df.filePath);
|
||||||
|
return options.patterns!.some((pattern) => {
|
||||||
|
if (pattern.includes('*') || pattern.includes('?')) {
|
||||||
|
// Convert glob pattern to regex
|
||||||
|
const regexStr = '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$';
|
||||||
|
return new RegExp(regexStr).test(basename);
|
||||||
|
}
|
||||||
|
return basename === pattern;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matched.length === 0) {
|
||||||
|
logger.log('warn', `No Dockerfiles matched patterns: ${options.patterns.join(', ')}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve dependency chain and preserve topological order
|
||||||
|
toBuild = this.resolveWithDependencies(matched, this.dockerfiles);
|
||||||
|
logger.log('info', `Matched ${matched.length} Dockerfile(s), building ${toBuild.length} (including dependencies)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if buildx is needed
|
||||||
|
if (options?.platform || (this.config.platforms && this.config.platforms.length > 1)) {
|
||||||
|
await this.ensureBuildx();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', `Building ${toBuild.length} Dockerfiles...`);
|
||||||
|
await Dockerfile.buildDockerfiles(toBuild, {
|
||||||
|
platform: options?.platform,
|
||||||
|
timeout: options?.timeout,
|
||||||
|
noCache: options?.noCache,
|
||||||
|
});
|
||||||
|
logger.log('success', 'All Dockerfiles built successfully');
|
||||||
|
|
||||||
|
return toBuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a set of target Dockerfiles to include all their local base image dependencies,
|
||||||
|
* preserving the original topological build order.
|
||||||
|
*/
|
||||||
|
private resolveWithDependencies(targets: Dockerfile[], allSorted: Dockerfile[]): Dockerfile[] {
|
||||||
|
const needed = new Set<Dockerfile>();
|
||||||
|
const addWithDeps = (df: Dockerfile) => {
|
||||||
|
if (needed.has(df)) return;
|
||||||
|
needed.add(df);
|
||||||
|
if (df.localBaseImageDependent && df.localBaseDockerfile) {
|
||||||
|
addWithDeps(df.localBaseDockerfile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const df of targets) addWithDeps(df);
|
||||||
|
return allSorted.filter((df) => needed.has(df));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures Docker buildx is set up for multi-architecture builds
|
||||||
|
*/
|
||||||
|
private async ensureBuildx(): Promise<void> {
|
||||||
|
logger.log('info', 'Setting up Docker buildx for multi-platform builds...');
|
||||||
|
|
||||||
|
// Check if a buildx builder exists
|
||||||
|
const inspectResult = await smartshellInstance.exec('docker buildx inspect tsdocker-builder 2>/dev/null');
|
||||||
|
|
||||||
|
if (inspectResult.exitCode !== 0) {
|
||||||
|
// Create a new buildx builder
|
||||||
|
logger.log('info', 'Creating new buildx builder...');
|
||||||
|
await smartshellInstance.exec('docker buildx create --name tsdocker-builder --use');
|
||||||
|
await smartshellInstance.exec('docker buildx inspect --bootstrap');
|
||||||
|
} else {
|
||||||
|
// Use existing builder
|
||||||
|
await smartshellInstance.exec('docker buildx use tsdocker-builder');
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('ok', 'Docker buildx ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes all built images to specified registries
|
||||||
|
*/
|
||||||
|
public async push(registryUrls?: string[]): Promise<void> {
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
await this.discoverDockerfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
logger.log('warn', 'No Dockerfiles found to push');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine which registries to push to
|
||||||
|
let registriesToPush: DockerRegistry[] = [];
|
||||||
|
|
||||||
|
if (registryUrls && registryUrls.length > 0) {
|
||||||
|
// Push to specified registries
|
||||||
|
for (const url of registryUrls) {
|
||||||
|
const registry = this.registryStorage.getRegistryByUrl(url);
|
||||||
|
if (registry) {
|
||||||
|
registriesToPush.push(registry);
|
||||||
|
} else {
|
||||||
|
logger.log('warn', `Registry ${url} not found in storage`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Push to all configured registries
|
||||||
|
registriesToPush = this.registryStorage.getAllRegistries();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registriesToPush.length === 0) {
|
||||||
|
logger.log('warn', 'No registries available to push to');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push each Dockerfile to each registry
|
||||||
|
for (const dockerfile of this.dockerfiles) {
|
||||||
|
for (const registry of registriesToPush) {
|
||||||
|
await dockerfile.push(registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('success', 'All images pushed successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pulls images from a specified registry
|
||||||
|
*/
|
||||||
|
public async pull(registryUrl: string): Promise<void> {
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
await this.discoverDockerfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
const registry = this.registryStorage.getRegistryByUrl(registryUrl);
|
||||||
|
if (!registry) {
|
||||||
|
throw new Error(`Registry ${registryUrl} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const dockerfile of this.dockerfiles) {
|
||||||
|
await dockerfile.pull(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('success', 'All images pulled successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs tests for all Dockerfiles
|
||||||
|
*/
|
||||||
|
public async test(): Promise<void> {
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
await this.discoverDockerfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
logger.log('warn', 'No Dockerfiles found to test');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Dockerfile.testDockerfiles(this.dockerfiles);
|
||||||
|
logger.log('success', 'All tests completed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all discovered Dockerfiles and their info
|
||||||
|
*/
|
||||||
|
public async list(): Promise<Dockerfile[]> {
|
||||||
|
if (this.dockerfiles.length === 0) {
|
||||||
|
await this.discoverDockerfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nDiscovered Dockerfiles:');
|
||||||
|
console.log('========================\n');
|
||||||
|
|
||||||
|
for (let i = 0; i < this.dockerfiles.length; i++) {
|
||||||
|
const df = this.dockerfiles[i];
|
||||||
|
console.log(`${i + 1}. ${df.filePath}`);
|
||||||
|
console.log(` Tag: ${df.cleanTag}`);
|
||||||
|
console.log(` Base Image: ${df.baseImage}`);
|
||||||
|
console.log(` Version: ${df.version}`);
|
||||||
|
if (df.localBaseImageDependent) {
|
||||||
|
console.log(` Depends on: ${df.localBaseDockerfile?.cleanTag}`);
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dockerfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the cached Dockerfiles (after discovery)
|
||||||
|
*/
|
||||||
|
public getDockerfiles(): Dockerfile[] {
|
||||||
|
return this.dockerfiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
import * as cli from './tsdocker.cli';
|
import * as cli from './tsdocker.cli.js';
|
||||||
|
|
||||||
cli.run();
|
cli.run();
|
||||||
|
|||||||
80
ts/interfaces/index.ts
Normal file
80
ts/interfaces/index.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Configuration interface for tsdocker
|
||||||
|
* Extends legacy config with new Docker build capabilities
|
||||||
|
*/
|
||||||
|
export interface ITsDockerConfig {
|
||||||
|
// Legacy (backward compatible)
|
||||||
|
baseImage: string;
|
||||||
|
command: string;
|
||||||
|
dockerSock: boolean;
|
||||||
|
keyValueObject: { [key: string]: any };
|
||||||
|
|
||||||
|
// New Docker build config
|
||||||
|
registries?: string[];
|
||||||
|
registryRepoMap?: { [registry: string]: string };
|
||||||
|
buildArgEnvMap?: { [dockerArg: string]: string };
|
||||||
|
platforms?: string[]; // ['linux/amd64', 'linux/arm64']
|
||||||
|
push?: boolean;
|
||||||
|
testDir?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for constructing a DockerRegistry
|
||||||
|
*/
|
||||||
|
export interface IDockerRegistryOptions {
|
||||||
|
registryUrl: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a discovered Dockerfile
|
||||||
|
*/
|
||||||
|
export interface IDockerfileInfo {
|
||||||
|
filePath: string;
|
||||||
|
fileName: string;
|
||||||
|
version: string;
|
||||||
|
baseImage: string;
|
||||||
|
buildTag: string;
|
||||||
|
localBaseImageDependent: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for creating a Dockerfile instance
|
||||||
|
*/
|
||||||
|
export interface IDockerfileOptions {
|
||||||
|
filePath?: string;
|
||||||
|
fileContents?: string;
|
||||||
|
read?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result from a Docker build operation
|
||||||
|
*/
|
||||||
|
export interface IBuildResult {
|
||||||
|
success: boolean;
|
||||||
|
tag: string;
|
||||||
|
duration?: number;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result from a Docker push operation
|
||||||
|
*/
|
||||||
|
export interface IPushResult {
|
||||||
|
success: boolean;
|
||||||
|
registry: string;
|
||||||
|
tag: string;
|
||||||
|
digest?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the build command
|
||||||
|
*/
|
||||||
|
export interface IBuildCommandOptions {
|
||||||
|
patterns?: string[]; // Dockerfile name patterns (e.g., ['Dockerfile_base', 'Dockerfile_*'])
|
||||||
|
platform?: string; // Single platform override (e.g., 'linux/arm64')
|
||||||
|
timeout?: number; // Build timeout in seconds
|
||||||
|
noCache?: boolean; // Force rebuild without Docker layer cache (--no-cache)
|
||||||
|
}
|
||||||
@@ -1,16 +1,21 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
import * as paths from './tsdocker.paths';
|
import * as paths from './tsdocker.paths.js';
|
||||||
|
|
||||||
// modules
|
// modules
|
||||||
import * as ConfigModule from './tsdocker.config';
|
import * as ConfigModule from './tsdocker.config.js';
|
||||||
import * as DockerModule from './tsdocker.docker';
|
import * as DockerModule from './tsdocker.docker.js';
|
||||||
|
|
||||||
import { logger, ora } from './tsdocker.logging';
|
import { logger, ora } from './tsdocker.logging.js';
|
||||||
|
import { TsDockerManager } from './classes.tsdockermanager.js';
|
||||||
|
import type { IBuildCommandOptions } from './interfaces/index.js';
|
||||||
|
import { commitinfo } from './00_commitinfo_data.js';
|
||||||
|
|
||||||
const tsdockerCli = new plugins.smartcli.Smartcli();
|
const tsdockerCli = new plugins.smartcli.Smartcli();
|
||||||
|
tsdockerCli.addVersion(commitinfo.version);
|
||||||
|
|
||||||
export let run = () => {
|
export let run = () => {
|
||||||
tsdockerCli.standardTask().subscribe(async argvArg => {
|
// Default command: run tests in container (legacy behavior)
|
||||||
|
tsdockerCli.standardCommand().subscribe(async argvArg => {
|
||||||
const configArg = await ConfigModule.run().then(DockerModule.run);
|
const configArg = await ConfigModule.run().then(DockerModule.run);
|
||||||
if (configArg.exitCode === 0) {
|
if (configArg.exitCode === 0) {
|
||||||
logger.log('success', 'container ended all right!');
|
logger.log('success', 'container ended all right!');
|
||||||
@@ -20,6 +25,165 @@ export let run = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Dockerfiles in dependency order
|
||||||
|
* Usage: tsdocker build [Dockerfile_patterns...] [--platform=linux/arm64] [--timeout=600]
|
||||||
|
*/
|
||||||
|
tsdockerCli.addCommand('build').subscribe(async argvArg => {
|
||||||
|
try {
|
||||||
|
const config = await ConfigModule.run();
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
|
||||||
|
const buildOptions: IBuildCommandOptions = {};
|
||||||
|
const patterns = argvArg._.slice(1) as string[];
|
||||||
|
if (patterns.length > 0) {
|
||||||
|
buildOptions.patterns = patterns;
|
||||||
|
}
|
||||||
|
if (argvArg.platform) {
|
||||||
|
buildOptions.platform = argvArg.platform as string;
|
||||||
|
}
|
||||||
|
if (argvArg.timeout) {
|
||||||
|
buildOptions.timeout = Number(argvArg.timeout);
|
||||||
|
}
|
||||||
|
if (argvArg.cache === false) {
|
||||||
|
buildOptions.noCache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await manager.build(buildOptions);
|
||||||
|
logger.log('success', 'Build completed successfully');
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error', `Build failed: ${(err as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push built images to configured registries
|
||||||
|
* Usage: tsdocker push [Dockerfile_patterns...] [--platform=linux/arm64] [--timeout=600] [--registry=url]
|
||||||
|
*/
|
||||||
|
tsdockerCli.addCommand('push').subscribe(async argvArg => {
|
||||||
|
try {
|
||||||
|
const config = await ConfigModule.run();
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
|
||||||
|
// Login first
|
||||||
|
await manager.login();
|
||||||
|
|
||||||
|
// Parse build options from positional args and flags
|
||||||
|
const buildOptions: IBuildCommandOptions = {};
|
||||||
|
const patterns = argvArg._.slice(1) as string[];
|
||||||
|
if (patterns.length > 0) {
|
||||||
|
buildOptions.patterns = patterns;
|
||||||
|
}
|
||||||
|
if (argvArg.platform) {
|
||||||
|
buildOptions.platform = argvArg.platform as string;
|
||||||
|
}
|
||||||
|
if (argvArg.timeout) {
|
||||||
|
buildOptions.timeout = Number(argvArg.timeout);
|
||||||
|
}
|
||||||
|
if (argvArg.cache === false) {
|
||||||
|
buildOptions.noCache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build images first (if not already built)
|
||||||
|
await manager.build(buildOptions);
|
||||||
|
|
||||||
|
// Get registry from --registry flag
|
||||||
|
const registryArg = argvArg.registry as string | undefined;
|
||||||
|
const registries = registryArg ? [registryArg] : undefined;
|
||||||
|
|
||||||
|
await manager.push(registries);
|
||||||
|
logger.log('success', 'Push completed successfully');
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error', `Push failed: ${(err as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull images from a specified registry
|
||||||
|
*/
|
||||||
|
tsdockerCli.addCommand('pull').subscribe(async argvArg => {
|
||||||
|
try {
|
||||||
|
const registryArg = argvArg._[1]; // e.g., tsdocker pull registry.gitlab.com
|
||||||
|
if (!registryArg) {
|
||||||
|
logger.log('error', 'Registry URL required. Usage: tsdocker pull <registry-url>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await ConfigModule.run();
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
|
||||||
|
// Login first
|
||||||
|
await manager.login();
|
||||||
|
|
||||||
|
await manager.pull(registryArg);
|
||||||
|
logger.log('success', 'Pull completed successfully');
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error', `Pull failed: ${(err as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run container tests for all Dockerfiles
|
||||||
|
*/
|
||||||
|
tsdockerCli.addCommand('test').subscribe(async argvArg => {
|
||||||
|
try {
|
||||||
|
const config = await ConfigModule.run();
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
|
||||||
|
// Build images first
|
||||||
|
const buildOptions: IBuildCommandOptions = {};
|
||||||
|
if (argvArg.cache === false) {
|
||||||
|
buildOptions.noCache = true;
|
||||||
|
}
|
||||||
|
await manager.build(buildOptions);
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
await manager.test();
|
||||||
|
logger.log('success', 'Tests completed successfully');
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error', `Tests failed: ${(err as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login to configured registries
|
||||||
|
*/
|
||||||
|
tsdockerCli.addCommand('login').subscribe(async argvArg => {
|
||||||
|
try {
|
||||||
|
const config = await ConfigModule.run();
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
await manager.login();
|
||||||
|
logger.log('success', 'Login completed successfully');
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error', `Login failed: ${(err as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List discovered Dockerfiles and their dependencies
|
||||||
|
*/
|
||||||
|
tsdockerCli.addCommand('list').subscribe(async argvArg => {
|
||||||
|
try {
|
||||||
|
const config = await ConfigModule.run();
|
||||||
|
const manager = new TsDockerManager(config);
|
||||||
|
await manager.prepare();
|
||||||
|
await manager.list();
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error', `List failed: ${(err as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this command is executed inside docker and meant for use from outside docker
|
* this command is executed inside docker and meant for use from outside docker
|
||||||
*/
|
*/
|
||||||
@@ -62,16 +226,6 @@ export let run = () => {
|
|||||||
ora.finishSuccess('docker environment now is clean!');
|
ora.finishSuccess('docker environment now is clean!');
|
||||||
});
|
});
|
||||||
|
|
||||||
tsdockerCli.addCommand('speedtest').subscribe(async argvArg => {
|
|
||||||
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
||||||
executor: 'bash'
|
|
||||||
});
|
|
||||||
logger.log('ok', 'Starting speedtest');
|
|
||||||
await smartshellInstance.exec(
|
|
||||||
`docker pull tianon/speedtest && docker run --rm tianon/speedtest --accept-license --accept-gdpr`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
tsdockerCli.addCommand('vscode').subscribe(async argvArg => {
|
tsdockerCli.addCommand('vscode').subscribe(async argvArg => {
|
||||||
const smartshellInstance = new plugins.smartshell.Smartshell({
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
executor: 'bash'
|
executor: 'bash'
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
import * as paths from './tsdocker.paths';
|
import * as paths from './tsdocker.paths.js';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import type { ITsDockerConfig } from './interfaces/index.js';
|
||||||
|
|
||||||
export interface IConfig {
|
// Re-export ITsDockerConfig as IConfig for backward compatibility
|
||||||
baseImage: string;
|
export type IConfig = ITsDockerConfig & {
|
||||||
command: string;
|
|
||||||
dockerSock: boolean;
|
|
||||||
exitCode?: number;
|
exitCode?: number;
|
||||||
keyValueObject: {[key: string]: any};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const getQenvKeyValueObject = async () => {
|
const getQenvKeyValueObject = async () => {
|
||||||
let qenvKeyValueObjectArray: { [key: string]: string | number };
|
let qenvKeyValueObjectArray: { [key: string]: string | number };
|
||||||
@@ -22,12 +20,21 @@ const getQenvKeyValueObject = async () => {
|
|||||||
|
|
||||||
const buildConfig = async (qenvKeyValueObjectArg: { [key: string]: string | number }) => {
|
const buildConfig = async (qenvKeyValueObjectArg: { [key: string]: string | number }) => {
|
||||||
const npmextra = new plugins.npmextra.Npmextra(paths.cwd);
|
const npmextra = new plugins.npmextra.Npmextra(paths.cwd);
|
||||||
const config = npmextra.dataFor<IConfig>('npmdocker', {
|
const config = npmextra.dataFor<IConfig>('@git.zone/tsdocker', {
|
||||||
|
// Legacy options (backward compatible)
|
||||||
baseImage: 'hosttoday/ht-docker-node:npmdocker',
|
baseImage: 'hosttoday/ht-docker-node:npmdocker',
|
||||||
init: 'rm -rf node_nodules/ && yarn install',
|
init: 'rm -rf node_nodules/ && yarn install',
|
||||||
command: 'npmci npm test',
|
command: 'npmci npm test',
|
||||||
dockerSock: false,
|
dockerSock: false,
|
||||||
keyValueObject: qenvKeyValueObjectArg
|
keyValueObject: qenvKeyValueObjectArg,
|
||||||
|
|
||||||
|
// New Docker build options
|
||||||
|
registries: [],
|
||||||
|
registryRepoMap: {},
|
||||||
|
buildArgEnvMap: {},
|
||||||
|
platforms: ['linux/amd64'],
|
||||||
|
push: false,
|
||||||
|
testDir: undefined,
|
||||||
});
|
});
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
import * as paths from './tsdocker.paths';
|
import * as paths from './tsdocker.paths.js';
|
||||||
import * as snippets from './tsdocker.snippets';
|
import * as snippets from './tsdocker.snippets.js';
|
||||||
|
|
||||||
import { logger, ora } from './tsdocker.logging';
|
import { logger, ora } from './tsdocker.logging.js';
|
||||||
|
|
||||||
const smartshellInstance = new plugins.smartshell.Smartshell({
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
executor: 'bash'
|
executor: 'bash'
|
||||||
});
|
});
|
||||||
|
|
||||||
// interfaces
|
// interfaces
|
||||||
import { IConfig } from './tsdocker.config';
|
import type { IConfig } from './tsdocker.config.js';
|
||||||
|
|
||||||
let config: IConfig;
|
let config: IConfig;
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ const buildDockerImage = async () => {
|
|||||||
await smartshellInstance.exec(`docker pull ${config.baseImage}`);
|
await smartshellInstance.exec(`docker pull ${config.baseImage}`);
|
||||||
ora.text('building Dockerimage...');
|
ora.text('building Dockerimage...');
|
||||||
const execResult = await smartshellInstance.execSilent(
|
const execResult = await smartshellInstance.execSilent(
|
||||||
`docker build -f npmdocker -t ${dockerData.imageTag} ${paths.cwd}`
|
`docker build --load -f npmdocker -t ${dockerData.imageTag} ${paths.cwd}`
|
||||||
);
|
);
|
||||||
if (execResult.exitCode !== 0) {
|
if (execResult.exitCode !== 0) {
|
||||||
console.log(execResult.stdout);
|
console.log(execResult.stdout);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
|
|
||||||
export const logger = new plugins.smartlog.Smartlog({
|
export const logger = new plugins.smartlog.Smartlog({
|
||||||
logContext: {
|
logContext: {
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
// directories
|
// directories
|
||||||
export let cwd = process.cwd();
|
export let cwd = process.cwd();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// push.rocks scope
|
// push.rocks scope
|
||||||
|
import * as lik from '@push.rocks/lik';
|
||||||
import * as npmextra from '@push.rocks/npmextra';
|
import * as npmextra from '@push.rocks/npmextra';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as projectinfo from '@push.rocks/projectinfo';
|
import * as projectinfo from '@push.rocks/projectinfo';
|
||||||
@@ -17,6 +18,7 @@ import * as smartstring from '@push.rocks/smartstring';
|
|||||||
export const smartfs = new SmartFs(new SmartFsProviderNode());
|
export const smartfs = new SmartFs(new SmartFsProviderNode());
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
lik,
|
||||||
npmextra,
|
npmextra,
|
||||||
path,
|
path,
|
||||||
projectinfo,
|
projectinfo,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as plugins from './tsdocker.plugins';
|
import * as plugins from './tsdocker.plugins.js';
|
||||||
|
|
||||||
export interface IDockerfileSnippet {
|
export interface IDockerfileSnippet {
|
||||||
baseImage: string;
|
baseImage: string;
|
||||||
@@ -14,23 +14,20 @@ let getMountSolutionString = (optionsArg: IDockerfileSnippet) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let getGlobalPreparationString = (optionsArg: IDockerfileSnippet) => {
|
let getGlobalPreparationString = (optionsArg: IDockerfileSnippet) => {
|
||||||
if (optionsArg.baseImage !== 'hosttoday/ht-docker-node:npmdocker') {
|
// Always install tsdocker to ensure the latest version is available
|
||||||
return 'RUN npm install -g npmdocker';
|
return 'RUN npm install -g @git.zone/tsdocker';
|
||||||
} else {
|
|
||||||
return '# not installing npmdocker since it is included in the base image';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export let dockerfileSnippet = (optionsArg: IDockerfileSnippet): string => {
|
export let dockerfileSnippet = (optionsArg: IDockerfileSnippet): string => {
|
||||||
return plugins.smartstring.indent.normalize(
|
return plugins.smartstring.indent.normalize(
|
||||||
`
|
`
|
||||||
FROM ${optionsArg.baseImage}
|
FROM ${optionsArg.baseImage}
|
||||||
# For info about what npmdocker does read the docs at https://gitzone.github.io/npmdocker
|
# For info about what tsdocker does read the docs at https://gitzone.github.io/tsdocker
|
||||||
${getGlobalPreparationString(optionsArg)}
|
${getGlobalPreparationString(optionsArg)}
|
||||||
${getMountSolutionString(optionsArg)}
|
${getMountSolutionString(optionsArg)}
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
ENV CI=true
|
ENV CI=true
|
||||||
ENTRYPOINT ["npmdocker"]
|
ENTRYPOINT ["tsdocker"]
|
||||||
CMD ["runinside"]
|
CMD ["runinside"]
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user