Compare commits

...

174 Commits

Author SHA1 Message Date
6c8095260d 6.0.1
Some checks failed
Default (tags) / security (push) Failing after 22s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-10-26 17:44:43 +00:00
95c4ae3b16 fix(tests): Migrate tests to Deno native runner and update Deno config 2025-10-26 17:44:43 +00:00
536ebca6ab 6.0.0
Some checks failed
Default (tags) / security (push) Failing after 22s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-10-26 15:23:56 +00:00
88f64536c2 BREAKING CHANGE(szci): Rename project from npmci to szci and migrate runtime to Deno; add compiled binaries, installer and wrapper; update imports, env handling and package metadata 2025-10-26 15:23:56 +00:00
4854d27a19 4.1.37
Some checks failed
Default (tags) / security (push) Failing after 15s
Default (tags) / test (push) Successful in 1m28s
Default (tags) / metadata (push) Failing after 11s
Default (tags) / release (push) Failing after 22s
2024-11-17 01:01:22 +01:00
75a0e8a7d8 fix(docker): Enhanced base image extraction logic from Dockerfile 2024-11-17 01:01:22 +01:00
43eb19f772 4.1.36
Some checks failed
Default (tags) / security (push) Failing after 16s
Default (tags) / test (push) Successful in 1m26s
Default (tags) / metadata (push) Failing after 11s
Default (tags) / release (push) Failing after 21s
2024-11-17 00:50:43 +01:00
dc2665d250 fix(docker): Improve logging for Dockerfile build order with base image details. 2024-11-17 00:50:43 +01:00
00f324e151 4.1.35
Some checks failed
Default (tags) / security (push) Failing after 16s
Default (tags) / test (push) Successful in 1m32s
Default (tags) / metadata (push) Failing after 10s
Default (tags) / release (push) Failing after 21s
2024-11-17 00:32:56 +01:00
e38cc40f11 fix(docker): Fix Dockerfile dependency sorting and enhance environment variable handling for GitHub repos 2024-11-17 00:32:56 +01:00
e9e8acafe4 4.1.34 2024-11-05 02:39:53 +01:00
c763db40bb fix(connector): Remove unused typedrequest implementation in cloudlyconnector 2024-11-05 02:39:53 +01:00
01256480c4 4.1.33 2024-11-05 02:38:21 +01:00
c6918399bf fix(core): Updated dependencies and improved npm preparation logic. 2024-11-05 02:38:21 +01:00
66d28e5081 4.1.32 2024-10-23 21:50:30 +02:00
9de77139ea fix(dependencies): Update project dependencies to latest versions 2024-10-23 21:50:30 +02:00
7b4bf10cc0 4.1.31 2024-05-24 15:54:33 +02:00
9eaa6347c1 fix(core): update 2024-05-24 15:54:32 +02:00
6e4c967917 4.1.30 2024-05-24 00:17:31 +02:00
52a8f42fc9 fix(core): update 2024-05-24 00:17:31 +02:00
8e9a61bbb2 4.1.29 2024-05-23 21:47:07 +02:00
dc809a6023 fix(core): update 2024-05-23 21:47:06 +02:00
7eeca992b0 4.1.28 2024-05-17 16:23:48 +02:00
d018102014 fix(core): update 2024-05-17 16:23:47 +02:00
838f2d6959 4.1.27 2023-08-24 17:25:56 +02:00
0d42e5f6eb fix(core): update 2023-08-24 17:25:55 +02:00
f96de8cdc3 4.1.26 2023-08-09 11:26:22 +02:00
965833916f fix(core): update 2023-08-09 11:26:22 +02:00
81ec1391e3 4.1.25 2023-08-09 11:23:57 +02:00
bbbca44640 fix(core): update 2023-08-09 11:23:56 +02:00
7ba7dc54a1 4.1.24 2023-08-08 23:32:08 +02:00
4f98eeec2a fix(core): update 2023-08-08 23:32:07 +02:00
419a6b9e5f 4.1.23 2023-07-13 16:00:47 +02:00
9c040c34f5 fix(core): update 2023-07-13 16:00:47 +02:00
50d3f2d795 4.1.22 2023-07-12 18:23:32 +02:00
305856b9e1 fix(core): update 2023-07-12 18:23:31 +02:00
3683743f3c 4.1.21 2023-07-12 15:35:39 +02:00
41237e0e5f fix(core): update 2023-07-12 15:35:38 +02:00
bce84a0e74 4.1.20 2023-07-02 00:10:28 +02:00
79bed919d8 fix(core): update 2023-07-02 00:10:27 +02:00
4b202ce00e 4.1.19 2023-07-01 22:09:54 +02:00
16ca787314 fix(core): update 2023-07-01 22:09:54 +02:00
3a9b6f658a 4.1.18 2023-07-01 22:05:44 +02:00
5c5dbf303f fix(core): update 2023-07-01 22:05:43 +02:00
4c07131e51 4.1.17 2023-07-01 19:23:17 +02:00
ba039469ff fix(core): update 2023-07-01 19:23:17 +02:00
49f2498ecd 4.1.16 2023-07-01 19:02:07 +02:00
1060060366 fix(core): update 2023-07-01 19:02:06 +02:00
0fa3a579f7 4.1.15 2023-06-26 10:09:25 +02:00
0e4556d59e fix(core): update 2023-06-26 10:09:24 +02:00
e2e7967fba 4.1.14 2023-06-26 09:28:19 +02:00
45b8d67abf fix(core): update 2023-06-26 09:28:19 +02:00
0a69aa5d62 4.1.13 2023-06-26 02:43:45 +02:00
dfbab1a1df fix(core): update 2023-06-26 02:43:45 +02:00
e58f009a24 4.1.12 2023-06-26 01:03:44 +02:00
2afd9cddc5 fix(core): update 2023-06-26 01:03:44 +02:00
d79c5366ef 4.1.11 2023-06-26 00:09:56 +02:00
8e4f7ad244 fix(core): update 2023-06-26 00:09:54 +02:00
39de3a1601 4.1.10 2023-06-25 23:59:26 +02:00
cb3d2964d1 fix(core): update 2023-06-25 23:59:25 +02:00
6b5390cef8 4.1.9 2023-06-25 23:26:25 +02:00
2736b85de3 fix(core): update 2023-06-25 23:26:25 +02:00
82d7778f59 4.1.8 2023-06-25 23:18:23 +02:00
8c99cc0491 fix(core): update 2023-06-25 23:18:23 +02:00
955e3d0dbe 4.1.7 2023-06-25 23:16:29 +02:00
702ae8bed8 fix(core): update 2023-06-25 23:16:29 +02:00
b6f0723b75 4.1.6 2023-06-25 22:23:31 +02:00
8a2fb30e59 fix(core): update 2023-06-25 22:23:30 +02:00
95b4030120 4.1.5 2023-06-25 21:15:51 +02:00
5c77cfbdc2 fix(core): update 2023-06-25 21:15:50 +02:00
5ea42320a9 4.1.4 2023-05-07 22:35:19 +02:00
d07ebfc9c6 fix(core): update 2023-05-07 22:35:18 +02:00
bbb5718184 4.1.3 2023-05-07 21:30:59 +02:00
0d8b54637c fix(core): update 2023-05-07 21:30:58 +02:00
e51b2e28b9 4.1.2 2022-11-02 18:57:47 +01:00
f767140cc8 fix(core): update 2022-11-02 18:57:47 +01:00
0d4d69f072 4.1.1 2022-11-02 16:43:54 +01:00
a3e628c43f fix(core): update 2022-11-02 16:43:54 +01:00
a58fa135c1 4.1.0 2022-10-24 21:23:14 +02:00
93c7af6c91 feat(precheck): now includes a precheck for more generic runner execution 2022-10-24 21:23:14 +02:00
ad0e12bf7b 4.0.11 2022-10-23 17:19:42 +02:00
498dd6eff6 fix(core): update 2022-10-23 17:19:41 +02:00
b3aa964739 4.0.10 2022-10-11 14:26:43 +02:00
03eb9d2657 fix(core): update 2022-10-11 14:26:42 +02:00
373a838a6a 4.0.9 2022-10-11 14:08:00 +02:00
960e3f4675 fix(core): update 2022-10-11 14:07:59 +02:00
09bf676b58 4.0.8 2022-10-11 13:59:40 +02:00
76ba8e2ab9 fix(core): update 2022-10-11 13:59:39 +02:00
aaaaca2d19 4.0.7 2022-10-11 13:59:16 +02:00
71b27eda17 fix(core): update 2022-10-11 13:59:15 +02:00
2d00882fd7 4.0.6 2022-10-11 13:58:48 +02:00
ba5e69041f fix(core): update 2022-10-11 13:58:48 +02:00
d2871d601a 4.0.5 2022-10-11 13:40:26 +02:00
9c66d88dc0 fix(core): update 2022-10-11 13:40:25 +02:00
fb4c84e1de 4.0.4 2022-10-11 13:38:40 +02:00
57aca36f11 fix(core): update 2022-10-11 13:38:40 +02:00
905f594af1 4.0.3 2022-10-11 13:26:50 +02:00
b788b7f96b fix(core): update 2022-10-11 13:26:49 +02:00
319a2dc41a 4.0.2 2022-10-11 13:08:10 +02:00
e01a998f0e fix(core): update 2022-10-11 13:08:10 +02:00
e40606d97b 4.0.1 2022-10-11 10:53:12 +02:00
449c7b2c04 fix(core): update 2022-10-11 10:53:12 +02:00
006782b57f 4.0.0 2022-10-09 18:15:37 +02:00
d643da29b0 BREAKING CHANGE(core): switch to esm style module 2022-10-09 18:15:37 +02:00
635f92d2bc 3.1.83 2022-01-18 18:10:28 +01:00
9a2cb56094 fix(core): update 2022-01-18 18:10:27 +01:00
5886283002 3.1.82 2021-11-10 12:42:42 +01:00
f886194c9c 3.1.81 2021-11-10 12:40:13 +01:00
e4efec89d9 fix(core): update 2021-11-10 12:40:12 +01:00
dbc12a593f 3.1.80 2021-11-09 20:27:31 +01:00
d7666e862b fix(core): update 2021-11-09 20:27:30 +01:00
e262d29510 3.1.79 2021-11-07 04:20:14 +01:00
858d97cb5c fix(core): update 2021-11-07 04:20:14 +01:00
b8a2df66fe 3.1.78 2021-10-22 01:51:43 +02:00
1c128dd694 fix(core): update 2021-10-22 01:51:43 +02:00
2744d0bf7f 3.1.77 2021-10-19 03:09:50 +02:00
9eb232da39 fix(core): update 2021-10-19 03:09:50 +02:00
52af1d5188 3.1.76 2021-05-14 18:19:43 +00:00
4325f21c8c fix(core): update 2021-05-14 18:19:42 +00:00
6cd3eaceb4 3.1.75 2021-05-14 18:11:13 +00:00
f850c79b6c fix(core): update 2021-05-14 18:11:12 +00:00
efdf789575 3.1.74 2019-11-26 17:47:22 +00:00
6ef6446022 fix(npm cache): now correctly setting it when preparing npm 2019-11-26 17:47:21 +00:00
ef7d85e7c4 3.1.73 2019-11-24 13:52:19 +00:00
93b5d9869b 3.1.72 2019-11-24 11:51:53 +00:00
2a0cfeffe9 fix(core): update 2019-11-24 11:51:52 +00:00
909aafbd5f 3.1.71 2019-11-23 22:36:34 +00:00
91288e2d74 3.1.70 2019-11-23 22:32:09 +00:00
25709b1f9a fix(core): update 2019-11-23 22:32:08 +00:00
8a03d9aa94 3.1.69 2019-11-23 19:46:24 +00:00
bbe1cf770a 3.1.68 2019-11-23 19:46:02 +00:00
ac8190282f fix(core): update 2019-11-23 19:46:02 +00:00
446d140e32 3.1.68 2019-11-23 19:45:00 +00:00
726948651e fix(core): update 2019-11-23 19:44:59 +00:00
dd0a7bb782 3.1.67 2019-10-04 15:18:51 +02:00
fca00ffcf8 fix(core): update 2019-10-04 15:18:51 +02:00
13f6334ae5 3.1.66 2019-10-03 00:00:20 +02:00
7275a858d6 fix(core): update 2019-10-03 00:00:20 +02:00
5a3befe5af 3.1.65 2019-10-02 23:55:55 +02:00
385a93a05e update 2019-10-02 23:55:51 +02:00
b4d444ff05 3.1.64 2019-10-02 14:54:21 +02:00
487bcb9a70 fix(core): update 2019-10-02 14:54:21 +02:00
aaf11b66d7 3.1.63 2019-10-02 11:57:00 +02:00
83d7d46896 fix(core): update 2019-10-02 11:56:59 +02:00
693bda6a49 3.1.62 2019-10-02 11:56:06 +02:00
bfe3e266ee fix(core): update 2019-10-02 11:56:05 +02:00
5f33ebd8a7 3.1.61 2019-10-02 11:33:52 +02:00
f78c80e100 fix(core): update 2019-10-02 11:33:52 +02:00
f4d8656831 3.1.60 2019-09-01 14:21:30 +02:00
2290081ef0 fix(core): update 2019-09-01 14:21:30 +02:00
189d02a16f 3.1.59 2019-09-01 14:11:35 +02:00
55aee04334 fix(core): update 2019-09-01 14:11:35 +02:00
0e407b9b9d 3.1.58 2019-09-01 13:54:00 +02:00
24095bbd40 fix(core): update 2019-09-01 13:54:00 +02:00
f97ee94b5a 3.1.57 2019-09-01 13:51:12 +02:00
4cf7aea374 fix(core): update 2019-09-01 13:51:11 +02:00
6ab5e9cb30 3.1.56 2019-09-01 13:49:28 +02:00
95c1145bf5 3.1.55 2019-09-01 13:49:12 +02:00
ea04a1b788 fix(core): update 2019-09-01 13:49:11 +02:00
3bc2499d09 3.1.54 2019-09-01 13:46:03 +02:00
1f5967ac45 fix(core): update 2019-09-01 13:46:03 +02:00
fd952f086b 3.1.53 2019-09-01 13:45:19 +02:00
79500cb2c2 fix(core): update 2019-09-01 13:45:18 +02:00
6c58864fcf 3.1.52 2019-09-01 13:44:22 +02:00
7ea3ac182d fix(core): update 2019-09-01 13:44:21 +02:00
8979d26005 3.1.51 2019-09-01 13:41:04 +02:00
c8876dac88 fix(core): update 2019-09-01 13:41:03 +02:00
9c8a257c2a 3.1.50 2019-08-30 18:40:00 +02:00
8b77930ece fix(core): update 2019-08-30 18:39:59 +02:00
ba672d030f 3.1.49 2019-08-30 14:51:04 +02:00
8ad7e016e7 fix(core): update 2019-08-30 14:51:03 +02:00
d843311d7b 3.1.48 2019-08-30 13:30:16 +02:00
14ef2cfa9b 3.1.47 2019-08-30 12:24:01 +02:00
648effcf86 fix(core): update 2019-08-30 12:24:00 +02:00
73 changed files with 12294 additions and 4747 deletions

View File

@@ -0,0 +1,66 @@
name: Default (not tags)
on:
push:
tags-ignore:
- '**'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@@ -0,0 +1,108 @@
name: Default (tags)
on:
push:
tags:
- '*'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_LOGIN_DOCKER_GITEA: ${{secrets.NPMCI_DOCKER_REGISTRYURL_DEFAULT}}|${{ gitea.repository_owner }}|${{ secrets.GITEA_TOKEN }}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
release:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Release
run: |
npmci node install stable
npmci npm publish
metadata:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm prepare
npmci npm install
- name: Trigger
run: npmci trigger
- name: Build docs and upload artifacts
run: |
npmci node install stable
npmci npm install
pnpm install -g @git.zone/tsdoc
npmci command tsdoc
continue-on-error: true

8
.gitignore vendored
View File

@@ -15,8 +15,10 @@ node_modules/
# builds
dist/
dist_web/
dist_serve/
dist_ts_web/
dist_*/
# deno
deno.lock
.deno/
# custom

View File

