fix(core): Stabilize CI/workflows and runtime: update CI images/metadata, improve streaming requests and image handling, and fix tests & package metadata
This commit is contained in:
		| @@ -6,8 +6,8 @@ on: | ||||
|       - '**' | ||||
|  | ||||
| env: | ||||
|   IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci | ||||
|   NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git | ||||
|   IMAGE: code.foss.global/host.today/ht-docker-node:npmci | ||||
|   NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git | ||||
|   NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}} | ||||
|   NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}} | ||||
|   NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}} | ||||
| @@ -26,7 +26,7 @@ jobs: | ||||
|       - name: Install pnpm and npmci | ||||
|         run: | | ||||
|           pnpm install -g pnpm | ||||
|           pnpm install -g @shipzone/npmci | ||||
|           pnpm install -g @ship.zone/npmci | ||||
|  | ||||
|       - name: Run npm prepare | ||||
|         run: npmci npm prepare | ||||
|   | ||||
| @@ -6,8 +6,8 @@ on: | ||||
|       - '*' | ||||
|  | ||||
| env: | ||||
|   IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci | ||||
|   NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git | ||||
|   IMAGE: code.foss.global/host.today/ht-docker-node:npmci | ||||
|   NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git | ||||
|   NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}} | ||||
|   NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}} | ||||
|   NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}} | ||||
| @@ -26,7 +26,7 @@ jobs: | ||||
|       - name: Prepare | ||||
|         run: | | ||||
|           pnpm install -g pnpm | ||||
|           pnpm install -g @shipzone/npmci | ||||
|           pnpm install -g @ship.zone/npmci | ||||
|           npmci npm prepare | ||||
|  | ||||
|       - name: Audit production dependencies | ||||
| @@ -54,7 +54,7 @@ jobs: | ||||
|       - name: Prepare | ||||
|         run: | | ||||
|           pnpm install -g pnpm | ||||
|           pnpm install -g @shipzone/npmci | ||||
|           pnpm install -g @ship.zone/npmci | ||||
|           npmci npm prepare | ||||
|  | ||||
|       - name: Test stable | ||||
| @@ -82,7 +82,7 @@ jobs: | ||||
|       - name: Prepare | ||||
|         run: | | ||||
|           pnpm install -g pnpm | ||||
|           pnpm install -g @shipzone/npmci | ||||
|           pnpm install -g @ship.zone/npmci | ||||
|           npmci npm prepare | ||||
|  | ||||
|       - name: Release | ||||
| @@ -104,7 +104,7 @@ jobs: | ||||
|       - name: Prepare | ||||
|         run: | | ||||
|           pnpm install -g pnpm | ||||
|           pnpm install -g @shipzone/npmci | ||||
|           pnpm install -g @ship.zone/npmci | ||||
|           npmci npm prepare | ||||
|  | ||||
|       - name: Code quality | ||||
|   | ||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,7 +3,6 @@ | ||||
| # artifacts | ||||
| coverage/ | ||||
| public/ | ||||
| pages/ | ||||
|  | ||||
| # installs | ||||
| node_modules/ | ||||
| @@ -17,4 +16,8 @@ node_modules/ | ||||
| dist/ | ||||
| dist_*/ | ||||
|  | ||||
| # custom | ||||
| # AI | ||||
| .claude/ | ||||
| .serena/ | ||||
|  | ||||
| #------# custom | ||||
							
								
								
									
										49
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,6 +1,18 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## 2025-08-19 - 1.3.5 - fix(core) | ||||
| Stabilize CI/workflows and runtime: update CI images/metadata, improve streaming requests and image handling, and fix tests & package metadata | ||||
|  | ||||
| - Update CI workflows and images: switch workflow IMAGE to code.foss.global/host.today/ht-docker-node:npmci, fix NPMCI_COMPUTED_REPOURL placeholders, and replace @shipzone/npmci with @ship.zone/npmci in workflows | ||||
| - Update npmextra.json gitzone metadata (githost -> code.foss.global, gitscope -> apiclient.xyz, npmPackagename -> @apiclient.xyz/docker) and npmdocker.baseImage -> host.today/ht-docker-node:npmci | ||||
| - Adjust package.json repository/bugs/homepage to code.foss.global, add pnpm overrides entry and normalize package metadata | ||||
| - Improve DockerHost streaming and request handling: reduce requestStreaming timeout to 30s, enable autoDrain for streaming requests, improve response parsing for streaming vs JSON endpoints to avoid hangs | ||||
| - Enhance DockerImage and DockerImageStore stream handling and tar processing: more robust import/export parsing, safer stream-to-file writes, repackaging steps, and error handling | ||||
| - Unskip and update tests: re-enable DockerImageStore integration test, change stored image name to 'hello2', add formatting fixes and ensure cleanup stops the test DockerHost | ||||
| - Miscellaneous code and docs cleanup: numerous formatting fixes and trailing-comma normalization across README and TS sources, update commitinfo and logger newline fixes, and add local tool ignores (.claude/.serena) to .gitignore | ||||
|  | ||||
| ## 2025-08-19 - 1.3.4 - fix(test) | ||||
|  | ||||
| Increase test timeout, enable DockerImageStore test, update test image name, bump smartrequest patch, and add local claude settings | ||||
|  | ||||
| - Increase tstest timeout from 120s to 600s in package.json to accommodate longer-running integration tests. | ||||
| @@ -9,6 +21,7 @@ Increase test timeout, enable DockerImageStore test, update test image name, bum | ||||
| - Add .claude/settings.local.json to allow local agent permissions for running tests and related tooling. | ||||
|  | ||||
| ## 2025-08-19 - 1.3.3 - fix(classes.host) | ||||
|  | ||||
| Adjust requestStreaming timeout and autoDrain; stabilize tests | ||||
|  | ||||
| - Reduced requestStreaming timeout from 10 minutes to 30 seconds to avoid long-running hanging requests. | ||||
| @@ -17,6 +30,7 @@ Adjust requestStreaming timeout and autoDrain; stabilize tests | ||||
| - Added local tool settings file (.claude/settings.local.json) with local permissions (development-only). | ||||
|  | ||||
| ## 2025-08-18 - 1.3.2 - fix(package.json) | ||||
|  | ||||
| Fix test script timeout typo, update dependency versions, and add typings & project configs | ||||
|  | ||||
| - Fix test script: correct 'tineout' -> 'timeout' for npm test command and set timeout to 120s | ||||
| @@ -26,6 +40,7 @@ Fix test script timeout typo, update dependency versions, and add typings & proj | ||||
| - Include generated cache/metadata files (typescript document symbols cache) — not source changes but tooling/cache artifacts | ||||
|  | ||||
| ## 2025-08-18 - 1.3.1 - fix(test) | ||||
|  | ||||
| Update test setup and devDependencies; adjust test import and add package metadata | ||||
|  | ||||
| - Update test script to run with additional flags: --verbose, --logfile and --tineout 120 | ||||
| @@ -35,26 +50,29 @@ Update test setup and devDependencies; adjust test import and add package metada | ||||
| - Add packageManager field for pnpm@10.14.0 with integrity hash | ||||
|  | ||||
| ## 2024-12-23 - 1.3.0 - feat(core) | ||||
|  | ||||
| Initial release of Docker client with TypeScript support | ||||
|  | ||||
| - Provides easy communication with Docker's remote API from Node.js | ||||
| - Includes implementations for managing Docker services, networks, secrets, containers, and images | ||||
|  | ||||
| ## 2024-12-23 - 1.2.8 - fix(core) | ||||
|  | ||||
| Improved the image creation process from tar stream in DockerImage class. | ||||
|  | ||||
| - Enhanced `DockerImage.createFromTarStream` method to handle streamed response and parse imported image details. | ||||
| - Fixed the dependency version for `@push.rocks/smartarchive` in package.json. | ||||
|  | ||||
| ## 2024-10-13 - 1.2.7 - fix(core) | ||||
|  | ||||
| Prepare patch release with minor fixes and improvements | ||||
|  | ||||
|  | ||||
| ## 2024-10-13 - 1.2.6 - fix(core) | ||||
|  | ||||
| Minor refactoring and code quality improvements. | ||||
|  | ||||
|  | ||||
| ## 2024-10-13 - 1.2.5 - fix(dependencies) | ||||
|  | ||||
| Update dependencies for stability improvements | ||||
|  | ||||
| - Updated @push.rocks/smartstream to version ^3.0.46 | ||||
| @@ -62,129 +80,152 @@ Update dependencies for stability improvements | ||||
| - Updated @types/node to version 22.7.5 | ||||
|  | ||||
| ## 2024-10-13 - 1.2.4 - fix(core) | ||||
|  | ||||
| Refactored DockerImageStore constructor to remove DockerHost dependency | ||||
|  | ||||
| - Adjusted DockerImageStore constructor to remove dependency on DockerHost | ||||
| - Updated ts/classes.host.ts to align with DockerImageStore's new constructor signature | ||||
|  | ||||
| ## 2024-08-21 - 1.2.3 - fix(dependencies) | ||||
|  | ||||
| Update dependencies to the latest versions and fix image export test | ||||
|  | ||||
| - Updated several dependencies to their latest versions in package.json. | ||||
| - Enabled the previously skipped 'should export images' test. | ||||
|  | ||||
| ## 2024-06-10 - 1.2.1-1.2.2 - Core/General | ||||
|  | ||||
| General updates and fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2024-06-10 - 1.2.0 - Core | ||||
|  | ||||
| Core updates and bug fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2024-06-08 - 1.2.0 - General/Core | ||||
|  | ||||
| Major release with core enhancements. | ||||
|  | ||||
| - Processing images with extraction, retagging, repackaging, and long-term storage | ||||
|  | ||||
| ## 2024-06-06 - 1.1.4 - General/Imagestore | ||||
|  | ||||
| Significant feature addition. | ||||
|  | ||||
| - Add feature to process images with extraction, retagging, repackaging, and long-term storage | ||||
|  | ||||
| ## 2024-05-08 - 1.0.112 - Images | ||||
|  | ||||
| Add new functionality for image handling. | ||||
|  | ||||
| - Can now import and export images | ||||
| - Start work on local 100% JS OCI image registry | ||||
|  | ||||
| ## 2024-06-05 - 1.1.0-1.1.3 - Core | ||||
|  | ||||
| Regular updates and fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2024-02-02 - 1.0.105-1.0.110 - Core | ||||
|  | ||||
| Routine core updates and fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2022-10-17 - 1.0.103-1.0.104 - Core | ||||
|  | ||||
| Routine core updates. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2020-10-01 - 1.0.99-1.0.102 - Core | ||||
|  | ||||
| Routine core updates. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2019-09-22 - 1.0.73-1.0.78 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2019-09-13 - 1.0.60-1.0.72 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2019-08-16 - 1.0.43-1.0.59 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2019-08-15 - 1.0.37-1.0.42 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2019-08-14 - 1.0.31-1.0.36 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2019-01-10 - 1.0.27-1.0.30 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core update | ||||
|  | ||||
| ## 2018-07-16 - 1.0.23-1.0.24 - Core | ||||
|  | ||||
| Routine updates and core fixes. | ||||
|  | ||||
| - Fix core shift to new style | ||||
|  | ||||
| ## 2017-07-16 - 1.0.20-1.0.22 - General | ||||
|  | ||||
| Routine updates and fixes. | ||||
|  | ||||
| - Update node_modules within npmdocker | ||||
|  | ||||
| ## 2017-04-02 - 1.0.18-1.0.19 - General | ||||
|  | ||||
| Routine updates and fixes. | ||||
|  | ||||
| - Work with npmdocker and npmts 7.x.x | ||||
| - CI updates | ||||
|  | ||||
| ## 2016-07-31 - 1.0.17 - General | ||||
|  | ||||
| Enhancements and fixes. | ||||
|  | ||||
| - Now waiting for response to be stored before ending streaming request | ||||
| - Cosmetic fix | ||||
|  | ||||
| ## 2016-07-29 - 1.0.14-1.0.16 - General | ||||
|  | ||||
| Multiple updates and features added. | ||||
|  | ||||
| - Fix request for change observable and add npmdocker | ||||
| - Add request typings | ||||
|  | ||||
| ## 2016-07-28 - 1.0.13 - Core | ||||
|  | ||||
| Fixes and preparations. | ||||
|  | ||||
| - Fixed request for newer docker | ||||
| - Prepare for npmdocker | ||||
|  | ||||
|  | ||||
| ## 2016-06-16 - 1.0.0-1.0.2 - General | ||||
|  | ||||
| Initial sequence of releases, significant feature additions and CI setups. | ||||
|  | ||||
| - Implement container start and stop | ||||
| @@ -192,7 +233,7 @@ Initial sequence of releases, significant feature additions and CI setups. | ||||
| - Add tests with in docker environment | ||||
|  | ||||
| ## 2016-04-12 - unknown - Initial Commit | ||||
|  | ||||
| Initial project setup. | ||||
|  | ||||
| - Initial commit | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "npmdocker": { | ||||
|     "baseImage": "hosttoday/ht-docker-node:npmci", | ||||
|     "baseImage": "host.today/ht-docker-node:npmci", | ||||
|     "command": "(ls -a && rm -r node_modules && yarn global add npmts && yarn install && npmts)", | ||||
|     "dockerSock": true | ||||
|   }, | ||||
| @@ -12,11 +12,11 @@ | ||||
|   "gitzone": { | ||||
|     "projectType": "npm", | ||||
|     "module": { | ||||
|       "githost": "gitlab.com", | ||||
|       "gitscope": "mojoio", | ||||
|       "githost": "code.foss.global", | ||||
|       "gitscope": "apiclient.xyz", | ||||
|       "gitrepo": "docker", | ||||
|       "description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.", | ||||
|       "npmPackagename": "@mojoio/docker", | ||||
|       "npmPackagename": "@apiclient.xyz/docker", | ||||
|       "license": "MIT", | ||||
|       "keywords": [ | ||||
|         "Docker", | ||||
|   | ||||
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							| @@ -13,7 +13,7 @@ | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git+https://gitlab.com/mojoio/docker.git" | ||||
|     "url": "https://code.foss.global/apiclient.xyz/docker.git" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "Docker", | ||||
| @@ -29,9 +29,9 @@ | ||||
|   "author": "Lossless GmbH", | ||||
|   "license": "MIT", | ||||
|   "bugs": { | ||||
|     "url": "https://gitlab.com/mojoio/docker/issues" | ||||
|     "url": "https://code.foss.global/apiclient.xyz/docker/issues" | ||||
|   }, | ||||
|   "homepage": "https://gitlab.com/mojoio/docker#readme", | ||||
|   "homepage": "https://code.foss.global/apiclient.xyz/docker#readme", | ||||
|   "dependencies": { | ||||
|     "@push.rocks/lik": "^6.2.2", | ||||
|     "@push.rocks/smartarchive": "^4.2.2", | ||||
| @@ -72,5 +72,8 @@ | ||||
|   "browserslist": [ | ||||
|     "last 1 chrome versions" | ||||
|   ], | ||||
|   "packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748" | ||||
|   "packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748", | ||||
|   "pnpm": { | ||||
|     "overrides": {} | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										123
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								readme.md
									
									
									
									
									
								
							| @@ -38,7 +38,7 @@ const docker = new DockerHost(); | ||||
|  | ||||
| // Or connect to remote Docker host | ||||
| const remoteDocker = new DockerHost({ | ||||
|   socketPath: 'tcp://remote-docker-host:2375' | ||||
|   socketPath: 'tcp://remote-docker-host:2375', | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| @@ -56,9 +56,9 @@ const docker = new DockerHost(); | ||||
|  | ||||
| // Custom initialization options | ||||
| const customDocker = new DockerHost({ | ||||
|   socketPath: '/var/run/docker.sock',  // Unix socket path | ||||
|   socketPath: '/var/run/docker.sock', // Unix socket path | ||||
|   // or | ||||
|   socketPath: 'tcp://192.168.1.100:2375'  // TCP connection | ||||
|   socketPath: 'tcp://192.168.1.100:2375', // TCP connection | ||||
| }); | ||||
|  | ||||
| // Start and stop (for lifecycle management) | ||||
| @@ -76,7 +76,7 @@ await docker.stop(); | ||||
| const allContainers = await docker.getContainers(); | ||||
|  | ||||
| // Each container includes detailed information | ||||
| allContainers.forEach(container => { | ||||
| allContainers.forEach((container) => { | ||||
|   console.log(`Container: ${container.Names[0]}`); | ||||
|   console.log(`  ID: ${container.Id}`); | ||||
|   console.log(`  Status: ${container.Status}`); | ||||
| @@ -96,21 +96,18 @@ const container = await DockerContainer.create(docker, { | ||||
|   name: 'my-nginx-server', | ||||
|   HostConfig: { | ||||
|     PortBindings: { | ||||
|       '80/tcp': [{ HostPort: '8080' }] | ||||
|       '80/tcp': [{ HostPort: '8080' }], | ||||
|     }, | ||||
|     RestartPolicy: { | ||||
|       Name: 'unless-stopped' | ||||
|       Name: 'unless-stopped', | ||||
|     }, | ||||
|     Memory: 512 * 1024 * 1024, // 512MB memory limit | ||||
|   }, | ||||
|   Env: [ | ||||
|     'NODE_ENV=production', | ||||
|     'LOG_LEVEL=info' | ||||
|   ], | ||||
|   Env: ['NODE_ENV=production', 'LOG_LEVEL=info'], | ||||
|   Labels: { | ||||
|     'app': 'web-server', | ||||
|     'environment': 'production' | ||||
|   } | ||||
|     app: 'web-server', | ||||
|     environment: 'production', | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| console.log(`Container created: ${container.Id}`); | ||||
| @@ -124,7 +121,10 @@ console.log(`Container created: ${container.Id}`); | ||||
| #### Get Container by ID | ||||
|  | ||||
| ```typescript | ||||
| const container = await DockerContainer.getContainerById(docker, 'container-id-here'); | ||||
| const container = await DockerContainer.getContainerById( | ||||
|   docker, | ||||
|   'container-id-here', | ||||
| ); | ||||
| if (container) { | ||||
|   console.log(`Found container: ${container.Names[0]}`); | ||||
| } | ||||
| @@ -142,7 +142,7 @@ const image = await DockerImage.createFromRegistry(docker, { | ||||
|   imageName: 'node', | ||||
|   imageTag: '18-alpine', | ||||
|   // Optional: provide registry authentication | ||||
|   authToken: 'your-registry-auth-token' | ||||
|   authToken: 'your-registry-auth-token', | ||||
| }); | ||||
|  | ||||
| console.log(`Image pulled: ${image.RepoTags[0]}`); | ||||
| @@ -159,7 +159,7 @@ const tarStream = fs.createReadStream('./my-image.tar'); | ||||
| const importedImage = await DockerImage.createFromTarStream(docker, { | ||||
|   tarStream, | ||||
|   imageUrl: 'file://./my-image.tar', | ||||
|   imageTag: 'my-app:v1.0.0' | ||||
|   imageTag: 'my-app:v1.0.0', | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| @@ -182,7 +182,7 @@ exportStream.pipe(writeStream); | ||||
| await DockerImage.tagImageByIdOrName(docker, 'node:18-alpine', { | ||||
|   registry: 'myregistry.com', | ||||
|   imageName: 'my-node-app', | ||||
|   imageTag: 'v2.0.0' | ||||
|   imageTag: 'v2.0.0', | ||||
| }); | ||||
| // Result: myregistry.com/my-node-app:v2.0.0 | ||||
| ``` | ||||
| @@ -201,15 +201,17 @@ const network = await DockerNetwork.createNetwork(docker, { | ||||
|   EnableIPv6: false, | ||||
|   IPAM: { | ||||
|     Driver: 'default', | ||||
|     Config: [{ | ||||
|       Subnet: '172.28.0.0/16', | ||||
|       Gateway: '172.28.0.1' | ||||
|     }] | ||||
|     Config: [ | ||||
|       { | ||||
|         Subnet: '172.28.0.0/16', | ||||
|         Gateway: '172.28.0.1', | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   Labels: { | ||||
|     'project': 'my-app', | ||||
|     'environment': 'production' | ||||
|   } | ||||
|     project: 'my-app', | ||||
|     environment: 'production', | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| console.log(`Network created: ${network.Id}`); | ||||
| @@ -220,14 +222,17 @@ console.log(`Network created: ${network.Id}`); | ||||
| ```typescript | ||||
| // Get all networks | ||||
| const networks = await docker.getNetworks(); | ||||
| networks.forEach(net => { | ||||
| networks.forEach((net) => { | ||||
|   console.log(`Network: ${net.Name} (${net.Driver})`); | ||||
|   console.log(`  Scope: ${net.Scope}`); | ||||
|   console.log(`  Internal: ${net.Internal}`); | ||||
| }); | ||||
|  | ||||
| // Get specific network | ||||
| const appNetwork = await DockerNetwork.getNetworkByName(docker, 'my-app-network'); | ||||
| const appNetwork = await DockerNetwork.getNetworkByName( | ||||
|   docker, | ||||
|   'my-app-network', | ||||
| ); | ||||
|  | ||||
| // Get containers on network | ||||
| const containers = await appNetwork.getContainersOnNetwork(); | ||||
| @@ -246,28 +251,32 @@ const service = await DockerService.createService(docker, { | ||||
|   name: 'web-api', | ||||
|   image: 'my-api:latest', | ||||
|   replicas: 3, | ||||
|   ports: [{ | ||||
|     Protocol: 'tcp', | ||||
|     PublishedPort: 80, | ||||
|     TargetPort: 3000 | ||||
|   }], | ||||
|   ports: [ | ||||
|     { | ||||
|       Protocol: 'tcp', | ||||
|       PublishedPort: 80, | ||||
|       TargetPort: 3000, | ||||
|     }, | ||||
|   ], | ||||
|   networks: ['my-app-network'], | ||||
|   labels: { | ||||
|     'app': 'api', | ||||
|     'version': '2.0.0' | ||||
|     app: 'api', | ||||
|     version: '2.0.0', | ||||
|   }, | ||||
|   resources: { | ||||
|     limits: { | ||||
|       Memory: 256 * 1024 * 1024, // 256MB | ||||
|       CPUs: 0.5 | ||||
|     } | ||||
|       CPUs: 0.5, | ||||
|     }, | ||||
|   }, | ||||
|   secrets: ['api-key', 'db-password'], | ||||
|   mounts: [{ | ||||
|     Target: '/data', | ||||
|     Source: 'app-data', | ||||
|     Type: 'volume' | ||||
|   }] | ||||
|   mounts: [ | ||||
|     { | ||||
|       Target: '/data', | ||||
|       Source: 'app-data', | ||||
|       Type: 'volume', | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
|  | ||||
| console.log(`Service deployed: ${service.ID}`); | ||||
| @@ -278,7 +287,7 @@ console.log(`Service deployed: ${service.ID}`); | ||||
| ```typescript | ||||
| // List all services | ||||
| const services = await docker.getServices(); | ||||
| services.forEach(service => { | ||||
| services.forEach((service) => { | ||||
|   console.log(`Service: ${service.Spec.Name}`); | ||||
|   console.log(`  Replicas: ${service.Spec.Mode.Replicated.Replicas}`); | ||||
|   console.log(`  Image: ${service.Spec.TaskTemplate.ContainerSpec.Image}`); | ||||
| @@ -307,16 +316,16 @@ const secret = await DockerSecret.createSecret(docker, { | ||||
|   name: 'api-key', | ||||
|   data: Buffer.from('super-secret-key-123').toString('base64'), | ||||
|   labels: { | ||||
|     'app': 'my-app', | ||||
|     'type': 'api-key' | ||||
|   } | ||||
|     app: 'my-app', | ||||
|     type: 'api-key', | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| console.log(`Secret created: ${secret.ID}`); | ||||
|  | ||||
| // List secrets | ||||
| const secrets = await DockerSecret.getSecrets(docker); | ||||
| secrets.forEach(secret => { | ||||
| secrets.forEach((secret) => { | ||||
|   console.log(`Secret: ${secret.Spec.Name}`); | ||||
| }); | ||||
|  | ||||
| @@ -325,7 +334,7 @@ const apiKeySecret = await DockerSecret.getSecretByName(docker, 'api-key'); | ||||
|  | ||||
| // Update secret | ||||
| await apiKeySecret.update({ | ||||
|   data: Buffer.from('new-secret-key-456').toString('base64') | ||||
|   data: Buffer.from('new-secret-key-456').toString('base64'), | ||||
| }); | ||||
|  | ||||
| // Remove secret | ||||
| @@ -342,7 +351,7 @@ await docker.addS3Storage({ | ||||
|   endpoint: 's3.amazonaws.com', | ||||
|   accessKeyId: 'your-access-key', | ||||
|   secretAccessKey: 'your-secret-key', | ||||
|   bucket: 'docker-images' | ||||
|   bucket: 'docker-images', | ||||
| }); | ||||
|  | ||||
| // Store an image to S3 | ||||
| @@ -368,7 +377,7 @@ const subscription = eventStream.subscribe({ | ||||
|     console.log(`Time: ${new Date(event.time * 1000).toISOString()}`); | ||||
|   }, | ||||
|   error: (err) => console.error('Event stream error:', err), | ||||
|   complete: () => console.log('Event stream completed') | ||||
|   complete: () => console.log('Event stream completed'), | ||||
| }); | ||||
|  | ||||
| // Unsubscribe when done | ||||
| @@ -384,7 +393,7 @@ Authenticate with Docker registries for private images: | ||||
| await docker.auth({ | ||||
|   username: 'your-username', | ||||
|   password: 'your-password', | ||||
|   serveraddress: 'https://index.docker.io/v1/' | ||||
|   serveraddress: 'https://index.docker.io/v1/', | ||||
| }); | ||||
|  | ||||
| // Or use existing Docker config | ||||
| @@ -394,7 +403,7 @@ const authToken = await docker.getAuthTokenFromDockerConfig('myregistry.com'); | ||||
| const privateImage = await DockerImage.createFromRegistry(docker, { | ||||
|   imageName: 'myregistry.com/private/image', | ||||
|   imageTag: 'latest', | ||||
|   authToken | ||||
|   authToken, | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| @@ -407,14 +416,14 @@ Initialize and manage Docker Swarm: | ||||
| await docker.activateSwarm({ | ||||
|   ListenAddr: '0.0.0.0:2377', | ||||
|   AdvertiseAddr: '192.168.1.100:2377', | ||||
|   ForceNewCluster: false | ||||
|   ForceNewCluster: false, | ||||
| }); | ||||
|  | ||||
| // Now you can create services, secrets, and use swarm features | ||||
| const service = await DockerService.createService(docker, { | ||||
|   name: 'my-swarm-service', | ||||
|   image: 'nginx:latest', | ||||
|   replicas: 5 | ||||
|   replicas: 5, | ||||
|   // ... more service config | ||||
| }); | ||||
| ``` | ||||
| @@ -430,13 +439,13 @@ async function deployStack() { | ||||
|   // Create network | ||||
|   const network = await DockerNetwork.createNetwork(docker, { | ||||
|     Name: 'app-network', | ||||
|     Driver: 'overlay' // for swarm mode | ||||
|     Driver: 'overlay', // for swarm mode | ||||
|   }); | ||||
|  | ||||
|   // Create secrets | ||||
|   const dbPassword = await DockerSecret.createSecret(docker, { | ||||
|     name: 'db-password', | ||||
|     data: Buffer.from('strong-password').toString('base64') | ||||
|     data: Buffer.from('strong-password').toString('base64'), | ||||
|   }); | ||||
|  | ||||
|   // Deploy database service | ||||
| @@ -445,7 +454,7 @@ async function deployStack() { | ||||
|     image: 'postgres:14', | ||||
|     networks: ['app-network'], | ||||
|     secrets: ['db-password'], | ||||
|     env: ['POSTGRES_PASSWORD_FILE=/run/secrets/db-password'] | ||||
|     env: ['POSTGRES_PASSWORD_FILE=/run/secrets/db-password'], | ||||
|   }); | ||||
|  | ||||
|   // Deploy application service | ||||
| @@ -454,7 +463,7 @@ async function deployStack() { | ||||
|     image: 'my-app:latest', | ||||
|     replicas: 3, | ||||
|     networks: ['app-network'], | ||||
|     ports: [{ Protocol: 'tcp', PublishedPort: 80, TargetPort: 3000 }] | ||||
|     ports: [{ Protocol: 'tcp', PublishedPort: 80, TargetPort: 3000 }], | ||||
|   }); | ||||
|  | ||||
|   console.log('Stack deployed successfully!'); | ||||
| @@ -471,7 +480,7 @@ import type { | ||||
|   IServiceCreationDescriptor, | ||||
|   INetworkCreationDescriptor, | ||||
|   IImageCreationDescriptor, | ||||
|   ISecretCreationDescriptor | ||||
|   ISecretCreationDescriptor, | ||||
| } from '@apiclient.xyz/docker'; | ||||
|  | ||||
| // Full IntelliSense support for all configuration options | ||||
|   | ||||
| @@ -41,7 +41,10 @@ tap.test('should create a network', async () => { | ||||
| }); | ||||
|  | ||||
| tap.test('should remove a network', async () => { | ||||
|   const webgateway = await docker.DockerNetwork.getNetworkByName(testDockerHost, 'webgateway'); | ||||
|   const webgateway = await docker.DockerNetwork.getNetworkByName( | ||||
|     testDockerHost, | ||||
|     'webgateway', | ||||
|   ); | ||||
|   await webgateway.remove(); | ||||
| }); | ||||
|  | ||||
| @@ -78,7 +81,10 @@ tap.test('should create a secret', async () => { | ||||
| }); | ||||
|  | ||||
| tap.test('should remove a secret by name', async () => { | ||||
|   const mySecret = await docker.DockerSecret.getSecretByName(testDockerHost, 'testSecret'); | ||||
|   const mySecret = await docker.DockerSecret.getSecretByName( | ||||
|     testDockerHost, | ||||
|     'testSecret', | ||||
|   ); | ||||
|   await mySecret.remove(); | ||||
| }); | ||||
|  | ||||
| @@ -102,11 +108,14 @@ tap.test('should create a service', async () => { | ||||
|     labels: {}, | ||||
|     contentArg: '{"hi": "wow"}', | ||||
|   }); | ||||
|   const testImage = await docker.DockerImage.createFromRegistry(testDockerHost, { | ||||
|     creationObject: { | ||||
|       imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', | ||||
|     } | ||||
|   }); | ||||
|   const testImage = await docker.DockerImage.createFromRegistry( | ||||
|     testDockerHost, | ||||
|     { | ||||
|       creationObject: { | ||||
|         imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', | ||||
|       }, | ||||
|     }, | ||||
|   ); | ||||
|   const testService = await docker.DockerService.createService(testDockerHost, { | ||||
|     image: testImage, | ||||
|     labels: {}, | ||||
| @@ -124,13 +133,16 @@ tap.test('should create a service', async () => { | ||||
|  | ||||
| tap.test('should export images', async (toolsArg) => { | ||||
|   const done = toolsArg.defer(); | ||||
|   const testImage = await docker.DockerImage.createFromRegistry(testDockerHost, { | ||||
|     creationObject: { | ||||
|       imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', | ||||
|     } | ||||
|   }); | ||||
|   const testImage = await docker.DockerImage.createFromRegistry( | ||||
|     testDockerHost, | ||||
|     { | ||||
|       creationObject: { | ||||
|         imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', | ||||
|       }, | ||||
|     }, | ||||
|   ); | ||||
|   const fsWriteStream = plugins.smartfile.fsStream.createWriteStream( | ||||
|     plugins.path.join(paths.nogitDir, 'testimage.tar') | ||||
|     plugins.path.join(paths.nogitDir, 'testimage.tar'), | ||||
|   ); | ||||
|   const exportStream = await testImage.exportToTarStream(); | ||||
|   exportStream.pipe(fsWriteStream).on('finish', () => { | ||||
| @@ -141,14 +153,17 @@ tap.test('should export images', async (toolsArg) => { | ||||
|  | ||||
| tap.test('should import images', async () => { | ||||
|   const fsReadStream = plugins.smartfile.fsStream.createReadStream( | ||||
|     plugins.path.join(paths.nogitDir, 'testimage.tar') | ||||
|     plugins.path.join(paths.nogitDir, 'testimage.tar'), | ||||
|   ); | ||||
|   const importedImage = await docker.DockerImage.createFromTarStream( | ||||
|     testDockerHost, | ||||
|     { | ||||
|       tarStream: fsReadStream, | ||||
|       creationObject: { | ||||
|         imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', | ||||
|       }, | ||||
|     }, | ||||
|   ); | ||||
|   const importedImage = await docker.DockerImage.createFromTarStream(testDockerHost, { | ||||
|     tarStream: fsReadStream, | ||||
|     creationObject: { | ||||
|       imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', | ||||
|     } | ||||
|   }); | ||||
|   expect(importedImage).toBeInstanceOf(docker.DockerImage); | ||||
| }); | ||||
|  | ||||
| @@ -163,11 +178,16 @@ tap.test('should expose a working DockerImageStore', async () => { | ||||
|   await testDockerHost.addS3Storage(s3Descriptor); | ||||
|  | ||||
|   // | ||||
|   await testDockerHost.imageStore.storeImage('hello2', plugins.smartfile.fsStream.createReadStream(plugins.path.join(paths.nogitDir, 'testimage.tar'))); | ||||
| }) | ||||
|   await testDockerHost.imageStore.storeImage( | ||||
|     'hello2', | ||||
|     plugins.smartfile.fsStream.createReadStream( | ||||
|       plugins.path.join(paths.nogitDir, 'testimage.tar'), | ||||
|     ), | ||||
|   ); | ||||
| }); | ||||
|  | ||||
| tap.test('cleanup', async () => { | ||||
|   await testDockerHost.stop(); | ||||
| }) | ||||
| }); | ||||
|  | ||||
| export default tap.start(); | ||||
|   | ||||
| @@ -3,6 +3,6 @@ | ||||
|  */ | ||||
| export const commitinfo = { | ||||
|   name: '@apiclient.xyz/docker', | ||||
|   version: '1.3.4', | ||||
|   version: '1.3.5', | ||||
|   description: 'Provides easy communication with Docker remote API from Node.js, with TypeScript support.' | ||||
| } | ||||
|   | ||||
| @@ -10,7 +10,9 @@ export class DockerContainer { | ||||
|   /** | ||||
|    * get all containers | ||||
|    */ | ||||
|   public static async getContainers(dockerHostArg: DockerHost): Promise<DockerContainer[]> { | ||||
|   public static async getContainers( | ||||
|     dockerHostArg: DockerHost, | ||||
|   ): Promise<DockerContainer[]> { | ||||
|     const result: DockerContainer[] = []; | ||||
|     const response = await dockerHostArg.request('GET', '/containers/json'); | ||||
|  | ||||
| @@ -34,7 +36,7 @@ export class DockerContainer { | ||||
|    */ | ||||
|   public static async create( | ||||
|     dockerHost: DockerHost, | ||||
|     containerCreationDescriptor: interfaces.IContainerCreationDescriptor | ||||
|     containerCreationDescriptor: interfaces.IContainerCreationDescriptor, | ||||
|   ) { | ||||
|     // check for unique hostname | ||||
|     const existingContainers = await DockerContainer.getContainers(dockerHost); | ||||
| @@ -50,7 +52,10 @@ export class DockerContainer { | ||||
|     if (response.statusCode < 300) { | ||||
|       logger.log('info', 'Container created successfully'); | ||||
|     } else { | ||||
|       logger.log('error', 'There has been a problem when creating the container'); | ||||
|       logger.log( | ||||
|         'error', | ||||
|         'There has been a problem when creating the container', | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -37,10 +37,13 @@ export class DockerHost { | ||||
|   constructor(optionsArg: IDockerHostConstructorOptions) { | ||||
|     this.options = { | ||||
|       ...{ | ||||
|         imageStoreDir: plugins.path.join(paths.nogitDir, 'temp-docker-image-store'), | ||||
|         imageStoreDir: plugins.path.join( | ||||
|           paths.nogitDir, | ||||
|           'temp-docker-image-store', | ||||
|         ), | ||||
|       }, | ||||
|       ...optionsArg, | ||||
|     } | ||||
|     }; | ||||
|     let pathToUse: string; | ||||
|     if (optionsArg.dockerSockPath) { | ||||
|       pathToUse = optionsArg.dockerSockPath; | ||||
| @@ -62,7 +65,7 @@ export class DockerHost { | ||||
|     this.imageStore = new DockerImageStore({ | ||||
|       bucketDir: null, | ||||
|       localDirPath: this.options.imageStoreDir, | ||||
|     }) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public async start() { | ||||
| @@ -84,17 +87,22 @@ export class DockerHost { | ||||
|       throw new Error(response.body.Status); | ||||
|     } | ||||
|     console.log(response.body.Status); | ||||
|     this.registryToken = plugins.smartstring.base64.encode(plugins.smartjson.stringify(authData)); | ||||
|     this.registryToken = plugins.smartstring.base64.encode( | ||||
|       plugins.smartjson.stringify(authData), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * gets the token from the .docker/config.json file for GitLab registry | ||||
|    */ | ||||
|   public async getAuthTokenFromDockerConfig(registryUrlArg: string) { | ||||
|     const dockerConfigPath = plugins.smartpath.get.home('~/.docker/config.json'); | ||||
|     const dockerConfigPath = plugins.smartpath.get.home( | ||||
|       '~/.docker/config.json', | ||||
|     ); | ||||
|     const configObject = plugins.smartfile.fs.toObjectSync(dockerConfigPath); | ||||
|     const gitlabAuthBase64 = configObject.auths[registryUrlArg].auth; | ||||
|     const gitlabAuth: string = plugins.smartstring.base64.decode(gitlabAuthBase64); | ||||
|     const gitlabAuth: string = | ||||
|       plugins.smartstring.base64.decode(gitlabAuthBase64); | ||||
|     const gitlabAuthArray = gitlabAuth.split(':'); | ||||
|     await this.auth({ | ||||
|       username: gitlabAuthArray[0], | ||||
| @@ -116,7 +124,9 @@ export class DockerHost { | ||||
|   /** | ||||
|    * create a network | ||||
|    */ | ||||
|   public async createNetwork(optionsArg: Parameters<typeof DockerNetwork.createNetwork>[1]) { | ||||
|   public async createNetwork( | ||||
|     optionsArg: Parameters<typeof DockerNetwork.createNetwork>[1], | ||||
|   ) { | ||||
|     return await DockerNetwork.createNetwork(this, optionsArg); | ||||
|   } | ||||
|  | ||||
| @@ -127,7 +137,6 @@ export class DockerHost { | ||||
|     return await DockerNetwork.getNetworkByName(this, networkNameArg); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   // ============== | ||||
|   // CONTAINERS | ||||
|   // ============== | ||||
| @@ -265,16 +274,21 @@ export class DockerHost { | ||||
|  | ||||
|     // Docker's streaming endpoints (like /images/create) return newline-delimited JSON | ||||
|     // which can't be parsed as a single JSON object | ||||
|     const isStreamingEndpoint = routeArg.includes('/images/create') ||  | ||||
|                                 routeArg.includes('/images/load') || | ||||
|                                 routeArg.includes('/build'); | ||||
|     const isStreamingEndpoint = | ||||
|       routeArg.includes('/images/create') || | ||||
|       routeArg.includes('/images/load') || | ||||
|       routeArg.includes('/build'); | ||||
|  | ||||
|     if (contentType.includes('application/json') && !isStreamingEndpoint) { | ||||
|       body = await response.json(); | ||||
|     } else { | ||||
|       body = await response.text(); | ||||
|       // Try to parse as JSON if it looks like JSON and is not a streaming response | ||||
|       if (!isStreamingEndpoint && body && (body.startsWith('{') || body.startsWith('['))) { | ||||
|       if ( | ||||
|         !isStreamingEndpoint && | ||||
|         body && | ||||
|         (body.startsWith('{') || body.startsWith('[')) | ||||
|       ) { | ||||
|         try { | ||||
|           body = JSON.parse(body); | ||||
|         } catch { | ||||
| @@ -287,7 +301,7 @@ export class DockerHost { | ||||
|     const legacyResponse = { | ||||
|       statusCode: response.status, | ||||
|       body: body, | ||||
|       headers: response.headers | ||||
|       headers: response.headers, | ||||
|     }; | ||||
|  | ||||
|     if (response.status !== 200) { | ||||
| @@ -297,7 +311,11 @@ export class DockerHost { | ||||
|     return legacyResponse; | ||||
|   } | ||||
|  | ||||
|   public async requestStreaming(methodArg: string, routeArg: string, readStream?: plugins.smartstream.stream.Readable) { | ||||
|   public async requestStreaming( | ||||
|     methodArg: string, | ||||
|     routeArg: string, | ||||
|     readStream?: plugins.smartstream.stream.Readable, | ||||
|   ) { | ||||
|     const requestUrl = `${this.socketPath}${routeArg}`; | ||||
|  | ||||
|     // Build the request using the fluent API | ||||
| @@ -319,7 +337,7 @@ export class DockerHost { | ||||
|           } | ||||
|           counter++; | ||||
|           return chunkArg; | ||||
|         } | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       // Pipe through the logging duplex stream | ||||
| @@ -362,7 +380,7 @@ export class DockerHost { | ||||
|       return { | ||||
|         statusCode: response.status, | ||||
|         body: body, | ||||
|         headers: response.headers | ||||
|         headers: response.headers, | ||||
|       }; | ||||
|     } | ||||
|  | ||||
| @@ -382,10 +400,14 @@ export class DockerHost { | ||||
|     if (!optionsArg.bucketName) { | ||||
|       throw new Error('bucketName is required'); | ||||
|     } | ||||
|     const bucket = await this.smartBucket.getBucketByName(optionsArg.bucketName); | ||||
|     const bucket = await this.smartBucket.getBucketByName( | ||||
|       optionsArg.bucketName, | ||||
|     ); | ||||
|     let wantedDirectory = await bucket.getBaseDirectory(); | ||||
|     if (optionsArg.directoryPath) { | ||||
|       wantedDirectory = await wantedDirectory.getSubDirectoryByName(optionsArg.directoryPath); | ||||
|       wantedDirectory = await wantedDirectory.getSubDirectoryByName( | ||||
|         optionsArg.directoryPath, | ||||
|       ); | ||||
|     } | ||||
|     this.imageStore.options.bucketDir = wantedDirectory; | ||||
|   } | ||||
|   | ||||
| @@ -17,7 +17,10 @@ export class DockerImage { | ||||
|     return images; | ||||
|   } | ||||
|  | ||||
|   public static async getImageByName(dockerHost: DockerHost, imageNameArg: string) { | ||||
|   public static async getImageByName( | ||||
|     dockerHost: DockerHost, | ||||
|     imageNameArg: string, | ||||
|   ) { | ||||
|     const images = await this.getImages(dockerHost); | ||||
|     const result = images.find((image) => { | ||||
|       if (image.RepoTags) { | ||||
| @@ -32,8 +35,8 @@ export class DockerImage { | ||||
|   public static async createFromRegistry( | ||||
|     dockerHostArg: DockerHost, | ||||
|     optionsArg: { | ||||
|       creationObject: interfaces.IImageCreationDescriptor | ||||
|     } | ||||
|       creationObject: interfaces.IImageCreationDescriptor; | ||||
|     }, | ||||
|   ): Promise<DockerImage> { | ||||
|     // lets create a sanatized imageUrlObject | ||||
|     const imageUrlObject: { | ||||
| @@ -50,7 +53,7 @@ export class DockerImage { | ||||
|       const imageTag = imageUrlObject.imageUrl.split(':')[1]; | ||||
|       if (imageUrlObject.imageTag) { | ||||
|         throw new Error( | ||||
|           `imageUrl ${imageUrlObject.imageUrl} can't be tagged with ${imageUrlObject.imageTag} because it is already tagged with ${imageTag}` | ||||
|           `imageUrl ${imageUrlObject.imageUrl} can't be tagged with ${imageUrlObject.imageTag} because it is already tagged with ${imageTag}`, | ||||
|         ); | ||||
|       } else { | ||||
|         imageUrlObject.imageUrl = imageUrl; | ||||
| @@ -65,12 +68,18 @@ export class DockerImage { | ||||
|     const response = await dockerHostArg.request( | ||||
|       'POST', | ||||
|       `/images/create?fromImage=${encodeURIComponent( | ||||
|         imageUrlObject.imageUrl | ||||
|       )}&tag=${encodeURIComponent(imageUrlObject.imageTag)}` | ||||
|         imageUrlObject.imageUrl, | ||||
|       )}&tag=${encodeURIComponent(imageUrlObject.imageTag)}`, | ||||
|     ); | ||||
|     if (response.statusCode < 300) { | ||||
|       logger.log('info', `Successfully pulled image ${imageUrlObject.imageUrl} from the registry`); | ||||
|       const image = await DockerImage.getImageByName(dockerHostArg, imageUrlObject.imageOriginTag); | ||||
|       logger.log( | ||||
|         'info', | ||||
|         `Successfully pulled image ${imageUrlObject.imageUrl} from the registry`, | ||||
|       ); | ||||
|       const image = await DockerImage.getImageByName( | ||||
|         dockerHostArg, | ||||
|         imageUrlObject.imageOriginTag, | ||||
|       ); | ||||
|       return image; | ||||
|     } else { | ||||
|       logger.log('error', `Failed at the attempt of creating a new image`); | ||||
| @@ -87,13 +96,13 @@ export class DockerImage { | ||||
|     optionsArg: { | ||||
|       creationObject: interfaces.IImageCreationDescriptor; | ||||
|       tarStream: plugins.smartstream.stream.Readable; | ||||
|     } | ||||
|     }, | ||||
|   ): Promise<DockerImage> { | ||||
|     // Start the request for importing an image | ||||
|     const response = await dockerHostArg.requestStreaming( | ||||
|       'POST', | ||||
|       '/images/load', | ||||
|       optionsArg.tarStream | ||||
|       optionsArg.tarStream, | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
| @@ -144,7 +153,7 @@ export class DockerImage { | ||||
|  | ||||
|     if (!loadedImageTag) { | ||||
|       throw new Error( | ||||
|         `Could not parse the loaded image info from Docker response.\nResponse was:\n${rawOutput}` | ||||
|         `Could not parse the loaded image info from Docker response.\nResponse was:\n${rawOutput}`, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
| @@ -153,34 +162,31 @@ export class DockerImage { | ||||
|     //   "myrepo/myimage:latest"  OR  "sha256:someHash..." | ||||
|     // If Docker gave you an ID (e.g. "sha256:..."), you may need a separate | ||||
|     // DockerImage.getImageById method; or if you prefer, you can treat it as a name. | ||||
|     const newlyImportedImage = await DockerImage.getImageByName(dockerHostArg, loadedImageTag); | ||||
|     const newlyImportedImage = await DockerImage.getImageByName( | ||||
|       dockerHostArg, | ||||
|       loadedImageTag, | ||||
|     ); | ||||
|  | ||||
|     if (!newlyImportedImage) { | ||||
|       throw new Error( | ||||
|         `Image load succeeded, but no local reference found for "${loadedImageTag}".` | ||||
|         `Image load succeeded, but no local reference found for "${loadedImageTag}".`, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     logger.log( | ||||
|       'info', | ||||
|       `Successfully imported image "${loadedImageTag}".` | ||||
|     ); | ||||
|     logger.log('info', `Successfully imported image "${loadedImageTag}".`); | ||||
|  | ||||
|     return newlyImportedImage; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   public static async tagImageByIdOrName( | ||||
|     dockerHost: DockerHost, | ||||
|     idOrNameArg: string, | ||||
|     newTagArg: string | ||||
|     newTagArg: string, | ||||
|   ) { | ||||
|     const response = await dockerHost.request( | ||||
|       'POST', | ||||
|       `/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}` | ||||
|       `/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}`, | ||||
|     ); | ||||
|  | ||||
|  | ||||
|   } | ||||
|  | ||||
|   public static async buildImage(dockerHostArg: DockerHost, dockerImageTag) { | ||||
| @@ -249,7 +255,10 @@ export class DockerImage { | ||||
|    */ | ||||
|   public async exportToTarStream(): Promise<plugins.smartstream.stream.Readable> { | ||||
|     logger.log('info', `Exporting image ${this.RepoTags[0]} to tar stream.`); | ||||
|     const response = await this.dockerHost.requestStreaming('GET', `/images/${encodeURIComponent(this.RepoTags[0])}/get`); | ||||
|     const response = await this.dockerHost.requestStreaming( | ||||
|       'GET', | ||||
|       `/images/${encodeURIComponent(this.RepoTags[0])}/get`, | ||||
|     ); | ||||
|  | ||||
|     // Check if response is a Node.js stream | ||||
|     if (!response || typeof response.on !== 'function') { | ||||
| @@ -259,11 +268,10 @@ export class DockerImage { | ||||
|     let counter = 0; | ||||
|     const webduplexStream = new plugins.smartstream.SmartDuplex({ | ||||
|       writeFunction: async (chunk, tools) => { | ||||
|         if (counter % 1000 === 0) | ||||
|           console.log(`Got chunk: ${counter}`); | ||||
|         if (counter % 1000 === 0) console.log(`Got chunk: ${counter}`); | ||||
|         counter++; | ||||
|         return chunk; | ||||
|       } | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     response.on('data', (chunk) => { | ||||
|   | ||||
| @@ -22,14 +22,25 @@ export class DockerImageStore { | ||||
|   } | ||||
|  | ||||
|   // Method to store tar stream | ||||
|   public async storeImage(imageName: string, tarStream: plugins.smartstream.stream.Readable): Promise<void> { | ||||
|   public async storeImage( | ||||
|     imageName: string, | ||||
|     tarStream: plugins.smartstream.stream.Readable, | ||||
|   ): Promise<void> { | ||||
|     logger.log('info', `Storing image ${imageName}...`); | ||||
|     const uniqueProcessingId = plugins.smartunique.shortId(); | ||||
|  | ||||
|     const initialTarDownloadPath = plugins.path.join(this.options.localDirPath, `${uniqueProcessingId}.tar`); | ||||
|     const extractionDir = plugins.path.join(this.options.localDirPath, uniqueProcessingId); | ||||
|     const initialTarDownloadPath = plugins.path.join( | ||||
|       this.options.localDirPath, | ||||
|       `${uniqueProcessingId}.tar`, | ||||
|     ); | ||||
|     const extractionDir = plugins.path.join( | ||||
|       this.options.localDirPath, | ||||
|       uniqueProcessingId, | ||||
|     ); | ||||
|     // Create a write stream to store the tar file | ||||
|     const writeStream = plugins.smartfile.fsStream.createWriteStream(initialTarDownloadPath); | ||||
|     const writeStream = plugins.smartfile.fsStream.createWriteStream( | ||||
|       initialTarDownloadPath, | ||||
|     ); | ||||
|  | ||||
|     // lets wait for the write stream to finish | ||||
|     await new Promise((resolve, reject) => { | ||||
| @@ -37,23 +48,43 @@ export class DockerImageStore { | ||||
|       writeStream.on('finish', resolve); | ||||
|       writeStream.on('error', reject); | ||||
|     }); | ||||
|     logger.log('info', `Image ${imageName} stored locally for processing. Extracting...`); | ||||
|     logger.log( | ||||
|       'info', | ||||
|       `Image ${imageName} stored locally for processing. Extracting...`, | ||||
|     ); | ||||
|  | ||||
|     // lets process the image | ||||
|     const tarArchive = await plugins.smartarchive.SmartArchive.fromArchiveFile(initialTarDownloadPath); | ||||
|     const tarArchive = await plugins.smartarchive.SmartArchive.fromArchiveFile( | ||||
|       initialTarDownloadPath, | ||||
|     ); | ||||
|     await tarArchive.exportToFs(extractionDir); | ||||
|     logger.log('info', `Image ${imageName} extracted.`); | ||||
|     await plugins.smartfile.fs.remove(initialTarDownloadPath); | ||||
|     logger.log('info', `deleted original tar to save space.`); | ||||
|     logger.log('info', `now repackaging for s3...`); | ||||
|     const smartfileIndexJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'index.json')); | ||||
|     const smartfileManifestJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'manifest.json')); | ||||
|     const smartfileOciLayoutJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'oci-layout')); | ||||
|     const smartfileRepositoriesJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'repositories')); | ||||
|     const smartfileIndexJson = await plugins.smartfile.SmartFile.fromFilePath( | ||||
|       plugins.path.join(extractionDir, 'index.json'), | ||||
|     ); | ||||
|     const smartfileManifestJson = | ||||
|       await plugins.smartfile.SmartFile.fromFilePath( | ||||
|         plugins.path.join(extractionDir, 'manifest.json'), | ||||
|       ); | ||||
|     const smartfileOciLayoutJson = | ||||
|       await plugins.smartfile.SmartFile.fromFilePath( | ||||
|         plugins.path.join(extractionDir, 'oci-layout'), | ||||
|       ); | ||||
|     const smartfileRepositoriesJson = | ||||
|       await plugins.smartfile.SmartFile.fromFilePath( | ||||
|         plugins.path.join(extractionDir, 'repositories'), | ||||
|       ); | ||||
|     const indexJson = JSON.parse(smartfileIndexJson.contents.toString()); | ||||
|     const manifestJson = JSON.parse(smartfileManifestJson.contents.toString()); | ||||
|     const ociLayoutJson = JSON.parse(smartfileOciLayoutJson.contents.toString()); | ||||
|     const repositoriesJson = JSON.parse(smartfileRepositoriesJson.contents.toString()); | ||||
|     const ociLayoutJson = JSON.parse( | ||||
|       smartfileOciLayoutJson.contents.toString(), | ||||
|     ); | ||||
|     const repositoriesJson = JSON.parse( | ||||
|       smartfileRepositoriesJson.contents.toString(), | ||||
|     ); | ||||
|  | ||||
|     indexJson.manifests[0].annotations['io.containerd.image.name'] = imageName; | ||||
|     manifestJson[0].RepoTags[0] = imageName; | ||||
| @@ -62,10 +93,18 @@ export class DockerImageStore { | ||||
|     repositoriesJson[imageName] = repoFirstValue; | ||||
|     delete repositoriesJson[repoFirstKey]; | ||||
|  | ||||
|     smartfileIndexJson.contents = Buffer.from(JSON.stringify(indexJson, null, 2)); | ||||
|     smartfileManifestJson.contents = Buffer.from(JSON.stringify(manifestJson, null, 2)); | ||||
|     smartfileOciLayoutJson.contents = Buffer.from(JSON.stringify(ociLayoutJson, null, 2)); | ||||
|     smartfileRepositoriesJson.contents = Buffer.from(JSON.stringify(repositoriesJson, null, 2)); | ||||
|     smartfileIndexJson.contents = Buffer.from( | ||||
|       JSON.stringify(indexJson, null, 2), | ||||
|     ); | ||||
|     smartfileManifestJson.contents = Buffer.from( | ||||
|       JSON.stringify(manifestJson, null, 2), | ||||
|     ); | ||||
|     smartfileOciLayoutJson.contents = Buffer.from( | ||||
|       JSON.stringify(ociLayoutJson, null, 2), | ||||
|     ); | ||||
|     smartfileRepositoriesJson.contents = Buffer.from( | ||||
|       JSON.stringify(repositoriesJson, null, 2), | ||||
|     ); | ||||
|     await Promise.all([ | ||||
|       smartfileIndexJson.write(), | ||||
|       smartfileManifestJson.write(), | ||||
| @@ -77,8 +116,12 @@ export class DockerImageStore { | ||||
|     const tartools = new plugins.smartarchive.TarTools(); | ||||
|     const newTarPack = await tartools.packDirectory(extractionDir); | ||||
|     const finalTarName = `${uniqueProcessingId}.processed.tar`; | ||||
|     const finalTarPath = plugins.path.join(this.options.localDirPath, finalTarName); | ||||
|     const finalWriteStream = plugins.smartfile.fsStream.createWriteStream(finalTarPath); | ||||
|     const finalTarPath = plugins.path.join( | ||||
|       this.options.localDirPath, | ||||
|       finalTarName, | ||||
|     ); | ||||
|     const finalWriteStream = | ||||
|       plugins.smartfile.fsStream.createWriteStream(finalTarPath); | ||||
|     await new Promise((resolve, reject) => { | ||||
|       newTarPack.finalize(); | ||||
|       newTarPack.pipe(finalWriteStream); | ||||
| @@ -87,7 +130,8 @@ export class DockerImageStore { | ||||
|     }); | ||||
|     logger.log('ok', `Repackaged image ${imageName} for s3.`); | ||||
|     await plugins.smartfile.fs.remove(extractionDir); | ||||
|     const finalTarReadStream = plugins.smartfile.fsStream.createReadStream(finalTarPath); | ||||
|     const finalTarReadStream = | ||||
|       plugins.smartfile.fsStream.createReadStream(finalTarPath); | ||||
|     await this.options.bucketDir.fastPutStream({ | ||||
|       stream: finalTarReadStream, | ||||
|       path: `${imageName}.tar`, | ||||
| @@ -102,8 +146,13 @@ export class DockerImageStore { | ||||
|   public async stop() {} | ||||
|  | ||||
|   // Method to retrieve tar stream | ||||
|   public async getImage(imageName: string): Promise<plugins.smartstream.stream.Readable> { | ||||
|     const imagePath = plugins.path.join(this.options.localDirPath, `${imageName}.tar`); | ||||
|   public async getImage( | ||||
|     imageName: string, | ||||
|   ): Promise<plugins.smartstream.stream.Readable> { | ||||
|     const imagePath = plugins.path.join( | ||||
|       this.options.localDirPath, | ||||
|       `${imageName}.tar`, | ||||
|     ); | ||||
|  | ||||
|     if (!(await plugins.smartfile.fs.fileExists(imagePath))) { | ||||
|       throw new Error(`Image ${imageName} does not exist.`); | ||||
|   | ||||
| @@ -6,7 +6,9 @@ import { DockerService } from './classes.service.js'; | ||||
| import { logger } from './logger.js'; | ||||
|  | ||||
| export class DockerNetwork { | ||||
|   public static async getNetworks(dockerHost: DockerHost): Promise<DockerNetwork[]> { | ||||
|   public static async getNetworks( | ||||
|     dockerHost: DockerHost, | ||||
|   ): Promise<DockerNetwork[]> { | ||||
|     const dockerNetworks: DockerNetwork[] = []; | ||||
|     const response = await dockerHost.request('GET', '/networks'); | ||||
|     for (const networkObject of response.body) { | ||||
| @@ -17,14 +19,19 @@ export class DockerNetwork { | ||||
|     return dockerNetworks; | ||||
|   } | ||||
|  | ||||
|   public static async getNetworkByName(dockerHost: DockerHost, dockerNetworkNameArg: string) { | ||||
|   public static async getNetworkByName( | ||||
|     dockerHost: DockerHost, | ||||
|     dockerNetworkNameArg: string, | ||||
|   ) { | ||||
|     const networks = await DockerNetwork.getNetworks(dockerHost); | ||||
|     return networks.find((dockerNetwork) => dockerNetwork.Name === dockerNetworkNameArg); | ||||
|     return networks.find( | ||||
|       (dockerNetwork) => dockerNetwork.Name === dockerNetworkNameArg, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public static async createNetwork( | ||||
|     dockerHost: DockerHost, | ||||
|     networkCreationDescriptor: interfaces.INetworkCreationDescriptor | ||||
|     networkCreationDescriptor: interfaces.INetworkCreationDescriptor, | ||||
|   ): Promise<DockerNetwork> { | ||||
|     const response = await dockerHost.request('POST', '/networks/create', { | ||||
|       Name: networkCreationDescriptor.Name, | ||||
| @@ -47,9 +54,15 @@ export class DockerNetwork { | ||||
|     }); | ||||
|     if (response.statusCode < 300) { | ||||
|       logger.log('info', 'Created network successfully'); | ||||
|       return await DockerNetwork.getNetworkByName(dockerHost, networkCreationDescriptor.Name); | ||||
|       return await DockerNetwork.getNetworkByName( | ||||
|         dockerHost, | ||||
|         networkCreationDescriptor.Name, | ||||
|       ); | ||||
|     } else { | ||||
|       logger.log('error', 'There has been an error creating the wanted network'); | ||||
|       logger.log( | ||||
|         'error', | ||||
|         'There has been an error creating the wanted network', | ||||
|       ); | ||||
|       return null; | ||||
|     } | ||||
|   } | ||||
| @@ -75,7 +88,7 @@ export class DockerNetwork { | ||||
|         Subnet: string; | ||||
|         IPRange: string; | ||||
|         Gateway: string; | ||||
|       } | ||||
|       }, | ||||
|     ]; | ||||
|   }; | ||||
|  | ||||
| @@ -87,7 +100,10 @@ export class DockerNetwork { | ||||
|    * removes the network | ||||
|    */ | ||||
|   public async remove() { | ||||
|     const response = await this.dockerHost.request('DELETE', `/networks/${this.Id}`); | ||||
|     const response = await this.dockerHost.request( | ||||
|       'DELETE', | ||||
|       `/networks/${this.Id}`, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public async getContainersOnNetwork(): Promise< | ||||
| @@ -100,7 +116,10 @@ export class DockerNetwork { | ||||
|     }> | ||||
|   > { | ||||
|     const returnArray = []; | ||||
|     const response = await this.dockerHost.request('GET', `/networks/${this.Id}`); | ||||
|     const response = await this.dockerHost.request( | ||||
|       'GET', | ||||
|       `/networks/${this.Id}`, | ||||
|     ); | ||||
|     for (const key of Object.keys(response.body.Containers)) { | ||||
|       returnArray.push(response.body.Containers[key]); | ||||
|     } | ||||
|   | ||||
| @@ -22,14 +22,17 @@ export class DockerSecret { | ||||
|     return secrets.find((secret) => secret.ID === idArg); | ||||
|   } | ||||
|  | ||||
|   public static async getSecretByName(dockerHostArg: DockerHost, nameArg: string) { | ||||
|   public static async getSecretByName( | ||||
|     dockerHostArg: DockerHost, | ||||
|     nameArg: string, | ||||
|   ) { | ||||
|     const secrets = await this.getSecrets(dockerHostArg); | ||||
|     return secrets.find((secret) => secret.Spec.Name === nameArg); | ||||
|   } | ||||
|  | ||||
|   public static async createSecret( | ||||
|     dockerHostArg: DockerHost, | ||||
|     secretDescriptor: interfaces.ISecretCreationDescriptor | ||||
|     secretDescriptor: interfaces.ISecretCreationDescriptor, | ||||
|   ) { | ||||
|     const labels: interfaces.TLabels = { | ||||
|       ...secretDescriptor.labels, | ||||
| @@ -45,7 +48,7 @@ export class DockerSecret { | ||||
|     Object.assign(newSecretInstance, response.body); | ||||
|     Object.assign( | ||||
|       newSecretInstance, | ||||
|       await DockerSecret.getSecretByID(dockerHostArg, newSecretInstance.ID) | ||||
|       await DockerSecret.getSecretByID(dockerHostArg, newSecretInstance.ID), | ||||
|     ); | ||||
|     return newSecretInstance; | ||||
|   } | ||||
| @@ -77,7 +80,7 @@ export class DockerSecret { | ||||
|         Name: this.Spec.Name, | ||||
|         Labels: this.Spec.Labels, | ||||
|         Data: plugins.smartstring.base64.encode(contentArg), | ||||
|       } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ export class DockerService { | ||||
|  | ||||
|   public static async getServiceByName( | ||||
|     dockerHost: DockerHost, | ||||
|     networkName: string | ||||
|     networkName: string, | ||||
|   ): Promise<DockerService> { | ||||
|     const allServices = await DockerService.getServices(dockerHost); | ||||
|     const wantedService = allServices.find((service) => { | ||||
| @@ -35,10 +35,13 @@ export class DockerService { | ||||
|    */ | ||||
|   public static async createService( | ||||
|     dockerHost: DockerHost, | ||||
|     serviceCreationDescriptor: interfaces.IServiceCreationDescriptor | ||||
|     serviceCreationDescriptor: interfaces.IServiceCreationDescriptor, | ||||
|   ): Promise<DockerService> { | ||||
|     // lets get the image | ||||
|     logger.log('info', `now creating service ${serviceCreationDescriptor.name}`); | ||||
|     logger.log( | ||||
|       'info', | ||||
|       `now creating service ${serviceCreationDescriptor.name}`, | ||||
|     ); | ||||
|  | ||||
|     // await serviceCreationDescriptor.image.pullLatestImageFromRegistry(); | ||||
|     const serviceVersion = await serviceCreationDescriptor.image.getVersion(); | ||||
| @@ -71,8 +74,12 @@ export class DockerService { | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     if (serviceCreationDescriptor.resources && serviceCreationDescriptor.resources.volumeMounts) { | ||||
|       for (const volumeMount of serviceCreationDescriptor.resources.volumeMounts) { | ||||
|     if ( | ||||
|       serviceCreationDescriptor.resources && | ||||
|       serviceCreationDescriptor.resources.volumeMounts | ||||
|     ) { | ||||
|       for (const volumeMount of serviceCreationDescriptor.resources | ||||
|         .volumeMounts) { | ||||
|         mounts.push({ | ||||
|           Target: volumeMount.containerFsPath, | ||||
|           Source: volumeMount.hostFsPath, | ||||
| @@ -130,7 +137,8 @@ export class DockerService { | ||||
|     // lets configure limits | ||||
|  | ||||
|     const memoryLimitMB = | ||||
|       serviceCreationDescriptor.resources && serviceCreationDescriptor.resources.memorySizeMB | ||||
|       serviceCreationDescriptor.resources && | ||||
|       serviceCreationDescriptor.resources.memorySizeMB | ||||
|         ? serviceCreationDescriptor.resources.memorySizeMB | ||||
|         : 1000; | ||||
|  | ||||
| @@ -139,7 +147,8 @@ export class DockerService { | ||||
|     }; | ||||
|  | ||||
|     if (serviceCreationDescriptor.resources) { | ||||
|       limits.MemoryBytes = serviceCreationDescriptor.resources.memorySizeMB * 1000000; | ||||
|       limits.MemoryBytes = | ||||
|         serviceCreationDescriptor.resources.memorySizeMB * 1000000; | ||||
|     } | ||||
|  | ||||
|     const response = await dockerHost.request('POST', '/services/create', { | ||||
| @@ -182,7 +191,7 @@ export class DockerService { | ||||
|  | ||||
|     const createdService = await DockerService.getServiceByName( | ||||
|       dockerHost, | ||||
|       serviceCreationDescriptor.name | ||||
|       serviceCreationDescriptor.name, | ||||
|     ); | ||||
|     return createdService; | ||||
|   } | ||||
| @@ -228,7 +237,10 @@ export class DockerService { | ||||
|   } | ||||
|  | ||||
|   public async reReadFromDockerEngine() { | ||||
|     const dockerData = await this.dockerHostRef.request('GET', `/services/${this.ID}`); | ||||
|     const dockerData = await this.dockerHostRef.request( | ||||
|       'GET', | ||||
|       `/services/${this.ID}`, | ||||
|     ); | ||||
|     // TODO: Better assign: Object.assign(this, dockerData); | ||||
|   } | ||||
|  | ||||
| @@ -236,14 +248,21 @@ export class DockerService { | ||||
|     // TODO: implement digest based update recognition | ||||
|  | ||||
|     await this.reReadFromDockerEngine(); | ||||
|     const dockerImage = await DockerImage.createFromRegistry(this.dockerHostRef, { | ||||
|       creationObject: { | ||||
|         imageUrl: this.Spec.TaskTemplate.ContainerSpec.Image, | ||||
|       } | ||||
|     }); | ||||
|     const dockerImage = await DockerImage.createFromRegistry( | ||||
|       this.dockerHostRef, | ||||
|       { | ||||
|         creationObject: { | ||||
|           imageUrl: this.Spec.TaskTemplate.ContainerSpec.Image, | ||||
|         }, | ||||
|       }, | ||||
|     ); | ||||
|  | ||||
|     const imageVersion = new plugins.smartversion.SmartVersion(dockerImage.Labels.version); | ||||
|     const serviceVersion = new plugins.smartversion.SmartVersion(this.Spec.Labels.version); | ||||
|     const imageVersion = new plugins.smartversion.SmartVersion( | ||||
|       dockerImage.Labels.version, | ||||
|     ); | ||||
|     const serviceVersion = new plugins.smartversion.SmartVersion( | ||||
|       this.Spec.Labels.version, | ||||
|     ); | ||||
|     if (imageVersion.greaterThan(serviceVersion)) { | ||||
|       console.log(`service ${this.Spec.Name}  needs to be updated`); | ||||
|       return true; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import * as plugins from './plugins.js'; | ||||
|  | ||||
| export const packageDir = plugins.path.resolve( | ||||
|   plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), | ||||
|   '../' | ||||
|   '../', | ||||
| ); | ||||
|  | ||||
| export const nogitDir = plugins.path.resolve(packageDir, '.nogit/'); | ||||
|   | ||||
| @@ -6,9 +6,9 @@ | ||||
|     "module": "NodeNext", | ||||
|     "moduleResolution": "NodeNext", | ||||
|     "esModuleInterop": true, | ||||
|     "verbatimModuleSyntax": true | ||||
|     "verbatimModuleSyntax": true, | ||||
|     "baseUrl": ".", | ||||
|     "paths": {} | ||||
|   }, | ||||
|   "exclude": [ | ||||
|     "dist_*/**/*.d.ts" | ||||
|   ] | ||||
|   "exclude": ["dist_*/**/*.d.ts"] | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user