@@ -1,119 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: "$CI_BUILD_STAGE"
stages:
- security
- test
- release
- metadata
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci git mirror
tags:
- docker
- notpriv
snyk:
stage: security
script:
- npmci npm prepare
- npmci command npm install -g snyk
- npmci command npm install --ignore-scripts
- npmci command snyk test
tags:
- docker
- notpriv
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- priv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
release:
stage: release
script:
- npmci node install lts
- npmci npm publish
only:
- tags
tags:
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
script:
- npmci command npm install -g tslint typescript
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- docker
- notpriv
pages:
image: hosttoday/ht-docker-dbase:npmci
services:
- docker:stable-dind
stage: metadata
script:
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

1
.serena/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/cache

71
.serena/project.yml Normal file
View File

@@ -0,0 +1,71 @@
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
# * For C, use cpp
# * For JavaScript, use typescript
# Special requirements:
# * csharp: Requires the presence of a .sln file in the project folder.
language: typescript
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# whether to use the project's gitignore file to ignore files
# Added on 2025-04-07
ignore_all_files_in_gitignore: true
# list of additional paths to ignore
# same syntax as gitignore, so you can use * and **
# Was previously called `ignored_dirs`, please update your config if you are using that.
# Added (renamed) on 2025-04-07
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
project_name: "npmci"

4
.snyk
View File

@@ -1,4 +0,0 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.13.1
ignore: {}
patch: {}

11
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"type": "node-terminal"
}
]
}

27
.vscode/settings.json vendored
View File

@@ -1,3 +1,26 @@
{
"workbench.colorCustomizations": {}
}
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"schema": {
"type": "object",
"properties": {
"npmci": {
"type": "object",
"description": "settings for npmci"
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}
}
}
}
]
}

108
bin/szci-wrapper.js Executable file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env node
/**
* SZCI npm wrapper
* This script executes the appropriate pre-compiled binary based on the current platform
*/
import { spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { existsSync } from 'fs';
import { platform, arch } from 'os';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Get the binary name for the current platform
*/
function getBinaryName() {
const plat = platform();
const architecture = arch();
// Map Node's platform/arch to our binary naming
const platformMap = {
'darwin': 'macos',
'linux': 'linux',
'win32': 'windows'
};
const archMap = {
'x64': 'x64',
'arm64': 'arm64'
};
const mappedPlatform = platformMap[plat];
const mappedArch = archMap[architecture];
if (!mappedPlatform || !mappedArch) {
console.error(`Error: Unsupported platform/architecture: ${plat}/${architecture}`);
console.error('Supported platforms: Linux, macOS, Windows');
console.error('Supported architectures: x64, arm64');
process.exit(1);
}
// Construct binary name
let binaryName = `szci-${mappedPlatform}-${mappedArch}`;
if (plat === 'win32') {
binaryName += '.exe';
}
return binaryName;
}
/**
* Execute the binary
*/
function executeBinary() {
const binaryName = getBinaryName();
const binaryPath = join(__dirname, '..', 'dist', 'binaries', binaryName);
// Check if binary exists
if (!existsSync(binaryPath)) {
console.error(`Error: Binary not found at ${binaryPath}`);
console.error('This might happen if:');
console.error('1. The postinstall script failed to run');
console.error('2. The platform is not supported');
console.error('3. The package was not installed correctly');
console.error('');
console.error('Try reinstalling the package:');
console.error(' npm uninstall -g @ship.zone/szci');
console.error(' npm install -g @ship.zone/szci');
process.exit(1);
}
// Spawn the binary with all arguments passed through
const child = spawn(binaryPath, process.argv.slice(2), {
stdio: 'inherit',
shell: false
});
// Handle child process events
child.on('error', (err) => {
console.error(`Error executing szci: ${err.message}`);
process.exit(1);
});
child.on('exit', (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
} else {
process.exit(code || 0);
}
});
// Forward signals to child process
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
signals.forEach(signal => {
process.on(signal, () => {
if (!child.killed) {
child.kill(signal);
}
});
});
}
// Execute
executeBinary();

115
changelog.md Normal file
View File

@@ -0,0 +1,115 @@
# Changelog
## 2025-10-26 - 6.0.1 - fix(tests)
Migrate tests to Deno native runner and update Deno config
- Convert test suites from tap/tapbundle to Deno.test and @std/assert
- Replace CommonJS-style runtime imports with .ts module imports for Deno (test files updated)
- Use Deno.env.set to configure test environment variables and restore working directory after tests
- Update test/test.cloudly.ts to import CloudlyConnector directly and disable TLS verification for tests
- Adjust deno.json version field (6.0.0 -> 5.0.0) as part of Deno configuration changes
- Add local project .claude/settings.local.json for tooling permissions
## 2025-10-26 - 6.0.0 - BREAKING CHANGE(szci)
Rename project from npmci to szci and migrate runtime to Deno; add compiled binaries, installer and wrapper; update imports, env handling and package metadata
- Major rename/refactor: Npmci -> Szci across the codebase (classes, filenames, modules and exports).
- Migrate runtime to Deno: add deno.json, mod.ts entry point, use Deno.std imports and .ts module imports throughout.
- Add compilation and distribution tooling: scripts/compile-all.sh, scripts/install-binary.js, bin/szci-wrapper.js and dist/binaries layout for prebuilt executables.
- Package metadata updated: package.json renamed/rewritten for @ship.zone/szci and bumped to 5.0.0, updated publishConfig, files and scripts.
- Environment API changes: replaced process.env usages with Deno.env accessors and updated path constants (Szci paths).
- Refactored helper modules: npmci.bash -> szci.bash, updated smartshell/bash wrappers and other manager modules to Deno patterns.
- Tests and imports updated to new module paths and package layout; .gitignore updated to ignore deno artifacts.
- Breaking changes to callers: CLI name, class names, programmatic API, binary installation and environment handling have changed and may require updates in integrations and CI configurations.
## 2024-11-17 - 4.1.37 - fix(docker)
Enhanced base image extraction logic from Dockerfile
- Improved dockerBaseImage to accurately extract base images considering ARG variables.
- Added support for parsing Dockerfile content without external libraries.
- Enhanced error handling for missing FROM instructions.
## 2024-11-17 - 4.1.36 - fix(docker)
Improve logging for Dockerfile build order with base image details.
- Enhance logging in Dockerfile sorting process to include base image information.
## 2024-11-17 - 4.1.35 - fix(docker)
Fix Dockerfile dependency sorting and enhance environment variable handling for GitHub repos
- Refined the algorithm for sorting Dockerfiles based on dependencies to ensure proper build order.
- Enhanced environment variable handling in the NpmciEnv class to support conditional assignments.
- Updated various dependencies in package.json for improved performance and compatibility.
- Added error handling to circular dependency detection in Dockerfile sorting.
## 2024-11-05 - 4.1.34 - fix(connector)
Remove unused typedrequest implementation in cloudlyconnector
- Removed commented out code that initialized typedrequest in CloudlyConnector.
## 2024-11-05 - 4.1.33 - fix(core)
Updated dependencies and improved npm preparation logic.
- Updated @git.zone/tsbuild from ^2.1.84 to ^2.2.0.
- Updated @git.zone/tsrun from ^1.2.49 to ^1.3.3.
- Updated @types/node from ^22.7.9 to ^22.8.7.
- Updated @serve.zone/api from ^1.2.1 to ^4.3.1.
- Improved npm preparation logic to handle empty tokens gracefully.
## 2024-10-23 - 4.1.32 - fix(dependencies)
Update project dependencies to latest versions
- Updated development dependencies, including @git.zone/tsbuild and @git.zone/tsrun.
- Updated production dependencies such as @api.global/typedrequest and @push.rocks/smartfile.
## 2022-10-24 - 4.0.11 - prerelease
now includes a precheck for more generic runner execution
- Implemented a precheck feature for runners.
## 2022-10-09 to 2022-10-11 - 4.0.0 to 4.0.10 - migration
internal migrations and fixes
- Major switch to ESM style module: **BREAKING CHANGE**.
- Multiple fixes in core functionalities and module updates.
## 2019-11-26 - 3.1.73 - fixes
correctly setting npm cache and other updates
- Ensured correct npm cache setting during preparation.
- Various core updates.
## 2018-12-23 - 3.1.19 - privacy updates
enhanced mirroring controls for private code
- Now refusing to mirror private code.
## 2018-11-24 - 3.1.2 - ci improvement
removed unnecessary build dependency
- Removed npmts build dependency in CI pipeline.
## 2018-09-22 - 3.0.59 - enhancement
integrated smartlog for improved logging
- Logs now utilize smartlog for better management.
## 2017-09-08 - 3.0.14 - analytics
added analytics features
- Enabled analytics throughout the system.
## 2017-08-29 - 3.0.9 - docker enhancements
docker improvements and build args implementation
- Implemented working `dockerBuildArgEnvMap`.
## 2017-07-27 - 2.4.0 - stability improvements
various updates to stabilize the environment
- Fixed npmci versioning issues.
## 2016-11-25 - 2.3.24 - global tools
improved handling for global tool installations
- Improved install handling for needed global tools.

3
cli.js
View File

@@ -1,3 +0,0 @@
#!/usr/bin/env node
process.env.CLI_CALL = 'true';
require('./dist/index');

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env node
process.env.CLI_CALL = 'true';
require('@gitzone/tsrun');
require('./ts/index');

67
deno.json Normal file
View File

@@ -0,0 +1,67 @@
{
"name": "@ship.zone/szci",
"version": "6.0.1",
"exports": "./mod.ts",
"nodeModulesDir": "auto",
"tasks": {
"dev": "deno run --allow-all mod.ts",
"compile": "deno task compile:all",
"compile:all": "bash scripts/compile-all.sh",
"test": "deno test --allow-all test/",
"test:watch": "deno test --allow-all --watch test/",
"check": "deno check mod.ts",
"fmt": "deno fmt",
"lint": "deno lint"
},
"lint": {
"rules": {
"tags": [
"recommended"
]
}
},
"fmt": {
"useTabs": false,
"lineWidth": 100,
"indentWidth": 2,
"semiColons": true,
"singleQuote": true
},
"compilerOptions": {
"lib": [
"deno.window"
],
"strict": true
},
"imports": {
"@std/path": "jsr:@std/path@^1.0.0",
"@std/fmt": "jsr:@std/fmt@^1.0.0",
"@std/cli": "jsr:@std/cli@^1.0.0",
"@std/assert": "jsr:@std/assert@^1.0.0",
"@api.global/typedrequest": "npm:@api.global/typedrequest@^3.1.10",
"@push.rocks/lik": "npm:@push.rocks/lik@^6.1.0",
"@push.rocks/npmextra": "npm:@push.rocks/npmextra@^5.1.2",
"@push.rocks/projectinfo": "npm:@push.rocks/projectinfo@^5.0.2",
"@push.rocks/qenv": "npm:@push.rocks/qenv@^6.0.2",
"@push.rocks/smartanalytics": "npm:@push.rocks/smartanalytics@^2.0.15",
"@push.rocks/smartcli": "npm:@push.rocks/smartcli@^4.0.11",
"@push.rocks/smartdelay": "npm:@push.rocks/smartdelay@^3.0.5",
"@push.rocks/smartenv": "npm:@push.rocks/smartenv@^5.0.5",
"@push.rocks/smartfile": "npm:@push.rocks/smartfile@^11.0.21",
"@push.rocks/smartgit": "npm:@push.rocks/smartgit@^3.1.1",
"@push.rocks/smartlog": "npm:@push.rocks/smartlog@^3.0.7",
"@push.rocks/smartlog-destination-local": "npm:@push.rocks/smartlog-destination-local@^9.0.0",
"@push.rocks/smartobject": "npm:@push.rocks/smartobject@^1.0.12",
"@push.rocks/smartpath": "npm:@push.rocks/smartpath@^5.0.11",
"@push.rocks/smartpromise": "npm:@push.rocks/smartpromise@^4.0.4",
"@push.rocks/smartrequest": "npm:@push.rocks/smartrequest@^2.0.23",
"@push.rocks/smartshell": "npm:@push.rocks/smartshell@^3.0.6",
"@push.rocks/smartsocket": "npm:@push.rocks/smartsocket@^2.0.22",
"@push.rocks/smartssh": "npm:@push.rocks/smartssh@^2.0.1",
"@push.rocks/smartstring": "npm:@push.rocks/smartstring@^4.0.8",
"@push.rocks/smartexpect": "npm:@push.rocks/smartexpect@^1.0.15",
"@serve.zone/api": "npm:@serve.zone/api@^4.3.11",
"@tsclass/tsclass": "npm:@tsclass/tsclass@^4.1.2",
"through2": "npm:through2@^4.0.2"
}
}

257
install.sh Executable file
View File

@@ -0,0 +1,257 @@
#!/bin/bash
# SZCI Installer Script (v5.0+)
# Downloads and installs pre-compiled SZCI binary from Gitea releases
#
# Usage:
# Direct piped installation (recommended):
# curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash
#
# With version specification:
# curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash -s -- --version v5.0.0
#
# Options:
# -h, --help Show this help message
# --version VERSION Install specific version (e.g., v5.0.0)
# --install-dir DIR Installation directory (default: /opt/szci)
set -e
# Default values
SHOW_HELP=0
SPECIFIED_VERSION=""
INSTALL_DIR="/opt/szci"
GITEA_BASE_URL="https://code.foss.global"
GITEA_REPO="ship.zone/szci"
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
SHOW_HELP=1
shift
;;
--version)
SPECIFIED_VERSION="$2"
shift 2
;;
--install-dir)
INSTALL_DIR="$2"
shift 2
;;
*)
echo "Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
if [ $SHOW_HELP -eq 1 ]; then
echo "SZCI Installer Script (v5.0+)"
echo "Downloads and installs pre-compiled SZCI binary"
echo ""
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " --version VERSION Install specific version (e.g., v5.0.0)"
echo " --install-dir DIR Installation directory (default: /opt/szci)"
echo ""
echo "Examples:"
echo " # Install latest version"
echo " curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash"
echo ""
echo " # Install specific version"
echo " curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash -s -- --version v5.0.0"
exit 0
fi
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root (sudo bash install.sh or pipe to sudo bash)"
exit 1
fi
# Helper function to detect OS and architecture
detect_platform() {
local os=$(uname -s)
local arch=$(uname -m)
# Map OS
case "$os" in
Linux)
os_name="linux"
;;
Darwin)
os_name="macos"
;;
MINGW*|MSYS*|CYGWIN*)
os_name="windows"
;;
*)
echo "Error: Unsupported operating system: $os"
echo "Supported: Linux, macOS, Windows"
exit 1
;;
esac
# Map architecture
case "$arch" in
x86_64|amd64)
arch_name="x64"
;;
aarch64|arm64)
arch_name="arm64"
;;
*)
echo "Error: Unsupported architecture: $arch"
echo "Supported: x86_64/amd64 (x64), aarch64/arm64 (arm64)"
exit 1
;;
esac
# Construct binary name
if [ "$os_name" = "windows" ]; then
echo "szci-${os_name}-${arch_name}.exe"
else
echo "szci-${os_name}-${arch_name}"
fi
}
# Get latest release version from Gitea API
get_latest_version() {
echo "Fetching latest release version from Gitea..." >&2
local api_url="${GITEA_BASE_URL}/api/v1/repos/${GITEA_REPO}/releases/latest"
local response=$(curl -sSL "$api_url" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
echo "Error: Failed to fetch latest release information from Gitea API" >&2
echo "URL: $api_url" >&2
exit 1
fi
# Extract tag_name from JSON response
local version=$(echo "$response" | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)
if [ -z "$version" ]; then
echo "Error: Could not determine latest version from API response" >&2
exit 1
fi
echo "$version"
}
# Main installation process
echo "================================================"
echo " SZCI Installation Script (v5.0+)"
echo " Serve Zone CI/CD Tool"
echo "================================================"
echo ""
# Detect platform
BINARY_NAME=$(detect_platform)
echo "Detected platform: $BINARY_NAME"
echo ""
# Determine version to install
if [ -n "$SPECIFIED_VERSION" ]; then
VERSION="$SPECIFIED_VERSION"
echo "Installing specified version: $VERSION"
else
VERSION=$(get_latest_version)
echo "Installing latest version: $VERSION"
fi
echo ""
# Construct download URL
DOWNLOAD_URL="${GITEA_BASE_URL}/${GITEA_REPO}/releases/download/${VERSION}/${BINARY_NAME}"
echo "Download URL: $DOWNLOAD_URL"
echo ""
# Clean installation directory - ensure only binary exists
if [ -d "$INSTALL_DIR" ]; then
echo "Cleaning installation directory: $INSTALL_DIR"
rm -rf "$INSTALL_DIR"
fi
# Create fresh installation directory
echo "Creating installation directory: $INSTALL_DIR"
mkdir -p "$INSTALL_DIR"
# Download binary
echo "Downloading SZCI binary..."
TEMP_FILE="$INSTALL_DIR/szci.download"
curl -sSL "$DOWNLOAD_URL" -o "$TEMP_FILE"
if [ $? -ne 0 ]; then
echo "Error: Failed to download binary from $DOWNLOAD_URL"
echo ""
echo "Please check:"
echo " 1. Your internet connection"
echo " 2. The specified version exists: ${GITEA_BASE_URL}/${GITEA_REPO}/releases"
echo " 3. The platform binary is available for this release"
rm -f "$TEMP_FILE"
exit 1
fi
# Check if download was successful (file exists and not empty)
if [ ! -s "$TEMP_FILE" ]; then
echo "Error: Downloaded file is empty or does not exist"
rm -f "$TEMP_FILE"
exit 1
fi
# Move to final location
BINARY_PATH="$INSTALL_DIR/szci"
mv "$TEMP_FILE" "$BINARY_PATH"
if [ $? -ne 0 ] || [ ! -f "$BINARY_PATH" ]; then
echo "Error: Failed to move binary to $BINARY_PATH"
rm -f "$TEMP_FILE" 2>/dev/null
exit 1
fi
# Make executable
chmod +x "$BINARY_PATH"
if [ $? -ne 0 ]; then
echo "Error: Failed to make binary executable"
exit 1
fi
echo "Binary installed successfully to: $BINARY_PATH"
echo ""
# Check if /usr/local/bin is in PATH
if [[ ":$PATH:" == *":/usr/local/bin:"* ]]; then
BIN_DIR="/usr/local/bin"
else
BIN_DIR="/usr/bin"
fi
# Create symlink for global access
ln -sf "$BINARY_PATH" "$BIN_DIR/szci"
echo "Symlink created: $BIN_DIR/szci -> $BINARY_PATH"
echo ""
echo "================================================"
echo " SZCI Installation Complete!"
echo "================================================"
echo ""
echo "Installation details:"
echo " Binary location: $BINARY_PATH"
echo " Symlink location: $BIN_DIR/szci"
echo " Version: $VERSION"
echo ""
echo "Get started:"
echo " szci --version"
echo " szci --help"
echo " szci node install stable # Install Node.js"
echo " szci docker build # Build Docker images"
echo " szci npm test # Run tests"
echo ""
echo "For CI/CD integration examples, visit:"
echo " https://code.foss.global/ship.zone/szci"
echo ""

View File

48
mod.ts Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env -S deno run --allow-all
/**
* SZCI - Serve Zone CI/CD Tool
*
* A command-line tool for streamlining Node.js and Docker workflows
* within CI environments, particularly GitLab CI, GitHub CI, and Gitea CI.
*
* Required Permissions:
* - --allow-net: Network access for Docker registries, npm, git operations
* - --allow-read: Read configuration files, Dockerfiles, package.json
* - --allow-write: Write configuration files, build artifacts
* - --allow-run: Execute system commands (docker, git, npm, ssh)
* - --allow-sys: Access system information (OS details)
* - --allow-env: Read/write environment variables
*
* @module
*/
import { Szci } from './ts/szci.classes.szci.ts';
/**
* Main entry point for the SZCI application
* Parses command-line arguments and executes the requested command
*/
async function main(): Promise<void> {
// Create Szci instance
const szciInstance = new Szci();
// Start the CLI
// Deno.args is already 0-indexed (unlike Node's process.argv which starts at index 2)
// The smartcli library may expect process.argv format, so we might need to prepend placeholders
await szciInstance.start();
}
// Execute main and handle errors
if (import.meta.main) {
try {
await main();
} catch (error) {
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
Deno.exit(1);
}
}
// Export for programmatic use
export { Szci } from './ts/szci.classes.szci.ts';
export { Dockerfile } from './ts/manager.docker/mod.classes.dockerfile.ts';

View File

@@ -9,13 +9,31 @@
"command": "npmci test stable"
},
"gitzone": {
"projectType": "npm",
"module": {
"githost": "gitlab.com",
"gitscope": "shipzone",
"gitscope": "ship.zone",
"gitrepo": "npmci",
"shortDescription": "node and docker in gitlab ci on steroids",
"npmPackagename": "@shipzone/npmci",
"license": "MIT"
"description": "A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities.",
"npmPackagename": "@ship.zone/npmci",
"license": "MIT",
"keywords": [
"Node.js",
"Docker",
"GitLab CI",
"GitHub CI",
"Gitea CI",
"CI/CD",
"automation",
"npm",
"TypeScript",
"cloud",
"SSH",
"registry",
"container management",
"continuous integration",
"continuous deployment"
]
}
}
}

3794
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +1,67 @@
{
"name": "@shipzone/npmci",
"version": "3.1.46",
"private": false,
"description": "node and docker in gitlab ci on steroids",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"bin": {
"npmci": "cli.js"
},
"scripts": {
"test": "(rm -f config.json) && tstest test/",
"build": "(rm -f config.json) && tsbuild && (npm run testVersion)",
"testVersion": "(cd test/assets/ && node ../../cli.js -v)"
"name": "@ship.zone/szci",
"version": "6.0.1",
"description": "Serve Zone CI - A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities. Powered by Deno with standalone executables.",
"keywords": [
"Node.js",
"Docker",
"GitLab CI",
"GitHub CI",
"Gitea CI",
"CI/CD",
"automation",
"npm",
"TypeScript",
"Deno",
"cloud",
"SSH",
"registry",
"container management",
"continuous integration",
"continuous deployment"
],
"homepage": "https://code.foss.global/ship.zone/szci",
"bugs": {
"url": "https://code.foss.global/ship.zone/szci/issues"
},
"repository": {
"type": "git",
"url": "git+ssh://git@gitlab.com/gitzone/npmci.git"
"url": "git+https://code.foss.global/ship.zone/szci.git"
},
"author": "Lossless GmbH",
"license": "MIT",
"bugs": {
"url": "https://gitlab.com/gitzone/npmci/issues"
"type": "module",
"bin": {
"szci": "./bin/szci-wrapper.js"
},
"homepage": "https://gitlab.com/gitzone/npmci#README",
"devDependencies": {
"@gitzone/tsbuild": "^2.1.17",
"@gitzone/tsrun": "^1.2.6",
"@gitzone/tstest": "^1.0.24",
"@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^12.7.3",
"tslint": "^5.19.0",
"tslint-config-prettier": "^1.18.0"
},
"dependencies": {
"@apiglobal/typedrequest": "^1.0.17",
"@pushrocks/lik": "^3.0.11",
"@pushrocks/npmextra": "^3.0.5",
"@pushrocks/projectinfo": "^4.0.2",
"@pushrocks/qenv": "^4.0.4",
"@pushrocks/smartanalytics": "^2.0.15",
"@pushrocks/smartcli": "^3.0.7",
"@pushrocks/smartdelay": "^2.0.3",
"@pushrocks/smartfile": "^7.0.2",
"@pushrocks/smartgit": "^1.0.13",
"@pushrocks/smartlog": "^2.0.19",
"@pushrocks/smartlog-destination-local": "^8.0.2",
"@pushrocks/smartparam": "^1.0.4",
"@pushrocks/smartpromise": "^3.0.2",
"@pushrocks/smartrequest": "^1.1.23",
"@pushrocks/smartshell": "^2.0.25",
"@pushrocks/smartsocket": "^1.1.45",
"@pushrocks/smartssh": "^1.2.3",
"@pushrocks/smartstring": "^3.0.10",
"@servezone/servezone-interfaces": "^2.0.29",
"@types/shelljs": "^0.8.5",
"@types/through2": "^2.0.34",
"through2": "^3.0.1"
"scripts": {
"postinstall": "node scripts/install-binary.js",
"prepublishOnly": "echo 'Publishing SZCI binaries to npm...'",
"test": "echo 'Tests are run with Deno: deno task test'",
"build": "echo 'no build needed'"
},
"files": [
"ts/*",
"ts_web/*",
"dist/*",
"dist_web/*",
"dist_ts_web/*",
"assets/*",
"cli.js",
"npmextra.json",
"readme.md"
]
"bin/",
"scripts/install-binary.js",
"readme.md",
"license",
"changelog.md"
],
"engines": {
"node": ">=14.0.0"
},
"os": [
"darwin",
"linux",
"win32"
],
"cpu": [
"x64",
"arm64"
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34"
}

9740
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

4
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,4 @@
onlyBuiltDependencies:
- esbuild
- mongodb-memory-server
- puppeteer

2
readme.hints.md Normal file
View File

@@ -0,0 +1,2 @@
- focus on cli usage in CI environments.
- show Gitlab CI, GitHub CI and Gitea CI examples.

360
readme.md
View File

@@ -1,101 +1,307 @@
# @shipzone/npmci
node and docker in gitlab ci on steroids
# @ship.zone/szci
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@shipzone/npmci)
* [gitlab.com (source)](https://gitlab.com/shipzone/npmci)
* [github.com (source mirror)](https://github.com/shipzone/npmci)
* [docs (typedoc)](https://shipzone.gitlab.io/npmci/)
**Serve Zone CI** - A powerful CI/CD tool for streamlining Node.js and Docker workflows within CI environments (GitLab CI, GitHub CI, Gitea CI). Now powered by Deno with standalone executables.
## Status for master
[![build status](https://gitlab.com/shipzone/npmci/badges/master/build.svg)](https://gitlab.com/shipzone/npmci/commits/master)
[![coverage report](https://gitlab.com/shipzone/npmci/badges/master/coverage.svg)](https://gitlab.com/shipzone/npmci/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/@shipzone/npmci.svg)](https://www.npmjs.com/package/@shipzone/npmci)
[![Known Vulnerabilities](https://snyk.io/test/npm/@shipzone/npmci/badge.svg)](https://snyk.io/test/npm/@shipzone/npmci)
[![TypeScript](https://img.shields.io/badge/TypeScript->=%203.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-prettier-ff69b4.svg)](https://prettier.io/)
## ✨ Features
## Usage
- 🚀 **Standalone Executables** - No Node.js installation required
- 🐳 **Docker Integration** - Build, tag, and push Docker images
- 📦 **NPM Management** - Install, test, and publish npm packages
- 🔧 **Node.js Version Management** - Install and switch between Node versions
- 🔐 **SSH Key Management** - Deploy SSH keys from environment variables
- ☁️ **Multi-Registry Support** - Push to multiple Docker registries
- 🎯 **Cross-Platform** - Binaries for Linux, macOS, and Windows
Use TypeScript for best in class instellisense.
## 📥 Installation
npmci is designed to work in docker CI environments. The following docker images come with npmci presinstalled:
### NPM (Recommended)
Docker Hub:
- [hosttoday/ht-docker-node:npmci](https://hub.docker.com/r/hosttoday/ht-docker-node/)
has LTS node version and npmci preinstalled.
- [hosttoday/ht-docker-dbase](https://hub.docker.com/r/hosttoday/ht-docker-dbase/)
based on docker:git, can be used to build docker images in conjunction with docker:dind
npmci can be called from commandline and handle a lot of tasks durug ci:
```shell
# Handle node versions
npmci node install stable # will install latest stable node version and update PATH for node and npm
npmci node install lts # will install latest LTS node version and update PATH for node and npm versions
npmci node install legacy # will install latest legacy node version and update PATH for node and npm
npmci node install x.x.x # will install any specific node version.
# Handle npm and yarn tasks
npmcu npm login # logs in npm using the auth key provided at env var "NPMCI_TOKEN_NPM"
npmci npm install # installs dependencies using npm or yarn dependending on availablity
npmci npm test # tests the package
npmci npm publish # builds a package and publishes it
# handle docker tasks
npmci docker prepare
## npmci test docker will look at all Dockerfiles and look for according tags on GitLab container registry
# prepare tools
npmci prepare npm # will look for $NPMCI_TOKEN_NPM env var and create .npmrc, so npm is authenticated
npmci prepare docker # will look for $NPMCI_LOGIN_DOCKER in form username|password and authenticate docker
npmci prepare docker-gitlab # will authenticate docker for gitlab container registry
# build containers
npmci docker build # will build containers
## all Dockerfiles named Dockerfile* are picked up.
## specify tags like this Dockerfile_[tag]
## uploads all built images as [username]/[reponame]:[tag]_test to GitLab
## then test in next step with "npmci test docker"
# publish npm module
npmci publish npm # will look vor $NPMCI_TOKEN_NPM env var and push any module in cwd to npm
npmci publish docker
# trigger webhooks
npmci trigger # will look for NPMCI_TRIGGER_1 to NPMCI_TRIGGER_100 in form domain|id|token|ref|name
```sh
npm install -g @ship.zone/szci
```
## Configuration
The package will automatically download the appropriate pre-compiled binary for your platform.
npmci supports the use of npmextra.
### From Source (Deno Required)
To configure npmci create a `npmextra.json` file at the root of your project
```sh
git clone https://code.foss.global/ship.zone/szci.git
cd szci
deno task compile
```
## 🚀 Quick Start
```sh
# Install Node.js
szci node install stable
# Install dependencies
szci npm install
# Build Docker images
szci docker build
# Run tests
szci npm test
# Push Docker images
szci docker push registry.example.com
```
## 📖 Usage
### Node.js Management
```sh
# Install specific Node.js version
szci node install lts
szci node install stable
szci node install 18
# Install from .nvmrc
szci node install legacy
```
### NPM Commands
```sh
# Install dependencies
szci npm install
# Run tests
szci npm test
# Publish package
szci npm publish
```
### Docker Workflows
```sh
# Prepare Docker environment
szci docker prepare
# Build all Dockerfiles
szci docker build
# Push to registry
szci docker push registry.example.com
# Pull from registry
szci docker pull registry.example.com
# Test Dockerfiles
szci docker test
```
### SSH Key Management
```sh
# Deploy SSH keys from environment
szci ssh prepare
```
Set environment variables like `NPMCI_SSHKEY_1`, `NPMCI_SSHKEY_2`, etc.
## 🔧 CI/CD Integration
### GitLab CI
```yaml
image: denoland/deno:alpine
stages:
- prepare
- build
- test
- deploy
variables:
SZCI_VERSION: "latest"
before_script:
- deno install --allow-all --global --name szci https://code.foss.global/ship.zone/szci/raw/branch/master/mod.ts
# OR use the npm package:
# - npm install -g @ship.zone/szci
prepare:
stage: prepare
script:
- szci node install stable
- szci npm install
build:
stage: build
script:
- szci docker build
test:
stage: test
script:
- szci npm test
deploy:
stage: deploy
script:
- szci docker push $CI_REGISTRY
only:
- master
```
### GitHub Actions
```yaml
name: CI Pipeline
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install SZCI
run: npm install -g @ship.zone/szci
- name: Setup Node.js
run: szci node install stable
- name: Install Dependencies
run: szci npm install
- name: Build Docker Images
run: szci docker build
- name: Run Tests
run: szci npm test
- name: Push Images
if: github.ref == 'refs/heads/main'
run: szci docker push ghcr.io
```
## ⚙️ Configuration
Create an `npmextra.json` file in your project root:
```json
{
"npmci": {
"globalNpmTools": ["npm-check-updates", "protractor", "npmts", "gitzone"]
"npmGlobalTools": [],
"npmAccessLevel": "public",
"npmRegistryUrl": "registry.npmjs.org",
"dockerRegistries": [
"registry.gitlab.com"
],
"dockerRegistryRepoMap": {
"registry.gitlab.com": "mygroup/myrepo"
},
"dockerBuildargEnvMap": {
"ARG_NAME": "ENV_VAR_NAME"
}
}
}
```
**Available options**
## 🐳 Docker Registry Authentication
| setting | example | description |
| -------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- |
| globalNpmTools | "globalNpmTools": ["gitbook"] | Will look for the specified package names locally and (if not yet present) install them from npm. |
SZCI supports automatic authentication with:
For further information read the linked docs at the top of this README.
- GitLab CI Registry (via `CI_JOB_TOKEN`)
- Custom registries via environment variables
Use TypeScript for best in class instellisense.
Set `NPMCI_LOGIN_DOCKER*` environment variables:
For further information read the linked docs at the top of this readme.
```sh
NPMCI_LOGIN_DOCKER_1="registry.example.com|username|password"
NPMCI_LOGIN_DOCKER_2="another-registry.com|user2|pass2"
```
> MIT licensed | **&copy;** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
## 🏗️ Development
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)
### Prerequisites
- Deno 1.40+ installed
### Building from Source
```sh
# Clone the repository
git clone https://code.foss.global/ship.zone/szci.git
cd szci
# Compile for all platforms
deno task compile
# Compile for current platform only
deno compile --allow-all --output szci mod.ts
# Run tests
deno task test
# Development mode
deno task dev --help
```
### Testing
SZCI uses Deno's native test framework:
```sh
# Run all tests
deno task test
# Run tests in watch mode
deno task test:watch
# Run specific test file
deno test --allow-all test/test.cloudly.ts
```
## 📦 Binary Sizes
The standalone executables are approximately:
- Linux x64: ~800MB
- Linux ARM64: ~800MB
- macOS x64: ~796MB
- macOS ARM64: ~796MB
- Windows x64: ~804MB
Sizes include all dependencies and the Deno runtime.
## 🔄 Migration from npmci
If you're upgrading from the old `@ship.zone/npmci` package:
1. Update package references:
```sh
npm uninstall -g @ship.zone/npmci
npm install -g @ship.zone/szci
```
2. Update CI configuration files - replace `npmci` with `szci`
3. The command interface remains the same, only the binary name changed
## 📝 License
MIT © Lossless GmbH
## 🔗 Links
- [Repository](https://code.foss.global/ship.zone/szci)
- [Issues](https://code.foss.global/ship.zone/szci/issues)
- [Changelog](./changelog.md)
## 🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
---
**Built with Deno 🦕**

66
scripts/compile-all.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
set -e
# Get version from deno.json
VERSION=$(cat deno.json | grep -o '"version": *"[^"]*"' | cut -d'"' -f4)
BINARY_DIR="dist/binaries"
echo "================================================"
echo " SZCI Compilation Script"
echo " Version: ${VERSION}"
echo "================================================"
echo ""
echo "Compiling for all supported platforms..."
echo ""
# Clean up old binaries and create fresh directory
rm -rf "$BINARY_DIR"
mkdir -p "$BINARY_DIR"
echo "→ Cleaned old binaries from $BINARY_DIR"
echo ""
# Linux x86_64
echo "→ Compiling for Linux x86_64..."
deno compile --allow-all --no-check --output "$BINARY_DIR/szci-linux-x64" \
--target x86_64-unknown-linux-gnu mod.ts
echo " ✓ Linux x86_64 complete"
echo ""
# Linux ARM64
echo "→ Compiling for Linux ARM64..."
deno compile --allow-all --no-check --output "$BINARY_DIR/szci-linux-arm64" \
--target aarch64-unknown-linux-gnu mod.ts
echo " ✓ Linux ARM64 complete"
echo ""
# macOS x86_64
echo "→ Compiling for macOS x86_64..."
deno compile --allow-all --no-check --output "$BINARY_DIR/szci-macos-x64" \
--target x86_64-apple-darwin mod.ts
echo " ✓ macOS x86_64 complete"
echo ""
# macOS ARM64
echo "→ Compiling for macOS ARM64..."
deno compile --allow-all --no-check --output "$BINARY_DIR/szci-macos-arm64" \
--target aarch64-apple-darwin mod.ts
echo " ✓ macOS ARM64 complete"
echo ""
# Windows x86_64
echo "→ Compiling for Windows x86_64..."
deno compile --allow-all --no-check --output "$BINARY_DIR/szci-windows-x64.exe" \
--target x86_64-pc-windows-msvc mod.ts
echo " ✓ Windows x86_64 complete"
echo ""
echo "================================================"
echo " Compilation Summary"
echo "================================================"
echo ""
ls -lh "$BINARY_DIR/" | tail -n +2
echo ""
echo "✓ All binaries compiled successfully!"
echo ""
echo "Binary location: $BINARY_DIR/"
echo ""

228
scripts/install-binary.js Normal file
View File

@@ -0,0 +1,228 @@
#!/usr/bin/env node
/**
* SZCI npm postinstall script
* Downloads the appropriate binary for the current platform from repository releases
*/
import { platform, arch } from 'os';
import { existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import https from 'https';
import { pipeline } from 'stream';
import { promisify } from 'util';
import { createWriteStream } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const streamPipeline = promisify(pipeline);
// Configuration
const REPO_BASE = 'https://code.foss.global/ship.zone/szci';
const VERSION = process.env.npm_package_version || '4.1.37';
function getBinaryInfo() {
const plat = platform();
const architecture = arch();
const platformMap = {
'darwin': 'macos',
'linux': 'linux',
'win32': 'windows'
};
const archMap = {
'x64': 'x64',
'arm64': 'arm64'
};
const mappedPlatform = platformMap[plat];
const mappedArch = archMap[architecture];
if (!mappedPlatform || !mappedArch) {
return { supported: false, platform: plat, arch: architecture };
}
let binaryName = `szci-${mappedPlatform}-${mappedArch}`;
if (plat === 'win32') {
binaryName += '.exe';
}
return {
supported: true,
platform: mappedPlatform,
arch: mappedArch,
binaryName,
originalPlatform: plat
};
}
function downloadFile(url, destination) {
return new Promise((resolve, reject) => {
console.log(`Downloading from: ${url}`);
// Follow redirects
const download = (url, redirectCount = 0) => {
if (redirectCount > 5) {
reject(new Error('Too many redirects'));
return;
}
https.get(url, (response) => {
if (response.statusCode === 301 || response.statusCode === 302) {
console.log(`Following redirect to: ${response.headers.location}`);
download(response.headers.location, redirectCount + 1);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`Failed to download: ${response.statusCode} ${response.statusMessage}`));
return;
}
const totalSize = parseInt(response.headers['content-length'], 10);
let downloadedSize = 0;
let lastProgress = 0;
response.on('data', (chunk) => {
downloadedSize += chunk.length;
const progress = Math.round((downloadedSize / totalSize) * 100);
// Only log every 10% to reduce noise
if (progress >= lastProgress + 10) {
console.log(`Download progress: ${progress}%`);
lastProgress = progress;
}
});
const file = createWriteStream(destination);
pipeline(response, file, (err) => {
if (err) {
reject(err);
} else {
console.log('Download complete!');
resolve();
}
});
}).on('error', reject);
};
download(url);
});
}
async function main() {
console.log('===========================================');
console.log(' SZCI - Binary Installation');
console.log('===========================================');
console.log('');
const binaryInfo = getBinaryInfo();
if (!binaryInfo.supported) {
console.error(`❌ Error: Unsupported platform/architecture: ${binaryInfo.platform}/${binaryInfo.arch}`);
console.error('');
console.error('Supported platforms:');
console.error(' • Linux (x64, arm64)');
console.error(' • macOS (x64, arm64)');
console.error(' • Windows (x64)');
console.error('');
console.error('If you believe your platform should be supported, please file an issue:');
console.error(' https://code.foss.global/ship.zone/szci/issues');
process.exit(1);
}
console.log(`Platform: ${binaryInfo.platform} (${binaryInfo.originalPlatform})`);
console.log(`Architecture: ${binaryInfo.arch}`);
console.log(`Binary: ${binaryInfo.binaryName}`);
console.log(`Version: ${VERSION}`);
console.log('');
// Create dist/binaries directory if it doesn't exist
const binariesDir = join(__dirname, '..', 'dist', 'binaries');
if (!existsSync(binariesDir)) {
console.log('Creating binaries directory...');
mkdirSync(binariesDir, { recursive: true });
}
const binaryPath = join(binariesDir, binaryInfo.binaryName);
// Check if binary already exists and skip download
if (existsSync(binaryPath)) {
console.log('✓ Binary already exists, skipping download');
} else {
// Construct download URL
// Try release URL first, fall back to raw branch if needed
const releaseUrl = `${REPO_BASE}/releases/download/v${VERSION}/${binaryInfo.binaryName}`;
const fallbackUrl = `${REPO_BASE}/raw/branch/master/dist/binaries/${binaryInfo.binaryName}`;
console.log('Downloading platform-specific binary...');
console.log('This may take a moment depending on your connection speed.');
console.log('');
try {
// Try downloading from release
await downloadFile(releaseUrl, binaryPath);
} catch (err) {
console.log(`Release download failed: ${err.message}`);
console.log('Trying fallback URL...');
try {
// Try fallback URL
await downloadFile(fallbackUrl, binaryPath);
} catch (fallbackErr) {
console.error(`❌ Error: Failed to download binary`);
console.error(` Primary URL: ${releaseUrl}`);
console.error(` Fallback URL: ${fallbackUrl}`);
console.error('');
console.error('This might be because:');
console.error('1. The release has not been created yet');
console.error('2. Network connectivity issues');
console.error('3. The version specified does not exist');
console.error('');
console.error('You can try:');
console.error('1. Installing from source: https://code.foss.global/ship.zone/szci');
console.error('2. Downloading the binary manually from the releases page');
console.error('3. Building from source with: deno task compile');
// Clean up partial download
if (existsSync(binaryPath)) {
unlinkSync(binaryPath);
}
process.exit(1);
}
}
console.log(`✓ Binary downloaded successfully`);
}
// On Unix-like systems, ensure the binary is executable
if (binaryInfo.originalPlatform !== 'win32') {
try {
console.log('Setting executable permissions...');
chmodSync(binaryPath, 0o755);
console.log('✓ Binary permissions updated');
} catch (err) {
console.error(`⚠️ Warning: Could not set executable permissions: ${err.message}`);
console.error(' You may need to manually run:');
console.error(` chmod +x ${binaryPath}`);
}
}
console.log('');
console.log('✅ SZCI installation completed successfully!');
console.log('');
console.log('You can now use SZCI by running:');
console.log(' szci --help');
console.log('');
console.log('===========================================');
}
// Run the installation
main().catch(err => {
console.error(`❌ Installation failed: ${err.message}`);
process.exit(1);
});

View File

@@ -0,0 +1,2 @@
FROM mygroup/myrepo:sometag2
RUN apt-get update

View File

@@ -1 +1,9 @@
{}
{
"gitzone": {
"module": {
"githost": "code.foss.global",
"gitscope": "mygroup",
"gitrepo": "myrepo"
}
}
}

17
test/test.cloudly.ts Normal file
View File

@@ -0,0 +1,17 @@
// Disable TLS certificate validation for testing
Deno.env.set('NODE_TLS_REJECT_UNAUTHORIZED', '0');
import { CloudlyConnector } from '../ts/connector.cloudly/cloudlyconnector.ts';
Deno.test('should be able to announce a container to cloudly', async () => {
const cloudlyConnector = new CloudlyConnector(null);
await cloudlyConnector.announceDockerContainer(
{
registryUrl: 'registry.losssless.com',
tag: 'testcontainer',
version: 'x.x.x',
labels: [],
},
'cloudly.lossless.one'
);
});

View File

@@ -1,123 +1,116 @@
import { tap, expect } from '@pushrocks/tapbundle';
import * as path from 'path';
import { assertEquals } from '@std/assert';
import * as path from '@std/path';
import * as smartpath from '@push.rocks/smartpath';
process.env.NPMTS_TEST = 'true';
// Set up test environment
Deno.env.set('NPMTS_TEST', 'true');
Deno.env.set('NPMCI_URL_CLOUDLY', 'localhost');
Deno.env.set('CI_REPOSITORY_URL', 'https://yyyyyy:xxxxxxxx@gitlab.com/mygroup/myrepo.git');
Deno.env.set('CI_BUILD_TOKEN', 'kjlkjfiudofiufs');
Deno.env.set('NPMCI_LOGIN_DOCKER', 'docker.io|someuser|somepass');
Deno.env.set('NPMCI_SSHKEY_1', 'hostString|somePrivKey|##');
// set up environment
process.env.CI_REPOSITORY_URL = 'https://yyyyyy:xxxxxxxx@gitlab.com/mygroup/myrepo.git';
process.env.CI_BUILD_TOKEN = 'kjlkjfiudofiufs';
// Get the test assets directory
const testAssetsDir = path.join(smartpath.get.dirnameFromImportMetaUrl(import.meta.url), 'assets/');
// Docker
process.env.NPMCI_LOGIN_DOCKER = 'docker.io|someuser|somepass';
// Save original cwd and change to test assets
const originalCwd = Deno.cwd();
Deno.chdir(testAssetsDir);
// SSH env
process.env.NPMCI_SSHKEY_1 = 'hostString|somePrivKey|##';
process.cwd = () => {
return path.join(__dirname, 'assets/');
};
import * as npmci from '../ts';
import type { Dockerfile } from '../ts/manager.docker/mod.classes.dockerfile.ts';
import { Szci } from '../ts/szci.classes.szci.ts';
import * as DockerfileModule from '../ts/manager.docker/mod.classes.dockerfile.ts';
// ======
// Docker
// ======
let dockerfile1: npmci.Dockerfile;
let dockerfile2: npmci.Dockerfile;
let sortableArray: npmci.Dockerfile[];
let dockerfile1: Dockerfile;
let dockerfile2: Dockerfile;
let sortableArray: Dockerfile[];
tap.test('should return valid Dockerfiles', async () => {
const npmciInstance = new npmci.Npmci();
dockerfile1 = new npmci.Dockerfile(npmciInstance.dockerManager, {
Deno.test('should return valid Dockerfiles', async () => {
const szciInstance = new Szci();
await szciInstance.start();
dockerfile1 = new DockerfileModule.Dockerfile(szciInstance.dockerManager, {
filePath: './Dockerfile',
read: true
read: true,
});
dockerfile2 = new npmci.Dockerfile(npmciInstance.dockerManager, {
dockerfile2 = new DockerfileModule.Dockerfile(szciInstance.dockerManager, {
filePath: './Dockerfile_sometag1',
read: true
read: true,
});
expect(dockerfile1.version).to.equal('latest');
return expect(dockerfile2.version).to.equal('sometag1');
assertEquals(dockerfile1.version, 'latest');
assertEquals(dockerfile2.version, 'sometag1');
});
tap.test('should read a directory of Dockerfiles', async () => {
const npmciInstance = new npmci.Npmci();
return npmci.Dockerfile.readDockerfiles(npmciInstance.dockerManager).then(
async (readDockerfilesArrayArg: npmci.Dockerfile[]) => {
sortableArray = readDockerfilesArrayArg;
return expect(readDockerfilesArrayArg[1].version).to.equal('sometag1');
}
Deno.test('should read a directory of Dockerfiles', async () => {
const szciInstance = new Szci();
await szciInstance.start();
const readDockerfilesArray = await DockerfileModule.Dockerfile.readDockerfiles(
szciInstance.dockerManager
);
sortableArray = readDockerfilesArray;
assertEquals(readDockerfilesArray[1].version, 'sometag1');
});
tap.test('should sort an array of Dockerfiles', async () => {
return npmci.Dockerfile.sortDockerfiles(sortableArray).then(
async (sortedArrayArg: npmci.Dockerfile[]) => {
console.log(sortedArrayArg);
}
);
Deno.test('should sort an array of Dockerfiles', async () => {
const sortedArray = await DockerfileModule.Dockerfile.sortDockerfiles(sortableArray);
console.log(sortedArray);
});
tap.test('should build all Dockerfiles', async () => {
const npmciInstance = new npmci.Npmci();
return npmciInstance.dockerManager.handleCli({
_: ['docker', 'build']
Deno.test('should build all Dockerfiles', async () => {
const szciInstance = new Szci();
await szciInstance.start();
await szciInstance.dockerManager.handleCli({
_: ['docker', 'build'],
});
});
tap.test('should test all Dockerfiles', async () => {
const npmciInstance = new npmci.Npmci();
return npmciInstance.dockerManager.handleCli({
_: ['docker', 'test']
Deno.test('should test all Dockerfiles', async () => {
const szciInstance = new Szci();
await szciInstance.start();
await szciInstance.dockerManager.handleCli({
_: ['docker', 'test'],
});
});
tap.test('should test dockerfiles', async () => {
const npmciInstance = new npmci.Npmci();
return npmciInstance.dockerManager.handleCli({
_: ['docker', 'test']
});
});
tap.test('should login docker daemon', async () => {
const npmciInstance = new npmci.Npmci();
return npmciInstance.dockerManager.handleCli({
_: ['docker', 'login']
Deno.test('should login docker daemon', async () => {
const szciInstance = new Szci();
await szciInstance.start();
await szciInstance.dockerManager.handleCli({
_: ['docker', 'login'],
});
});
// ===
// SSH
// ===
tap.test('should prepare SSH keys', async () => {
const npmciModSsh = await import('../ts/mod_ssh');
return await npmciModSsh.handleCli({
_: ['ssh', 'prepare']
Deno.test('should prepare SSH keys', async () => {
const npmciModSsh = await import('../ts/mod_ssh/index.ts');
await npmciModSsh.handleCli({
_: ['ssh', 'prepare'],
});
});
// ====
// node
// ====
tap.test('should install a certain version of node', async () => {
const npmciInstance = new npmci.Npmci();
await npmciInstance.nodejsManager.handleCli({
_: ['node', 'install', 'stable']
Deno.test('should install a certain version of node', async () => {
const szciInstance = new Szci();
await szciInstance.start();
await szciInstance.nodejsManager.handleCli({
_: ['node', 'install', 'stable'],
});
await npmciInstance.nodejsManager.handleCli({
_: ['node', 'install', 'lts']
await szciInstance.nodejsManager.handleCli({
_: ['node', 'install', 'lts'],
});
await npmciInstance.nodejsManager.handleCli({
_: ['node', 'install', 'legacy']
await szciInstance.nodejsManager.handleCli({
_: ['node', 'install', 'legacy'],
});
});
// make sure test ends all right
tap.test('reset paths', async () => {
process.cwd = () => {
return path.join(__dirname, '../');
};
// Restore original working directory after all tests
Deno.test('reset paths', () => {
Deno.chdir(originalCwd);
});
tap.start();

8
ts/00_commitinfo_data.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@ship.zone/szci',
version: '6.0.1',
description: 'Serve Zone CI - A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities. Powered by Deno with standalone executables.'
}

View File

@@ -1,30 +1,31 @@
import * as plugins from '../npmci.plugins';
import * as plugins from '../szci.plugins.ts';
import {Npmci} from '../npmci.classes.npmci';
import { logger } from '../npmci.logging';
import { Szci } from '../szci.classes.szci.ts';
import { logger } from '../szci.logging.ts';
/**
* connects to cloudly
*/
export class CloudlyConnector {
public npmciRef: Npmci;
public szciRef: Szci;
constructor(npmciRefArg: Npmci) {
this.npmciRef = npmciRefArg;
constructor(szciRefArg: Szci) {
this.szciRef = szciRefArg;
}
public async announceDockerContainer(optionsArg: plugins.servezoneInterfaces.IVersionData) {
const cloudlyUrl = this.npmciRef.npmciConfig.getConfig().urlCloudly;
public async announceDockerContainer(
optionsArg: plugins.tsclass.container.IContainer,
testCloudlyUrlArg?: string
) {
const cloudlyUrl = testCloudlyUrlArg || this.szciRef.npmciConfig.getConfig().urlCloudly;
if (!cloudlyUrl) {
logger.log('warn', 'no cloudly url provided. Thus we cannot announce the newly built Dockerimage!');
logger.log(
'warn',
'no cloudly url provided. Thus we cannot announce the newly built Dockerimage!'
);
return;
}
const typedrequest = new plugins.typedrequest.TypedRequest<
plugins.servezoneInterfaces.IRequest_Any_Cloudly_VersionManager_Update
>(`https://${cloudlyUrl}/versionmanager`, 'update');
const response = (await typedrequest.fire(optionsArg));
// lets push to cloudly here
}
}

View File

@@ -1,10 +1,10 @@
import { Npmci } from './npmci.classes.npmci';
import { Dockerfile } from './manager.docker/mod.classes.dockerfile';
import { Szci } from './szci.classes.szci.ts';
import { Dockerfile } from './manager.docker/mod.classes.dockerfile.ts';
export const npmciInstance = new Npmci();
export const szciInstance = new Npmci();
export { Dockerfile, Npmci };
if (process.env.CLI_CALL) {
npmciInstance.start();
}
export const runCli = async () => {
await szciInstance.start();
};

View File

@@ -0,0 +1,67 @@
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
import * as paths from '../szci.paths.ts';
import { bash } from '../szci.bash.ts';
import { Szci } from '../szci.classes.szci.ts';
export class SzciCloudronManager {
public szciRef: Szci;
constructor(szciArg: Szci) {
this.szciRef = szciArg;
}
/**
* handle cli input
* @param argvArg
*/
public handleCli = async (argvArg: any) => {
if (argvArg._.length >= 2) {
const action: string = argvArg._[1];
switch (action) {
case 'deploy':
await this.deploy();
break;
default:
logger.log('error', `>>npmci cloudron ...<< action >>${action}<< not supported`);
}
} else {
logger.log(
'info',
`>>npmci cloudron ...<< cli arguments invalid... Please read the documentation.`
);
}
};
/**
* Replaces the version string in CloudronManifest file
* @param versionArg
*/
public deploy = async () => {
logger.log('info', 'now deploying to cloudron...');
logger.log('info', 'installing cloudron cli...');
await bash(`pnpm install -g cloudron`);
logger.log('ok', 'cloudron cli installed');
// lets set the version in the CloudronManifest file
await this.prepareCloudronManifest(this.szciRef.npmciConfig.getConfig().projectInfo.npm.version);
logger.log('ok', 'CloudronManifest prepared');
// lets figure out the docker image tag
const dockerImageTag = await this.szciRef.npmciConfig.kvStorage.readKey('latestPushedDockerTag');
const appName = this.szciRef.npmciConfig.getConfig().cloudronAppName;
const cloudronEnvVar = Deno.env.get("NPMCI_LOGIN_CLOUDRON");
const cloudronServer = cloudronEnvVar.split('|')[0];
const cloudronToken = cloudronEnvVar.split('|')[1];
await bash(`cloudron update --server ${cloudronServer} --token ${cloudronToken} --image ${dockerImageTag} --app ${appName}`);
};
private prepareCloudronManifest = async (versionArg: string) => {
const manifestPath = plugins.path.join(paths.cwd, 'CloudronManifest.json');
let manifestFile = plugins.smartfile.fs.toStringSync(manifestPath);
manifestFile = manifestFile.replace(/##version##/g, versionArg);
plugins.smartfile.memory.toFsSync(manifestFile, manifestPath);
logger.log('info', 'Version replaced in CloudronManifest file');
}
}

View File

@@ -0,0 +1 @@
export * from '../szci.plugins.ts';

View File

@@ -1,27 +1,27 @@
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins';
import * as paths from '../npmci.paths';
import { bash } from '../npmci.bash';
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
import * as paths from '../szci.paths.ts';
import { bash } from '../szci.bash.ts';
// classes
import { Npmci } from '../npmci.classes.npmci';
import { Dockerfile } from './mod.classes.dockerfile';
import { DockerRegistry } from './mod.classes.dockerregistry';
import { RegistryStorage } from './mod.classes.registrystorage';
import { Szci } from '../szci.classes.szci.ts';
import { Dockerfile } from './mod.classes.dockerfile.ts';
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
import { RegistryStorage } from './mod.classes.registrystorage.ts';
export class NpmciDockerManager {
public npmciRef: Npmci;
export class SzciDockerManager {
public szciRef: Szci;
public npmciRegistryStorage = new RegistryStorage();
constructor(npmciArg: Npmci) {
this.npmciRef = npmciArg;
constructor(szciArg: Szci) {
this.szciRef = szciArg;
}
/**
* handle cli input
* @param argvArg
*/
public handleCli = async argvArg => {
public handleCli = async (argvArg: any) => {
if (argvArg._.length >= 2) {
const action: string = argvArg._[1];
switch (action) {
@@ -50,7 +50,7 @@ export class NpmciDockerManager {
`>>npmci docker ...<< cli arguments invalid... Please read the documentation.`
);
}
}
};
/**
* builds a cwd of Dockerfiles by triggering a promisechain
@@ -62,7 +62,7 @@ export class NpmciDockerManager {
.then(Dockerfile.sortDockerfiles)
.then(Dockerfile.mapDockerfiles)
.then(Dockerfile.buildDockerfiles);
}
};
/**
* login to the DockerRegistries
@@ -70,41 +70,44 @@ export class NpmciDockerManager {
public login = async () => {
await this.prepare();
await this.npmciRegistryStorage.loginAll();
}
};
/**
* logs in docker
*/
public prepare = async () => {
// Always login to GitLab Registry
if (!process.env.CI_BUILD_TOKEN || process.env.CI_BUILD_TOKEN === '') {
logger.log('error', 'No registry token specified by gitlab!');
process.exit(1);
if (Deno.env.get("GITLAB_CI")) {
console.log('gitlab ci detected');
if (!Deno.env.get("CI_JOB_TOKEN") || Deno.env.get("CI_JOB_TOKEN") === '') {
logger.log('error', 'Running in Gitlab CI, but no registry token specified by gitlab!');
Deno.exit(1);
}
this.npmciRegistryStorage.addRegistry(
new DockerRegistry({
registryUrl: 'registry.gitlab.com',
username: 'gitlab-ci-token',
password: Deno.env.get("CI_JOB_TOKEN"),
})
);
}
this.npmciRegistryStorage.addRegistry(
new DockerRegistry({
registryUrl: 'registry.gitlab.com',
username: 'gitlab-ci-token',
password: process.env.CI_BUILD_TOKEN
})
);
// handle registries
await plugins.smartparam.forEachMinimatch(
process.env,
await plugins.smartobject.forEachMinimatch(
Deno.env.toObject(),
'NPMCI_LOGIN_DOCKER*',
async envString => {
async (envString: string) => {
this.npmciRegistryStorage.addRegistry(DockerRegistry.fromEnvString(envString));
}
);
return;
}
};
/**
* pushes an image towards a registry
* @param argvArg
*/
public push = async argvArg => {
public push = async (argvArg: any) => {
await this.prepare();
let dockerRegistryUrls: string[] = [];
@@ -112,14 +115,14 @@ export class NpmciDockerManager {
if (argvArg._.length >= 3 && argvArg._[2] !== 'npmextra') {
dockerRegistryUrls.push(argvArg._[2]);
} else {
if (this.npmciRef.npmciConfig.getConfig().dockerRegistries.length === 0) {
if (this.szciRef.npmciConfig.getConfig().dockerRegistries.length === 0) {
logger.log(
'warn',
`There are no docker registries listed in npmextra.json! This is strange!`
);
}
dockerRegistryUrls = dockerRegistryUrls.concat(
this.npmciRef.npmciConfig.getConfig().dockerRegistries
this.szciRef.npmciConfig.getConfig().dockerRegistries
);
}
@@ -134,38 +137,40 @@ export class NpmciDockerManager {
const dockerfileArray = await Dockerfile.readDockerfiles(this)
.then(Dockerfile.sortDockerfiles)
.then(Dockerfile.mapDockerfiles);
const dockerRegistryToPushTo = this.npmciRegistryStorage.getRegistryByUrl(dockerRegistryUrl);
const dockerRegistryToPushTo = await this.npmciRegistryStorage.getRegistryByUrl(
dockerRegistryUrl
);
if (!dockerRegistryToPushTo) {
logger.log(
'error',
`Cannot push to registry ${dockerRegistryUrl}, because it was not found in the authenticated registry list.`
);
process.exit(1);
Deno.exit(1);
}
for (const dockerfile of dockerfileArray) {
await dockerfile.push(dockerRegistryToPushTo, suffix);
}
}
}
};
/**
* pulls an image
*/
public pull = async argvArg => {
public pull = async (argvArg: any) => {
await this.prepare();
const registryUrlArg = argvArg._[2];
let suffix = null;
if (argvArg._.length >= 4) {
suffix = argvArg._[3];
}
const localDockerRegistry = this.npmciRegistryStorage.getRegistryByUrl(registryUrlArg);
const localDockerRegistry = await this.npmciRegistryStorage.getRegistryByUrl(registryUrlArg);
const dockerfileArray = await Dockerfile.readDockerfiles(this)
.then(Dockerfile.sortDockerfiles)
.then(Dockerfile.mapDockerfiles);
for (const dockerfile of dockerfileArray) {
await dockerfile.pull(localDockerRegistry, suffix);
}
}
};
/**
* tests docker files
@@ -173,5 +178,13 @@ export class NpmciDockerManager {
public test = async () => {
await this.prepare();
return await Dockerfile.readDockerfiles(this).then(Dockerfile.testDockerfiles);
};
/**
* can be used to get the Dockerfiles in the directory
*/
getDockerfiles = async () => {
const dockerfiles = await Dockerfile.readDockerfiles(this);
return dockerfiles;
}
}

View File

@@ -1,13 +1,13 @@
import * as plugins from './mod.plugins';
import * as paths from '../npmci.paths';
import * as plugins from './mod.plugins.ts';
import * as paths from '../szci.paths.ts';
import { logger } from '../npmci.logging';
import { bash } from '../npmci.bash';
import { logger } from '../szci.logging.ts';
import { bash } from '../szci.bash.ts';
import { DockerRegistry } from './mod.classes.dockerregistry';
import * as helpers from './mod.helpers';
import { NpmciDockerManager } from '.';
import { Npmci } from '../npmci.classes.npmci';
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
import * as helpers from './mod.helpers.ts';
import { SzciDockerManager } from './index.ts';
import { Szci } from '../szci.classes.szci.ts';
/**
* class Dockerfile represents a Dockerfile on disk in npmci
@@ -20,7 +20,7 @@ export class Dockerfile {
* @returns Promise<Dockerfile[]>
*/
public static async readDockerfiles(
npmciDockerManagerRefArg: NpmciDockerManager
npmciDockerManagerRefArg: SzciDockerManager
): Promise<Dockerfile[]> {
const fileTree = await plugins.smartfile.fs.listFileTree(paths.cwd, 'Dockerfile*');
@@ -31,7 +31,7 @@ export class Dockerfile {
for (const dockerfilePath of fileTree) {
const myDockerfile = new Dockerfile(npmciDockerManagerRefArg, {
filePath: dockerfilePath,
read: true
read: true,
});
readDockerfilesArray.push(myDockerfile);
}
@@ -40,50 +40,83 @@ export class Dockerfile {
}
/**
* sorts Dockerfiles into a dependency chain
* @param sortableArrayArg an array of instances of class Dockerfile
* @returns Promise<Dockerfile[]>
* Sorts Dockerfiles into a build order based on dependencies.
* @param dockerfiles An array of Dockerfile instances.
* @returns A Promise that resolves to a sorted array of Dockerfiles.
*/
public static async sortDockerfiles(sortableArrayArg: Dockerfile[]): Promise<Dockerfile[]> {
const done = plugins.smartpromise.defer<Dockerfile[]>();
logger.log('info', 'sorting Dockerfiles:');
const sortedArray: Dockerfile[] = [];
const cleanTagsOriginal = Dockerfile.cleanTagsArrayFunction(sortableArrayArg, sortedArray);
let sorterFunctionCounter: number = 0;
const sorterFunction = () => {
sortableArrayArg.forEach(dockerfileArg => {
const cleanTags = Dockerfile.cleanTagsArrayFunction(sortableArrayArg, sortedArray);
if (
cleanTags.indexOf(dockerfileArg.baseImage) === -1 &&
sortedArray.indexOf(dockerfileArg) === -1
) {
sortedArray.push(dockerfileArg);
}
if (cleanTagsOriginal.indexOf(dockerfileArg.baseImage) !== -1) {
dockerfileArg.localBaseImageDependent = true;
}
});
if (sortableArrayArg.length === sortedArray.length) {
let counter = 1;
for (const dockerfile of sortedArray) {
logger.log('info', `tag ${counter}: -> ${dockerfile.cleanTag}`);
counter++;
}
done.resolve(sortedArray);
} else if (sorterFunctionCounter < 10) {
sorterFunctionCounter++;
sorterFunction();
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;
// Check if the baseImage is among the local Dockerfiles
if (tagToDockerfile.has(baseImage)) {
const baseDockerfile = tagToDockerfile.get(baseImage);
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);
}
};
sorterFunction();
return done.promise;
try {
dockerfiles.forEach((dockerfile) => {
if (!visited.has(dockerfile)) {
visit(dockerfile);
}
});
} catch (error) {
logger.log('error', 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 correspoding Dockerfile class instances
*/
public static async mapDockerfiles(sortedDockerfileArray: Dockerfile[]): Promise<Dockerfile[]> {
sortedDockerfileArray.forEach(dockerfileArg => {
sortedDockerfileArray.forEach((dockerfileArg) => {
if (dockerfileArg.localBaseImageDependent) {
sortedDockerfileArray.forEach((dockfile2: Dockerfile) => {
if (dockfile2.cleanTag === dockerfileArg.baseImage) {
@@ -120,41 +153,98 @@ export class Dockerfile {
* returns a version for a docker file
* @execution SYNC
*/
public static dockerFileVersion(dockerfileNameArg: string): string {
public static dockerFileVersion(
dockerfileInstanceArg: Dockerfile,
dockerfileNameArg: string
): string {
let versionString: string;
const versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/;
const versionRegex = /Dockerfile_(.+)$/;
const regexResultArray = versionRegex.exec(dockerfileNameArg);
if (regexResultArray && regexResultArray.length === 2) {
versionString = regexResultArray[1];
} else {
versionString = 'latest';
}
versionString = versionString.replace(
'##version##',
dockerfileInstanceArg.npmciDockerManagerRef.szciRef.npmciConfig.getConfig().projectInfo.npm
.version
);
return versionString;
}
/**
* returns the docker base image for a Dockerfile
* Extracts the base image from a Dockerfile content without using external libraries.
* @param dockerfileContentArg The content of the Dockerfile as a string.
* @returns The base image specified in the first FROM instruction.
*/
public static dockerBaseImage(dockerfileContentArg: string): string {
const baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/;
const regexResultArray = baseImageRegex.exec(dockerfileContentArg);
return regexResultArray[1];
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] : Deno.env.get(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}.
* @param str The string containing variables.
* @param vars The object containing variable values.
* @returns The string with variables substituted.
*/
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 '';
}
});
}
/**
* returns the docker tag
*/
public static getDockerTagString(
npmciDockerManagerRef: NpmciDockerManager,
npmciDockerManagerRef: SzciDockerManager,
registryArg: string,
repoArg: string,
versionArg: string,
suffixArg?: string
): string {
// determine wether the repo should be mapped accordingly to the registry
const mappedRepo = npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerRegistryRepoMap[
registryArg
];
const mappedRepo =
npmciDockerManagerRef.szciRef.npmciConfig.getConfig().dockerRegistryRepoMap[registryArg];
const repo = (() => {
if (mappedRepo) {
return mappedRepo;
@@ -174,40 +264,27 @@ export class Dockerfile {
}
public static async getDockerBuildArgs(
npmciDockerManagerRef: NpmciDockerManager
npmciDockerManagerRef: SzciDockerManager
): Promise<string> {
logger.log('info', 'checking for env vars to be supplied to the docker build');
let buildArgsString: string = '';
for (const key of Object.keys(
npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerBuildargEnvMap
for (const dockerArgKey of Object.keys(
npmciDockerManagerRef.szciRef.npmciConfig.getConfig().dockerBuildargEnvMap
)) {
const targetValue =
process.env[
npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerBuildargEnvMap[key]
];
buildArgsString = `${buildArgsString} --build-arg ${key}="${targetValue}"`;
const dockerArgOuterEnvVar =
npmciDockerManagerRef.szciRef.npmciConfig.getConfig().dockerBuildargEnvMap[dockerArgKey];
logger.log(
'note',
`docker ARG "${dockerArgKey}" maps to outer env var "${dockerArgOuterEnvVar}"`
);
const targetValue = Deno.env.get(dockerArgOuterEnvVar);
buildArgsString = `${buildArgsString} --build-arg ${dockerArgKey}="${targetValue}"`;
}
return buildArgsString;
}
/**
*
*/
public static cleanTagsArrayFunction(
dockerfileArrayArg: Dockerfile[],
trackingArrayArg: Dockerfile[]
): string[] {
const cleanTagsArray: string[] = [];
dockerfileArrayArg.forEach(dockerfileArg => {
if (trackingArrayArg.indexOf(dockerfileArg) === -1) {
cleanTagsArray.push(dockerfileArg.cleanTag);
}
});
return cleanTagsArray;
}
// INSTANCE
public npmciDockerManagerRef: NpmciDockerManager;
public npmciDockerManagerRef: SzciDockerManager;
public filePath: string;
public repo: string;
@@ -222,16 +299,16 @@ export class Dockerfile {
public localBaseDockerfile: Dockerfile;
constructor(
dockerManagerRefArg: NpmciDockerManager,
dockerManagerRefArg: SzciDockerManager,
options: { filePath?: string; fileContents?: string | Buffer; read?: boolean }
) {
this.npmciDockerManagerRef = dockerManagerRefArg;
this.filePath = options.filePath;
this.repo =
this.npmciDockerManagerRef.npmciRef.npmciEnv.repo.user +
this.npmciDockerManagerRef.szciRef.npmciEnv.repo.user +
'/' +
this.npmciDockerManagerRef.npmciRef.npmciEnv.repo.repo;
this.version = Dockerfile.dockerFileVersion(plugins.path.parse(options.filePath).base);
this.npmciDockerManagerRef.szciRef.npmciEnv.repo.repo;
this.version = Dockerfile.dockerFileVersion(this, plugins.path.parse(options.filePath).base);
this.cleanTag = this.repo + ':' + this.version;
this.buildTag = this.cleanTag;
@@ -250,7 +327,7 @@ export class Dockerfile {
logger.log('info', 'now building Dockerfile for ' + this.cleanTag);
const buildArgsString = await Dockerfile.getDockerBuildArgs(this.npmciDockerManagerRef);
const buildCommand = `docker build --label="version=${
this.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version
this.npmciDockerManagerRef.szciRef.npmciConfig.getConfig().projectInfo.npm.version
}" -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
await bash(buildCommand);
return;
@@ -269,16 +346,20 @@ export class Dockerfile {
);
await bash(`docker tag ${this.buildTag} ${this.pushTag}`);
await bash(`docker push ${this.pushTag}`);
console.log('you can get the digest using this command');
console.log(`docker inspect --format='{{index .RepoDigests 0}}' ${this.pushTag}`);
/* const imageDigest = (await bash(
`docker inspect --format='{{index .RepoDigests 0}}' ${pushTag}`
)).split('@')[1]; */
await this.npmciDockerManagerRef.npmciRef.cloudlyConnector.announceDockerContainer({
dockerImageUrl: this.pushTag,
dockerImageVersion: this.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo
.npm.version
const imageDigest = (
await bash(`docker inspect --format="{{index .RepoDigests 0}}" ${this.pushTag}`)
).split('@')[1];
console.log(`The image ${this.pushTag} has digest ${imageDigest}`);
await this.npmciDockerManagerRef.szciRef.cloudlyConnector.announceDockerContainer({
registryUrl: this.pushTag,
tag: this.buildTag,
labels: [],
version: this.npmciDockerManagerRef.szciRef.npmciConfig.getConfig().projectInfo.npm.version,
});
await this.npmciDockerManagerRef.szciRef.npmciConfig.kvStorage.writeKey(
'latestPushedDockerTag',
this.pushTag
);
}
/**
@@ -300,7 +381,7 @@ export class Dockerfile {
* tests the Dockerfile;
*/
public async test() {
const testFile: string = plugins.path.join(paths.NpmciTestDir, 'test_' + this.version + '.sh');
const testFile: string = plugins.path.join(paths.SzciTestDir, 'test_' + this.version + '.sh');
const testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile);
if (testFileExists) {
// run tests

View File

@@ -1,6 +1,6 @@
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins';
import { bash } from '../npmci.bash';
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
import { bash } from '../szci.bash.ts';
export interface IDockerRegistryConstructorOptions {
registryUrl: string;
@@ -23,16 +23,16 @@ export class DockerRegistry {
const dockerRegexResultArray = envString.split('|');
if (dockerRegexResultArray.length !== 3) {
logger.log('error', 'malformed docker env var...');
process.exit(1);
Deno.exit(1);
return;
}
const registryUrl = dockerRegexResultArray[0];
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
password: password,
});
}

View File

@@ -1,11 +1,10 @@
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins';
import { Objectmap } from '@pushrocks/lik';
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
import { DockerRegistry } from './mod.classes.dockerregistry';
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
export class RegistryStorage {
objectMap = new Objectmap<DockerRegistry>();
objectMap = new plugins.lik.ObjectMap<DockerRegistry>();
constructor() {
// Nothing here
}
@@ -15,13 +14,13 @@ export class RegistryStorage {
}
getRegistryByUrl(registryUrlArg: string) {
return this.objectMap.find(registryArg => {
return this.objectMap.findSync((registryArg) => {
return registryArg.registryUrl === registryUrlArg;
});
}
async loginAll() {
await this.objectMap.forEach(async registryArg => {
await this.objectMap.forEach(async (registryArg) => {
await registryArg.login();
});
logger.log('success', 'logged in successfully into all available DockerRegistries!');

View File

@@ -1,5 +1,5 @@
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins';
import * as paths from '../npmci.paths';
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
import * as paths from '../szci.paths.ts';
import { Dockerfile } from './mod.classes.dockerfile';
import { Dockerfile } from './mod.classes.dockerfile.ts';

View File

@@ -1 +1 @@
export * from '../npmci.plugins';
export * from '../szci.plugins.ts';

View File

@@ -1,20 +1,20 @@
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins';
import { bash } from '../npmci.bash';
import { Npmci } from '../npmci.classes.npmci';
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
import { bash, bashNoError } from '../szci.bash.ts';
import { Szci } from '../szci.classes.szci.ts';
export class NpmciGitManager {
public npmciRef: Npmci;
export class SzciGitManager {
public szciRef: Szci;
constructor(npmciRefArg: Npmci) {
this.npmciRef = npmciRefArg;
constructor(szciRefArg: Szci) {
this.szciRef = szciRefArg;
}
/**
* handle cli input
* @param argvArg
*/
public handleCli = async argvArg => {
public handleCli = async (argvArg: any) => {
if (argvArg._.length >= 2) {
const action: string = argvArg._[1];
switch (action) {
@@ -30,12 +30,12 @@ export class NpmciGitManager {
};
public mirror = async () => {
const githubToken = process.env.NPMCI_GIT_GITHUBTOKEN;
const githubUser = process.env.NPMCI_GIT_GITHUBGROUP || this.npmciRef.npmciEnv.repo.user;
const githubRepo = process.env.NPMCI_GIT_GITHUB || this.npmciRef.npmciEnv.repo;
const githubToken = Deno.env.get("NPMCI_GIT_GITHUBTOKEN");
const githubUser = Deno.env.get("NPMCI_GIT_GITHUBGROUP") || this.szciRef.npmciEnv.repo.user;
const githubRepo = Deno.env.get("NPMCI_GIT_GITHUB") || this.szciRef.npmciEnv.repo.repo;
if (
this.npmciRef.npmciConfig.getConfig().projectInfo.npm.packageJson.private === true ||
this.npmciRef.npmciConfig.getConfig().npmAccessLevel === 'private'
this.szciRef.npmciConfig.getConfig().projectInfo.npm.packageJson.private === true ||
this.szciRef.npmciConfig.getConfig().npmAccessLevel === 'private'
) {
logger.log(
'warn',
@@ -47,19 +47,25 @@ export class NpmciGitManager {
logger.log('info', 'found github token.');
logger.log('info', 'attempting the mirror the repository to GitHub');
// plugins.smartgit.GitRepo;
// remove old mirrors
await bashNoError('git remote rm mirror');
await bash(`git fetch`);
// add the mirror
await bash(
await bashNoError(
`git remote add mirror https://${githubToken}@github.com/${githubUser}/${githubRepo}.git`
);
await bash(`git push mirror --all`);
await bashNoError(`git push mirror --all`);
await bashNoError(`git checkout origin/master`);
await bashNoError(`git push mirror master`);
logger.log('ok', 'pushed all branches to mirror!');
await bash(`git push mirror --tags`);
await bashNoError(`git push mirror --tags`);
logger.log('ok', 'pushed all tags to mirror!');
// remove old mirrors
await bashNoError('git remote rm mirror');
} else {
logger.log('error', `cannot find NPMCI_GIT_GITHUBTOKEN env var!`);
process.exit(1);
Deno.exit(1);
}
};
}

View File

@@ -1,5 +1 @@
export * from '../npmci.plugins';
import * as smartgit from '@pushrocks/smartgit';
export { smartgit };
export * from '../szci.plugins.ts';

View File

@@ -1,22 +1,22 @@
import * as plugins from '../npmci.plugins';
import * as paths from '../npmci.paths';
import * as plugins from '../szci.plugins.ts';
import * as paths from '../szci.paths.ts';
import { logger } from '../npmci.logging';
import { bash, bashNoError, nvmAvailable } from '../npmci.bash';
import { Npmci } from '../npmci.classes.npmci';
import { logger } from '../szci.logging.ts';
import { bash, bashNoError, nvmAvailable } from '../szci.bash.ts';
import { Szci } from '../szci.classes.szci.ts';
export class NpmciNodeJsManager {
public npmciRef: Npmci;
export class SzciNodeJsManager {
public szciRef: Szci;
constructor(npmciRefArg: Npmci) {
this.npmciRef = npmciRefArg;
constructor(szciRefArg: Szci) {
this.szciRef = szciRefArg;
}
/**
* handle cli input
* @param argvArg
*/
public async handleCli(argvArg) {
public async handleCli(argvArg: any) {
if (argvArg._.length >= 3) {
const action: string = argvArg._[1];
switch (action) {
@@ -25,14 +25,14 @@ export class NpmciNodeJsManager {
break;
default:
logger.log('error', `>>npmci node ...<< action >>${action}<< not supported`);
process.exit(1);
Deno.exit(1);
}
} else {
logger.log(
'error',
`>>npmci node ...<< cli arguments invalid... Please read the documentation.`
);
process.exit(1);
Deno.exit(1);
}
}
@@ -40,15 +40,15 @@ export class NpmciNodeJsManager {
* Install a specific version of node
* @param versionArg
*/
public async install(versionArg) {
public async install(versionArg: any) {
logger.log('info', `now installing node version ${versionArg}`);
let version: string;
if (versionArg === 'stable') {
version = '12';
version = '18';
} else if (versionArg === 'lts') {
version = '10';
version = '16';
} else if (versionArg === 'legacy') {
version = '8';
version = '14';
} else {
version = versionArg;
}
@@ -62,10 +62,9 @@ export class NpmciNodeJsManager {
await bash('npm install -g npm');
await bash('node -v');
await bash('npm -v');
await bash(`npm config set cache ${paths.NpmciCacheDir} --global `);
// lets look for further config
const config = await this.npmciRef.npmciConfig.getConfig();
const config = await this.szciRef.npmciConfig.getConfig();
logger.log('info', 'Now checking for needed global npm tools...');
for (const npmTool of config.npmGlobalTools) {
logger.log('info', `Checking for global "${npmTool}"`);

View File

@@ -1,27 +1,31 @@
import * as plugins from './mod.plugins';
import * as plugins from './mod.plugins.ts';
import * as paths from '../szci.paths.ts';
import { logger } from '../npmci.logging';
import { bash, bashNoError, nvmAvailable } from '../npmci.bash';
import { Npmci } from '../npmci.classes.npmci';
import { logger } from '../szci.logging.ts';
import { bash, bashNoError, nvmAvailable } from '../szci.bash.ts';
import { Szci } from '../szci.classes.szci.ts';
export class NpmciNpmManager {
public npmciRef: Npmci;
export class SzciNpmManager {
public szciRef: Szci;
constructor(npmciRefArg) {
this.npmciRef = npmciRefArg;
constructor(szciRefArg: Szci) {
this.szciRef = szciRefArg;
}
/**
* handle cli input
* @param argvArg
*/
public async handleCli(argvArg) {
public async handleCli(argvArg: any) {
if (argvArg._.length >= 2) {
const action: string = argvArg._[1];
switch (action) {
case 'install':
await this.install();
break;
case 'build':
await this.build();
break;
case 'prepare':
await this.prepare();
break;
@@ -33,14 +37,14 @@ export class NpmciNpmManager {
break;
default:
logger.log('error', `>>npmci npm ...<< action >>${action}<< not supported`);
process.exit(1);
Deno.exit(1);
}
} else {
logger.log(
'info',
`>>npmci npm ...<< cli arguments invalid... Please read the documentation.`
);
process.exit(1);
Deno.exit(1);
}
}
@@ -48,15 +52,29 @@ export class NpmciNpmManager {
* authenticates npm with token from env var
*/
public async prepare() {
const config = this.npmciRef.npmciConfig.getConfig();
logger.log('info', 'running >>npm prepare<<');
const config = this.szciRef.npmciConfig.getConfig();
let npmrcFileString: string = '';
await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TOKEN_NPM*', npmEnvArg => {
const npmRegistryUrl = npmEnvArg.split('|')[0];
const npmToken = npmEnvArg.split('|')[1];
npmrcFileString += `//${npmRegistryUrl}/:_authToken="${plugins.smartstring.base64.decode(
npmToken
)}"\n`;
});
await plugins.smartobject.forEachMinimatch(
Deno.env.toObject(),
'NPMCI_TOKEN_NPM*',
(npmEnvArg: string) => {
if (!npmEnvArg) {
logger.log('note','found empty token...');
return;
}
const npmRegistryUrl = npmEnvArg.split('|')[0];
logger.log('ok', `found token for ${npmRegistryUrl}`);
let npmToken = npmEnvArg.split('|')[1];
if (npmEnvArg.split('|')[2] && npmEnvArg.split('|')[2] === 'plain') {
logger.log('ok', 'npm token not base64 encoded.');
} else {
logger.log('ok', 'npm token base64 encoded.');
npmToken = plugins.smartstring.base64.decode(npmToken);
}
npmrcFileString += `//${npmRegistryUrl}/:_authToken="${npmToken}"\n`;
}
);
logger.log('info', `setting default npm registry to ${config.npmRegistryUrl}`);
npmrcFileString += `registry=https://${config.npmRegistryUrl}\n`;
@@ -65,11 +83,15 @@ export class NpmciNpmManager {
logger.log('info', 'found one or more access tokens');
} else {
logger.log('error', 'no access token found! Exiting!');
process.exit(1);
Deno.exit(1);
}
// lets save it to disk
plugins.smartfile.memory.toFsSync(npmrcFileString, '/root/.npmrc');
// lets set the cache directory
await bash(`npm config set cache ${paths.SzciCacheDir} --global `);
return;
}
@@ -81,11 +103,15 @@ export class NpmciNpmManager {
let npmAccessCliString = ``;
let npmRegistryCliString = ``;
let publishVerdaccioAsWell = false;
const config = this.npmciRef.npmciConfig.getConfig();
const config = this.szciRef.npmciConfig.getConfig();
const availableRegistries: string[] = [];
await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TOKEN_NPM*', npmEnvArg => {
availableRegistries.push(npmEnvArg.split('|')[0]);
});
await plugins.smartobject.forEachMinimatch(
Deno.env.toObject(),
'NPMCI_TOKEN_NPM*',
(npmEnvArg: string) => {
availableRegistries.push(npmEnvArg.split('|')[0]);
}
);
// -> configure package access level
if (config.npmAccessLevel) {
@@ -101,14 +127,14 @@ export class NpmciNpmManager {
npmRegistryCliString = `--registry=https://${config.npmRegistryUrl}`;
} else {
logger.log('error', `no registry url specified. Can't publish!`);
process.exit(1);
Deno.exit(1);
}
let publishCommand = `npm publish ${npmAccessCliString} ${npmRegistryCliString} `;
// publishEverywhere
if (publishVerdaccioAsWell) {
const verdaccioRegistry = availableRegistries.find(registryString =>
const verdaccioRegistry = availableRegistries.find((registryString) =>
registryString.startsWith('verdaccio')
);
if (verdaccioRegistry) {
@@ -131,10 +157,11 @@ export class NpmciNpmManager {
logger.log('info', `now preparing environment:`);
this.prepare();
await bash(`npm -v`);
await bash(`pnpm -v`);
// -> build it
await bash(`npm install`);
await bash(`npm run build`);
await this.install();
await this.build();
logger.log('success', `Nice!!! The build for the publication was successfull!`);
logger.log('info', `Lets clean up so we don't publish any packages that don't belong to us:`);
@@ -152,11 +179,16 @@ export class NpmciNpmManager {
public async install(): Promise<void> {
logger.log('info', 'now installing dependencies:');
await bash('npm install');
await bash('pnpm install');
}
public async build(): Promise<void> {
logger.log('info', 'now building the project:');
await bash('pnpm run build');
}
public async test(): Promise<void> {
logger.log('info', 'now starting tests:');
await bash('npm test');
await bash('pnpm test');
}
}

View File

@@ -1 +1 @@
export * from '../npmci.plugins';
export * from '../szci.plugins.ts';

View File

@@ -1,10 +1,10 @@
import * as plugins from './mod.plugins';
import * as paths from '../npmci.paths';
import * as plugins from './mod.plugins.ts';
import * as paths from '../szci.paths.ts';
/**
* cleans npmci config files
*/
export let clean = async (): Promise<void> => {
plugins.smartfile.fs.removeSync(paths.NpmciPackageConfig);
plugins.smartfile.fs.removeSync(paths.SzciPackageConfig);
return;
};

View File

@@ -1 +1 @@
export * from '../npmci.plugins';
export * from '../szci.plugins.ts';

View File

@@ -1,9 +1,9 @@
import * as plugins from './mod.plugins';
import { bash } from '../npmci.bash';
import * as plugins from './mod.plugins.ts';
import { bash } from '../szci.bash.ts';
export let command = async () => {
let wrappedCommand: string = '';
let argvArray = process.argv;
let argvArray = ['deno', 'mod.ts', ...Deno.args];
for (let i = 3; i < argvArray.length; i++) {
wrappedCommand = wrappedCommand + argvArray[i];
if (i + 1 !== argvArray.length) {

View File

@@ -1 +1 @@
export * from '../npmci.plugins';
export * from '../szci.plugins.ts';

24
ts/mod_precheck/index.ts Normal file
View File

@@ -0,0 +1,24 @@
import * as plugins from './plugins.ts';
import * as paths from '../szci.paths.ts';
import { logger } from '../szci.logging.ts';
import { Szci } from '../szci.classes.szci.ts';
export const handleCli = async (szciRefArg: Szci, argvArg: any) => {
logger.log('info', 'checking execution context');
const presentRunnerTags = Deno.env.get("CI_RUNNER_TAGS").split(',').map((stringArg) =>
stringArg.trim()
);
let allDesiredGitlabRunnerTagsPresent = true;
for (const desiredRunnerTag of szciRefArg.npmciConfig.getConfig().gitlabRunnerTags) {
if (!presentRunnerTags.includes(desiredRunnerTag)) {
allDesiredGitlabRunnerTagsPresent = false;
logger.log(
'error',
`Desired runnerRag ${desiredRunnerTag} is missing in current execution context.`
);
}
}
if (!allDesiredGitlabRunnerTagsPresent) {
Deno.exit(1);
}
};

View File

@@ -0,0 +1 @@
export * from '../szci.plugins.ts';

View File

@@ -1,8 +1,8 @@
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins';
import { logger } from '../szci.logging.ts';
import * as plugins from './mod.plugins.ts';
let sshInstance: plugins.smartssh.SshInstance;
export let handleCli = async argvArg => {
export let handleCli = async (argvArg: any) => {
if (argvArg._.length >= 2) {
const action: string = argvArg._[1];
switch (action) {
@@ -11,11 +11,11 @@ export let handleCli = async argvArg => {
break;
default:
logger.log('error', `action >>${action}<< not supported`);
process.exit(1);
Deno.exit(1);
}
} else {
logger.log('error', `>>npmci ssh ...<< please specify an action!`);
process.exit(1);
Deno.exit(1);
}
};
@@ -31,8 +31,8 @@ const notUndefined = (stringArg: string) => {
*/
export let prepare = async () => {
sshInstance = new plugins.smartssh.SshInstance(); // init ssh instance
plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_SSHKEY_*', evaluateSshEnv);
if (!process.env.NPMTS_TEST) {
plugins.smartobject.forEachMinimatch(Deno.env.toObject(), 'NPMCI_SSHKEY_*', evaluateSshEnv);
if (!Deno.env.get("NPMTS_TEST")) {
sshInstance.writeToDisk();
} else {
logger.log('info', 'In test mode, so not storing SSH keys to disk!');

View File

@@ -1 +1 @@
export * from '../npmci.plugins';
export * from '../szci.plugins.ts';

View File

@@ -1,15 +1,16 @@
import * as plugins from './mod.plugins';
import { bash } from '../npmci.bash';
import { logger } from '../npmci.logging';
import * as plugins from './mod.plugins.ts';
import { bash } from '../szci.bash.ts';
import { logger } from '../szci.logging.ts';
const triggerValueRegex = /^([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|?([a-zA-Z0-9\.\-\/]*)/;
const triggerValueRegex =
/^([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|?([a-zA-Z0-9\.\-\/]*)/;
export let trigger = async () => {
logger.log('info', 'now running triggers');
await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TRIGGER_*', evaluateTrigger);
await plugins.smartobject.forEachMinimatch(Deno.env.toObject(), 'NPMCI_TRIGGER_*', evaluateTrigger);
};
const evaluateTrigger = async triggerEnvVarArg => {
const evaluateTrigger = async (triggerEnvVarArg) => {
const triggerRegexResultArray = triggerValueRegex.exec(triggerEnvVarArg);
const regexDomain = triggerRegexResultArray[1];
const regexProjectId = triggerRegexResultArray[2];
@@ -30,13 +31,13 @@ const evaluateTrigger = async triggerEnvVarArg => {
{
name: 'token',
payload: regexProjectTriggerToken,
type: 'string'
type: 'string',
},
{
name: 'ref',
payload: regexRefName,
type: 'string'
}
type: 'string',
},
]
);
};

View File

@@ -1 +1 @@
export * from '../npmci.plugins';
export * from '../szci.plugins.ts';

View File

@@ -1,55 +0,0 @@
import * as plugins from './npmci.plugins';
import { CloudlyConnector } from './connector.cloudly/cloudlyconnector';
import { NpmciInfo } from './npmci.classes.npmciinfo';
import { NpmciCli } from './npmci.classes.npmcicli';
import { NpmciConfig } from './npmci.classes.npmciconfig';
// mods
import { NpmciDockerManager } from './manager.docker';
import { NpmciGitManager } from './manager.git';
import { NpmciNodeJsManager } from './manager.nodejs';
import { NpmciNpmManager } from './manager.npm';
import { NpmciEnv } from './npmci.classes.npmcienv';
export class Npmci {
public analytics: plugins.smartanalytics.Analytics;
public cloudlyConnector: CloudlyConnector;
public npmciEnv: NpmciEnv;
public npmciInfo: NpmciInfo;
public npmciConfig: NpmciConfig;
public npmciCli: NpmciCli;
// managers
public dockerManager: NpmciDockerManager;
public gitManager: NpmciGitManager;
public nodejsManager: NpmciNodeJsManager;
public npmManager: NpmciNpmManager;
constructor() {
this.analytics = new plugins.smartanalytics.Analytics({
apiEndPoint: 'https://pubapi.lossless.one/analytics',
projectId: 'gitzone',
appName: 'npmci'
});
this.cloudlyConnector = new CloudlyConnector(this);
this.npmciEnv = new NpmciEnv(this);
this.npmciInfo = new NpmciInfo(this);
this.npmciCli = new NpmciCli(this);
this.npmciConfig = new NpmciConfig(this);
// managers
this.dockerManager = new NpmciDockerManager(this);
this.gitManager = new NpmciGitManager(this);
this.nodejsManager = new NpmciNodeJsManager(this);
this.npmManager = new NpmciNpmManager(this);
}
public async start() {
await this.npmciInfo.printToConsole();
await this.npmciConfig.init();
this.npmciCli.startParse();
}
}

View File

@@ -1,110 +0,0 @@
import { logger } from './npmci.logging';
import * as plugins from './npmci.plugins';
import * as paths from './npmci.paths';
import { Npmci } from './npmci.classes.npmci';
export class NpmciCli {
public npmciRef: Npmci;
public smartcli: plugins.smartcli.Smartcli;
constructor(npmciArg: Npmci) {
this.npmciRef = npmciArg;
this.smartcli = new plugins.smartcli.Smartcli();
this.smartcli.addVersion(this.npmciRef.npmciInfo.projectInfo.version);
// clean
this.smartcli.addCommand('clean').subscribe(
async argv => {
const modClean = await import('./mod_clean/index');
await modClean.clean();
},
err => {
console.log(err);
process.exit(1);
}
);
// command
this.smartcli.addCommand('command').subscribe(
async argv => {
const modCommand = await import('./mod_command/index');
await modCommand.command();
},
err => {
console.log(err);
process.exit(1);
}
);
// command
this.smartcli.addCommand('git').subscribe(
async argvArg => {
await this.npmciRef.gitManager.handleCli(argvArg);
},
err => {
console.log(err);
process.exit(1);
}
);
// build
this.smartcli.addCommand('docker').subscribe(
async argvArg => {
await this.npmciRef.dockerManager.handleCli(argvArg);
},
err => {
console.log(err);
process.exit(1);
}
);
// node
this.smartcli.addCommand('node').subscribe(
async argvArg => {
await this.npmciRef.nodejsManager.handleCli(argvArg);
},
err => {
console.log(err);
process.exit(1);
}
);
// npm
this.smartcli.addCommand('npm').subscribe(
async argvArg => {
await this.npmciRef.npmManager.handleCli(argvArg);
},
err => {
console.log(err);
}
);
// trigger
this.smartcli.addCommand('ssh').subscribe(
async argvArg => {
const modSsh = await import('./mod_ssh/index');
await modSsh.handleCli(argvArg);
},
err => {
console.log(err);
process.exit(1);
}
);
// trigger
this.smartcli.addCommand('trigger').subscribe(
async argv => {
const modTrigger = await import('./mod_trigger/index');
await modTrigger.trigger();
},
err => {
console.log(err);
process.exit(1);
}
);
}
public startParse = () => {
this.smartcli.startParse();
};
}

View File

@@ -1,18 +0,0 @@
import * as plugins from './npmci.plugins';
import { Npmci } from './npmci.classes.npmci';
export class NpmciEnv {
public npmciRef: Npmci;
public repoString: string;
public repo: plugins.smartstring.GitRepo;
constructor(npmciRefArg: Npmci) {
this.npmciRef = npmciRefArg;
this.repoString = process.env.CI_REPOSITORY_URL;
if (!this.repoString) {
this.repoString = 'https://undefined:undefined@github.com/undefined/undefined.git';
}
this.repo = new plugins.smartstring.GitRepo(this.repoString);
}
}

View File

@@ -1,17 +0,0 @@
import * as plugins from './npmci.plugins';
import * as paths from './npmci.paths';
import { logger } from './npmci.logging';
import { Npmci } from './npmci.classes.npmci';
export class NpmciInfo {
public npmciRef: Npmci;
public projectInfo = new plugins.projectinfo.ProjectinfoNpm(paths.NpmciPackageRoot);
constructor(npmciArg: Npmci) {
this.npmciRef = npmciArg;
}
public printToConsole() {
logger.log('info', `npmci version: ${this.projectInfo.version}`);
}
}

View File

@@ -1,13 +0,0 @@
import * as plugins from './npmci.plugins';
export const cwd = process.cwd();
// package paths
export const NpmciPackageRoot = plugins.path.join(__dirname, '../');
export const NpmciPackageConfig = plugins.path.join(NpmciPackageRoot, './config.json');
// project paths
export const NpmciProjectDir = cwd;
export const NpmciProjectNogitDir = plugins.path.join(NpmciProjectDir, './.nogit');
export const NpmciTestDir = plugins.path.join(cwd, './test');
export const NpmciCacheDir = plugins.path.join(cwd, './.npmci_cache');

View File

@@ -1,57 +0,0 @@
// node native
import * as path from 'path';
export { path };
// @apiglobal
import * as typedrequest from '@apiglobal/typedrequest';
export {
typedrequest
};
// @servezone
import * as servezoneInterfaces from '@servezone/servezone-interfaces';
export { servezoneInterfaces };
// @pushrocks
import * as npmextra from '@pushrocks/npmextra';
import * as projectinfo from '@pushrocks/projectinfo';
import * as qenv from '@pushrocks/qenv';
import * as smartanalytics from '@pushrocks/smartanalytics';
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartfile from '@pushrocks/smartfile';
import * as smartcli from '@pushrocks/smartcli';
import * as smartlog from '@pushrocks/smartlog';
import * as smartlogDestinationLocal from '@pushrocks/smartlog-destination-local';
import * as smartparam from '@pushrocks/smartparam';
import * as smartpromise from '@pushrocks/smartpromise';
import * as smartrequest from '@pushrocks/smartrequest';
import * as smartshell from '@pushrocks/smartshell';
import * as smartsocket from '@pushrocks/smartsocket';
import * as smartssh from '@pushrocks/smartssh';
import * as smartstring from '@pushrocks/smartstring';
export {
npmextra,
projectinfo,
qenv,
smartanalytics,
smartdelay,
smartfile,
smartcli,
smartlog,
smartlogDestinationLocal,
smartparam,
smartpromise,
smartrequest,
smartshell,
smartsocket,
smartssh,
smartstring
};
import * as through2 from 'through2';
export { through2 };

View File

@@ -1,19 +1,17 @@
import { logger } from './npmci.logging';
import * as plugins from './npmci.plugins';
import * as paths from './npmci.paths';
import * as smartpromise from '@pushrocks/smartpromise';
import { logger } from './szci.logging.ts';
import * as plugins from './szci.plugins.ts';
import * as paths from './szci.paths.ts';
/**
* wether nvm is available or not
*/
export let nvmAvailable = smartpromise.defer<boolean>();
export let nvmAvailable = plugins.smartpromise.defer<boolean>();
/**
* the smartshell instance for npmci
*/
const npmciSmartshell = new plugins.smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: []
sourceFilePaths: [],
});
/**
@@ -21,7 +19,7 @@ const npmciSmartshell = new plugins.smartshell.Smartshell({
*/
const checkToolsAvailable = async () => {
// check for nvm
if (!process.env.NPMTS_TEST) {
if (!Deno.env.get('NPMTS_TEST')) {
if (
(await npmciSmartshell.execSilent(`bash -c "source /usr/local/nvm/nvm.sh"`)).exitCode === 0
) {
@@ -57,10 +55,10 @@ export let bash = async (commandArg: string, retryArg: number = 2): Promise<stri
retryArg = 0;
}
if (!process.env.NPMTS_TEST) {
if (!Deno.env.get('NPMTS_TEST')) {
// NPMTS_TEST is used during testing
for (let i = 0; i <= retryArg; i++) {
if (process.env.DEBUG_NPMCI === 'true') {
if (Deno.env.get('DEBUG_NPMCI') === 'true') {
console.log(commandArg);
}
execResult = await npmciSmartshell.exec(commandArg);
@@ -70,7 +68,7 @@ export let bash = async (commandArg: string, retryArg: number = 2): Promise<stri
// something went wrong and retries are exhausted
if (failOnError) {
logger.log('error', 'something went wrong and retries are exhausted');
process.exit(1);
Deno.exit(1);
}
} else if (execResult.exitCode === 0) {
// everything went fine, or no error wanted
@@ -84,7 +82,7 @@ export let bash = async (commandArg: string, retryArg: number = 2): Promise<stri
logger.log('info', 'ShellExec would be: ' + commandArg);
execResult = {
exitCode: 0,
stdout: 'testOutput'
stdout: 'testOutput',
};
}
return execResult.stdout;

61
ts/szci.classes.szci.ts Normal file
View File

@@ -0,0 +1,61 @@
import * as plugins from './szci.plugins.ts';
// env
import { SzciEnv } from './szci.classes.szcienv.ts';
import { SzciInfo } from './szci.classes.szciinfo.ts';
import { SzciCli } from './szci.classes.szcicli.ts';
import { SzciConfig } from './szci.classes.szciconfig.ts';
// connectors
import { CloudlyConnector } from './connector.cloudly/cloudlyconnector.ts';
// managers
import { SzciCloudronManager } from './manager.cloudron/index.ts';
import { SzciDockerManager } from './manager.docker/index.ts';
import { SzciGitManager } from './manager.git/index.ts';
import { SzciNodeJsManager } from './manager.nodejs/index.ts';
import { SzciNpmManager } from './manager.npm/index.ts';
export class Szci {
public analytics: plugins.smartanalytics.Analytics;
public cloudlyConnector: CloudlyConnector;
public npmciEnv: SzciEnv;
public npmciInfo: SzciInfo;
public npmciConfig: SzciConfig;
public npmciCli: SzciCli;
// managers
public cloudronManager: SzciCloudronManager;
public dockerManager: SzciDockerManager;
public gitManager: SzciGitManager;
public nodejsManager: SzciNodeJsManager;
public npmManager: SzciNpmManager;
constructor() {
this.analytics = new plugins.smartanalytics.Analytics({
apiEndPoint: 'https://pubapi.lossless.one/analytics',
projectId: 'gitzone',
appName: 'szci',
});
}
public async start() {
this.cloudlyConnector = new CloudlyConnector(this);
this.npmciEnv = new SzciEnv(this);
this.npmciInfo = new SzciInfo(this);
await this.npmciInfo.printToConsole();
this.npmciCli = new SzciCli(this);
this.npmciConfig = new SzciConfig(this);
await this.npmciConfig.init();
// managers
this.cloudronManager = new SzciCloudronManager(this);
this.dockerManager = new SzciDockerManager(this);
this.gitManager = new SzciGitManager(this);
this.nodejsManager = new SzciNodeJsManager(this);
this.npmManager = new SzciNpmManager(this);
this.npmciCli.startParse();
}
}

120
ts/szci.classes.szcicli.ts Normal file
View File

@@ -0,0 +1,120 @@
import { logger } from './szci.logging.ts';
import * as plugins from './szci.plugins.ts';
import * as paths from './szci.paths.ts';
import { Szci } from './szci.classes.szci.ts';
export class SzciCli {
public szciRef: Szci;
public smartcli: plugins.smartcli.Smartcli;
constructor(szciArg: Szci) {
this.szciRef = szciArg;
this.smartcli = new plugins.smartcli.Smartcli();
this.smartcli.addVersion(this.szciRef.npmciInfo.version);
// clean
this.smartcli.addCommand('clean').subscribe(
async (argv) => {
const modClean = await import('./mod_clean/index.js');
await modClean.clean();
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
// cloudron
this.smartcli.addCommand('cloudron').subscribe(
async (argv) => {
await this.szciRef.cloudronManager.handleCli(argv);
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
// command
this.smartcli.addCommand('command').subscribe(
async (argv) => {
const modCommand = await import('./mod_command/index.js');
await modCommand.command();
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
// git
this.smartcli.addCommand('git').subscribe(
async (argvArg) => {
await this.szciRef.gitManager.handleCli(argvArg);
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
// build
this.smartcli.addCommand('docker').subscribe(
async (argvArg) => {
await this.szciRef.dockerManager.handleCli(argvArg);
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
// node
this.smartcli.addCommand('node').subscribe(
async (argvArg) => {
await this.szciRef.nodejsManager.handleCli(argvArg);
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
// npm
this.smartcli.addCommand('npm').subscribe(
async (argvArg) => {
await this.szciRef.npmManager.handleCli(argvArg);
},
(err) => {
console.log(err);
}
);
this.smartcli.addCommand('precheck').subscribe(async (argvArg) => {
const modPrecheck = await import('./mod_precheck/index.js');
await modPrecheck.handleCli(this.szciRef, argvArg);
});
// trigger
this.smartcli.addCommand('ssh').subscribe(async (argvArg) => {
const modSsh = await import('./mod_ssh/index.js');
await modSsh.handleCli(argvArg);
});
// trigger
this.smartcli.addCommand('trigger').subscribe(
async (argv) => {
const modTrigger = await import('./mod_trigger/index.js');
await modTrigger.trigger();
},
(err) => {
console.log(err);
Deno.exit(1);
}
);
}
public startParse = () => {
this.smartcli.startParse();
};
}

View File

@@ -1,13 +1,13 @@
import * as plugins from './npmci.plugins';
import * as paths from './npmci.paths';
import * as plugins from './szci.plugins.ts';
import * as paths from './szci.paths.ts';
import { logger } from './npmci.logging';
import { Npmci } from './npmci.classes.npmci';
import { logger } from './szci.logging.ts';
import { Szci } from './szci.classes.szci.ts';
/**
* the main config interface for npmci
*/
export interface INpmciOptions {
export interface ISzciOptions {
projectInfo: plugins.projectinfo.ProjectInfo;
// npm
@@ -20,35 +20,42 @@ export interface INpmciOptions {
dockerRegistryRepoMap: { [key: string]: string };
dockerBuildargEnvMap: { [key: string]: string };
// gitlab
gitlabRunnerTags: string[];
// urls
urlCloudly: string;
// cloudron
cloudronAppName?: string;
}
/**
* a config class for Npmci
*/
export class NpmciConfig {
public npmciRef: Npmci;
export class SzciConfig {
public szciRef: Szci;
public npmciNpmextra: plugins.npmextra.Npmextra;
public kvStorage: plugins.npmextra.KeyValueStore;
public npmciQenv: plugins.qenv.Qenv;
private configObject: INpmciOptions;
private configObject: ISzciOptions;
constructor(npmciRefArg: Npmci) {
this.npmciRef = npmciRefArg;
constructor(szciRefArg: Szci) {
this.szciRef = szciRefArg;
}
public async init() {
this.npmciNpmextra = new plugins.npmextra.Npmextra(paths.cwd);
this.kvStorage = new plugins.npmextra.KeyValueStore(
'custom',
`${this.npmciRef.npmciEnv.repo.user}_${this.npmciRef.npmciEnv.repo.repo}`
);
this.kvStorage = new plugins.npmextra.KeyValueStore({
typeArg: 'userHomeDir',
identityArg: `.npmci_${this.szciRef.npmciEnv.repo.user}_${this.szciRef.npmciEnv.repo.repo}`,
});
this.npmciQenv = new plugins.qenv.Qenv(
paths.NpmciProjectDir,
paths.NpmciProjectNogitDir,
false,
logger
paths.SzciProjectDir,
paths.SzciProjectNogitDir,
false
);
this.configObject = {
@@ -58,16 +65,14 @@ export class NpmciConfig {
dockerRegistryRepoMap: {},
npmAccessLevel: 'private',
npmRegistryUrl: 'registry.npmjs.org',
gitlabRunnerTags: [],
dockerBuildargEnvMap: {},
urlCloudly: this.npmciQenv.getEnvVarOnDemand('NPMCI_URL_CLOUDLY')
urlCloudly: await this.npmciQenv.getEnvVarOnDemand('NPMCI_URL_CLOUDLY'),
};
this.configObject = this.npmciNpmextra.dataFor<ISzciOptions>('szci', this.configObject);
}
public async init() {
this.configObject = this.npmciNpmextra.dataFor<INpmciOptions>('npmci', this.configObject);
}
public getConfig(): INpmciOptions {
public getConfig(): ISzciOptions {
return this.configObject;
}
}

View File

@@ -0,0 +1,23 @@
import * as plugins from './szci.plugins.ts';
import { Szci } from './szci.classes.szci.ts';
export class SzciEnv {
public szciRef: Szci;
public repoString: string;
public repo: plugins.smartstring.GitRepo;
constructor(szciRefArg: Szci) {
this.szciRef = szciRefArg;
if (!this.repoString && Deno.env.get("GITLAB_CI")) {
this.repoString = Deno.env.get("CI_REPOSITORY_URL");
}
if (!this.repoString && Deno.env.get("NPMCI_COMPUTED_REPOURL")) {
this.repoString = Deno.env.get("NPMCI_COMPUTED_REPOURL");
}
if (!this.repoString) {
this.repoString = 'https://undefined:undefined@github.com/undefined/undefined.git';
}
this.repo = new plugins.smartstring.GitRepo(this.repoString);
}
}

View File

@@ -0,0 +1,18 @@
import * as plugins from './szci.plugins.ts';
import * as paths from './szci.paths.ts';
import { logger } from './szci.logging.ts';
import { Szci } from './szci.classes.szci.ts';
import denoConfig from '../deno.json' with { type: 'json' };
export class SzciInfo {
public szciRef: Szci;
public version = denoConfig.version;
constructor(szciArg: Szci) {
this.szciRef = szciArg;
}
public async printToConsole() {
await logger.log('info', `szci version: ${this.version}`);
}
}

View File

@@ -1,4 +1,4 @@
import * as plugins from './npmci.plugins';
import * as plugins from './szci.plugins.ts';
export const logger = new plugins.smartlog.Smartlog({
logContext: {
@@ -7,8 +7,8 @@ export const logger = new plugins.smartlog.Smartlog({
containerName: 'Some ContainerName',
environment: 'test',
runtime: 'node',
zone: 'Some Zone'
}
zone: 'Some Zone',
},
});
logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal());

16
ts/szci.paths.ts Normal file
View File

@@ -0,0 +1,16 @@
import * as plugins from './szci.plugins.ts';
export const cwd = Deno.cwd();
// package paths
export const SzciPackageRoot = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../'
);
export const SzciPackageConfig = plugins.path.join(SzciPackageRoot, './config.json');
// project paths
export const SzciProjectDir = cwd;
export const SzciProjectNogitDir = plugins.path.join(SzciProjectDir, './.nogit');
export const SzciTestDir = plugins.path.join(cwd, './test');
export const SzciCacheDir = plugins.path.join(cwd, './.npmci_cache');

67
ts/szci.plugins.ts Normal file
View File

@@ -0,0 +1,67 @@
// Deno std libraries
import * as path from '@std/path';
export { path };
// @apiglobal
import * as typedrequest from '@api.global/typedrequest';
export { typedrequest };
// @servezone
import * as servezoneApi from '@serve.zone/api';
export { servezoneApi };
// @push.rocks
import * as lik from '@push.rocks/lik';
import * as npmextra from '@push.rocks/npmextra';
import * as projectinfo from '@push.rocks/projectinfo';
import * as qenv from '@push.rocks/qenv';
import * as smartanalytics from '@push.rocks/smartanalytics';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartfile from '@push.rocks/smartfile';
import * as smartcli from '@push.rocks/smartcli';
import * as smartgit from '@push.rocks/smartgit';
import * as smartlog from '@push.rocks/smartlog';
import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local';
import * as smartobject from '@push.rocks/smartobject';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrequest from '@push.rocks/smartrequest';
import * as smartshell from '@push.rocks/smartshell';
import * as smartsocket from '@push.rocks/smartsocket';
import * as smartssh from '@push.rocks/smartssh';
import * as smartstring from '@push.rocks/smartstring';
export {
lik,
npmextra,
projectinfo,
qenv,
smartanalytics,
smartdelay,
smartfile,
smartgit,
smartcli,
smartlog,
smartlogDestinationLocal,
smartobject,
smartpath,
smartpromise,
smartrequest,
smartshell,
smartsocket,
smartssh,
smartstring,
};
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export { tsclass };
// third party
import * as through2 from 'through2';
export { through2 };

View File

@@ -1,17 +0,0 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}

64
uninstall.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
# SZCI Uninstaller Script
# Removes SZCI from the system (installed via install.sh)
#
# Usage:
# sudo bash uninstall.sh
set -e
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root (sudo bash uninstall.sh)"
exit 1
fi
echo "================================================"
echo " SZCI Uninstaller"
echo " Serve Zone CI/CD Tool Removal"
echo "================================================"
echo ""
# Default installation directory
INSTALL_DIR="/opt/szci"
# Step 1: Remove global symlinks
echo "Checking for global symlinks..."
if [ -L "/usr/local/bin/szci" ]; then
echo "Removing symlink: /usr/local/bin/szci"
rm -f /usr/local/bin/szci
fi
if [ -L "/usr/bin/szci" ]; then
echo "Removing symlink: /usr/bin/szci"
rm -f /usr/bin/szci
fi
# Step 2: Remove installation directory
if [ -d "$INSTALL_DIR" ]; then
echo "Removing installation directory: $INSTALL_DIR"
rm -rf "$INSTALL_DIR"
else
echo "Installation directory not found: $INSTALL_DIR"
fi
echo ""
echo "================================================"
echo " SZCI Uninstallation Complete!"
echo "================================================"
echo ""
# Check for npm global installation
NODE_PATH=$(which node 2>/dev/null || true)
if [ -n "$NODE_PATH" ]; then
NPM_PATH=$(dirname "$NODE_PATH")/npm
if [ -x "$NPM_PATH" ]; then
echo "If you installed SZCI via npm, you may want to uninstall it with:"
echo " npm uninstall -g @ship.zone/szci"
echo ""
fi
fi
echo "SZCI has been removed from your system."
echo ""