Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d18e53e30 | |||
| 9ad5222b95 | |||
| dba2e2ae68 | |||
| 6c8095260d | |||
| 95c4ae3b16 | |||
| 536ebca6ab | |||
| 88f64536c2 | |||
| 4854d27a19 | |||
| 75a0e8a7d8 | |||
| 43eb19f772 | |||
| dc2665d250 | |||
| 00f324e151 | |||
| e38cc40f11 | |||
| e9e8acafe4 | |||
| c763db40bb | |||
| 01256480c4 | |||
| c6918399bf | |||
| 66d28e5081 | |||
| 9de77139ea | |||
| 7b4bf10cc0 | |||
| 9eaa6347c1 | |||
| 6e4c967917 | |||
| 52a8f42fc9 | |||
| 8e9a61bbb2 | |||
| dc809a6023 | |||
| 7eeca992b0 | |||
| d018102014 | |||
| 838f2d6959 | |||
| 0d42e5f6eb | |||
| f96de8cdc3 | |||
| 965833916f | |||
| 81ec1391e3 | |||
| bbbca44640 | |||
| 7ba7dc54a1 | |||
| 4f98eeec2a | |||
| 419a6b9e5f | |||
| 9c040c34f5 | |||
| 50d3f2d795 | |||
| 305856b9e1 |
31
.gitea/release-template.md
Normal file
31
.gitea/release-template.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
## SZCI {{VERSION}}
|
||||||
|
|
||||||
|
Pre-compiled binaries for multiple platforms.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
#### Option 1: Via npm (recommended)
|
||||||
|
```bash
|
||||||
|
npm install -g @ship.zone/szci
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option 2: Via installer script
|
||||||
|
```bash
|
||||||
|
curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option 3: Direct binary download
|
||||||
|
Download the appropriate binary for your platform from the assets below and make it executable.
|
||||||
|
|
||||||
|
### Supported Platforms
|
||||||
|
- Linux x86_64 (x64)
|
||||||
|
- Linux ARM64 (aarch64)
|
||||||
|
- macOS x86_64 (Intel)
|
||||||
|
- macOS ARM64 (Apple Silicon)
|
||||||
|
- Windows x86_64
|
||||||
|
|
||||||
|
### Checksums
|
||||||
|
SHA256 checksums are provided in `SHA256SUMS.txt` for binary verification.
|
||||||
|
|
||||||
|
### npm Package
|
||||||
|
The npm package includes automatic binary detection and installation for your platform.
|
||||||
122
.gitea/workflows/README.md
Normal file
122
.gitea/workflows/README.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# SZCI Gitea CI/CD Workflows
|
||||||
|
|
||||||
|
This directory contains Gitea Actions workflows for automated CI/CD of the SZCI project.
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### 1. CI (`ci.yml`)
|
||||||
|
|
||||||
|
**Trigger:** Push to `master` branch or pull requests
|
||||||
|
|
||||||
|
**Purpose:** Continuous integration checks on every push
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
- **Type Check & Lint** - Validates TypeScript types and code style
|
||||||
|
- **Build Test** - Compiles binary for Linux x64 and tests execution
|
||||||
|
- **Build All Platforms** - Compiles all 5 platform binaries and uploads as artifacts
|
||||||
|
|
||||||
|
**Usage:** Automatically runs on every commit to ensure code quality
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Release (`release.yml`)
|
||||||
|
|
||||||
|
**Trigger:** Push of version tags (e.g., `v5.0.0`)
|
||||||
|
|
||||||
|
**Purpose:** Creates GitHub releases with pre-compiled binaries
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
1. Validates deno.json version matches git tag
|
||||||
|
2. Compiles binaries for all 5 platforms
|
||||||
|
3. Generates SHA256 checksums
|
||||||
|
4. Creates Gitea release with binaries as assets
|
||||||
|
5. Cleans up old releases (keeps last 3)
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Update version in deno.json to 6.0.1
|
||||||
|
# Then create and push tag:
|
||||||
|
git tag v6.0.1
|
||||||
|
git push origin v6.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:** Release at `https://code.foss.global/ship.zone/szci/releases/tag/v6.0.1`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. NPM Publish (`npm-publish.yml`)
|
||||||
|
|
||||||
|
**Trigger:** Push of version tags (e.g., `v5.0.0`)
|
||||||
|
|
||||||
|
**Purpose:** Publishes package to npm registry
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
1. Validates deno.json version matches git tag
|
||||||
|
2. Compiles all binaries
|
||||||
|
3. Syncs package.json version
|
||||||
|
4. Creates npm package
|
||||||
|
5. Publishes to npm with access token
|
||||||
|
6. Verifies publication
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- `NPM_TOKEN` must be set in Gitea secrets
|
||||||
|
|
||||||
|
**Usage:** Same as release workflow (automatically triggered by version tags)
|
||||||
|
|
||||||
|
**Output:** Package at `https://www.npmjs.com/package/@ship.zone/szci`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
To create a new release:
|
||||||
|
|
||||||
|
1. **Update version in deno.json:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "6.0.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Commit the version change:**
|
||||||
|
```bash
|
||||||
|
git add deno.json
|
||||||
|
git commit -m "6.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create and push tag:**
|
||||||
|
```bash
|
||||||
|
git tag v6.0.1
|
||||||
|
git push origin master
|
||||||
|
git push origin v6.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Workflows run automatically:**
|
||||||
|
- `release.yml` creates Gitea release with binaries
|
||||||
|
- `npm-publish.yml` publishes to npm
|
||||||
|
|
||||||
|
5. **Verify:**
|
||||||
|
- Check https://code.foss.global/ship.zone/szci/releases
|
||||||
|
- Check https://www.npmjs.com/package/@ship.zone/szci
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Secrets Required
|
||||||
|
|
||||||
|
Configure these in Gitea repository settings:
|
||||||
|
|
||||||
|
- `GITHUB_TOKEN` - Gitea access token (auto-provided)
|
||||||
|
- `NPM_TOKEN` - npm publish token (must be configured)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Binary Artifacts
|
||||||
|
|
||||||
|
Each workflow produces binaries for:
|
||||||
|
- Linux x86_64
|
||||||
|
- Linux ARM64
|
||||||
|
- macOS x86_64
|
||||||
|
- macOS ARM64
|
||||||
|
- Windows x86_64
|
||||||
|
|
||||||
|
Total size per release: ~4GB (5 binaries × ~800MB each)
|
||||||
82
.gitea/workflows/ci.yml
Normal file
82
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Type Check & Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Check TypeScript types
|
||||||
|
run: deno check mod.ts
|
||||||
|
|
||||||
|
- name: Lint code
|
||||||
|
run: deno lint
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Format check
|
||||||
|
run: deno fmt --check
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build Test (Current Platform)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Compile for current platform
|
||||||
|
run: |
|
||||||
|
echo "Testing compilation for Linux x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output szci-test \
|
||||||
|
--target x86_64-unknown-linux-gnu mod.ts
|
||||||
|
|
||||||
|
- name: Test binary execution
|
||||||
|
run: |
|
||||||
|
chmod +x szci-test
|
||||||
|
./szci-test --version
|
||||||
|
|
||||||
|
build-all:
|
||||||
|
name: Build All Platforms
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Compile all platform binaries
|
||||||
|
run: bash scripts/compile-all.sh
|
||||||
|
|
||||||
|
- name: Upload all binaries as artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: szci-binaries.zip
|
||||||
|
path: dist/binaries/*
|
||||||
|
retention-days: 30
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
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 @gitzone/tsdoc
|
|
||||||
npmci command tsdoc
|
|
||||||
continue-on-error: true
|
|
||||||
129
.gitea/workflows/npm-publish.yml
Normal file
129
.gitea/workflows/npm-publish.yml
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
name: Publish to npm
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Setup Node.js for npm publishing
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org/'
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Publishing version: $VERSION"
|
||||||
|
|
||||||
|
- name: Verify deno.json version matches tag
|
||||||
|
run: |
|
||||||
|
DENO_VERSION=$(grep -o '"version": "[^"]*"' deno.json | cut -d'"' -f4)
|
||||||
|
TAG_VERSION="${{ steps.version.outputs.version_number }}"
|
||||||
|
echo "deno.json version: $DENO_VERSION"
|
||||||
|
echo "Tag version: $TAG_VERSION"
|
||||||
|
if [ "$DENO_VERSION" != "$TAG_VERSION" ]; then
|
||||||
|
echo "ERROR: Version mismatch!"
|
||||||
|
echo "deno.json has version $DENO_VERSION but tag is $TAG_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Compile binaries for npm package
|
||||||
|
run: |
|
||||||
|
echo "Compiling binaries for npm package..."
|
||||||
|
deno task compile
|
||||||
|
echo ""
|
||||||
|
echo "Binary sizes:"
|
||||||
|
ls -lh dist/binaries/
|
||||||
|
|
||||||
|
- name: Generate SHA256 checksums
|
||||||
|
run: |
|
||||||
|
cd dist/binaries
|
||||||
|
sha256sum * > SHA256SUMS
|
||||||
|
cat SHA256SUMS
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
- name: Sync package.json version
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version_number }}"
|
||||||
|
echo "Syncing package.json to version ${VERSION}..."
|
||||||
|
npm version ${VERSION} --no-git-tag-version --allow-same-version
|
||||||
|
echo "package.json version: $(grep '"version"' package.json | head -1)"
|
||||||
|
|
||||||
|
- name: Create npm package
|
||||||
|
run: |
|
||||||
|
echo "Creating npm package..."
|
||||||
|
npm pack
|
||||||
|
echo ""
|
||||||
|
echo "Package created:"
|
||||||
|
ls -lh *.tgz
|
||||||
|
|
||||||
|
- name: Test local installation
|
||||||
|
run: |
|
||||||
|
echo "Testing local package installation..."
|
||||||
|
PACKAGE_FILE=$(ls *.tgz)
|
||||||
|
npm install -g ${PACKAGE_FILE}
|
||||||
|
echo ""
|
||||||
|
echo "Testing szci command:"
|
||||||
|
szci --version || echo "Note: Binary execution may fail in CI environment"
|
||||||
|
echo ""
|
||||||
|
echo "Checking installed files:"
|
||||||
|
npm ls -g @ship.zone/szci || true
|
||||||
|
|
||||||
|
- name: Publish to npm
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "Publishing to npm registry..."
|
||||||
|
npm publish --access public
|
||||||
|
echo ""
|
||||||
|
echo "✅ Successfully published @ship.zone/szci to npm!"
|
||||||
|
echo ""
|
||||||
|
echo "Package info:"
|
||||||
|
npm view @ship.zone/szci
|
||||||
|
|
||||||
|
- name: Verify npm package
|
||||||
|
run: |
|
||||||
|
echo "Waiting for npm propagation..."
|
||||||
|
sleep 30
|
||||||
|
echo ""
|
||||||
|
echo "Verifying published package..."
|
||||||
|
npm view @ship.zone/szci
|
||||||
|
echo ""
|
||||||
|
echo "Testing installation from npm:"
|
||||||
|
npm install -g @ship.zone/szci
|
||||||
|
echo ""
|
||||||
|
echo "Package installed successfully!"
|
||||||
|
which szci || echo "Binary location check skipped"
|
||||||
|
|
||||||
|
- name: Publish Summary
|
||||||
|
run: |
|
||||||
|
echo "================================================"
|
||||||
|
echo " npm Publish Complete!"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
echo "✅ Package: @ship.zone/szci"
|
||||||
|
echo "✅ Version: ${{ steps.version.outputs.version }}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation:"
|
||||||
|
echo " npm install -g @ship.zone/szci"
|
||||||
|
echo ""
|
||||||
|
echo "Registry:"
|
||||||
|
echo " https://www.npmjs.com/package/@ship.zone/szci"
|
||||||
|
echo ""
|
||||||
248
.gitea/workflows/release.yml
Normal file
248
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building version: $VERSION"
|
||||||
|
|
||||||
|
- name: Verify deno.json version matches tag
|
||||||
|
run: |
|
||||||
|
DENO_VERSION=$(grep -o '"version": "[^"]*"' deno.json | cut -d'"' -f4)
|
||||||
|
TAG_VERSION="${{ steps.version.outputs.version_number }}"
|
||||||
|
echo "deno.json version: $DENO_VERSION"
|
||||||
|
echo "Tag version: $TAG_VERSION"
|
||||||
|
if [ "$DENO_VERSION" != "$TAG_VERSION" ]; then
|
||||||
|
echo "ERROR: Version mismatch!"
|
||||||
|
echo "deno.json has version $DENO_VERSION but tag is $TAG_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Compile binaries for all platforms
|
||||||
|
run: |
|
||||||
|
echo "================================================"
|
||||||
|
echo " SZCI Release Compilation"
|
||||||
|
echo " Version: ${{ steps.version.outputs.version }}"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Clean up old binaries and create fresh directory
|
||||||
|
rm -rf dist/binaries
|
||||||
|
mkdir -p dist/binaries
|
||||||
|
echo "→ Cleaned old binaries from dist/binaries"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Linux x86_64
|
||||||
|
echo "→ Compiling for Linux x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/szci-linux-x64 \
|
||||||
|
--target x86_64-unknown-linux-gnu mod.ts
|
||||||
|
echo " ✓ Linux x86_64 complete"
|
||||||
|
|
||||||
|
# Linux ARM64
|
||||||
|
echo "→ Compiling for Linux ARM64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/szci-linux-arm64 \
|
||||||
|
--target aarch64-unknown-linux-gnu mod.ts
|
||||||
|
echo " ✓ Linux ARM64 complete"
|
||||||
|
|
||||||
|
# macOS x86_64
|
||||||
|
echo "→ Compiling for macOS x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/szci-macos-x64 \
|
||||||
|
--target x86_64-apple-darwin mod.ts
|
||||||
|
echo " ✓ macOS x86_64 complete"
|
||||||
|
|
||||||
|
# macOS ARM64
|
||||||
|
echo "→ Compiling for macOS ARM64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/szci-macos-arm64 \
|
||||||
|
--target aarch64-apple-darwin mod.ts
|
||||||
|
echo " ✓ macOS ARM64 complete"
|
||||||
|
|
||||||
|
# Windows x86_64
|
||||||
|
echo "→ Compiling for Windows x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/szci-windows-x64.exe \
|
||||||
|
--target x86_64-pc-windows-msvc mod.ts
|
||||||
|
echo " ✓ Windows x86_64 complete"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "All binaries compiled successfully!"
|
||||||
|
ls -lh dist/binaries/
|
||||||
|
|
||||||
|
- name: Generate SHA256 checksums
|
||||||
|
run: |
|
||||||
|
cd dist/binaries
|
||||||
|
sha256sum * > SHA256SUMS.txt
|
||||||
|
cat SHA256SUMS.txt
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
- name: Extract changelog for this version
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
# Check if changelog.md exists
|
||||||
|
if [ ! -f changelog.md ]; then
|
||||||
|
echo "No changelog.md found, using default release notes"
|
||||||
|
cat > /tmp/release_notes.md << EOF
|
||||||
|
## SZCI $VERSION
|
||||||
|
|
||||||
|
Pre-compiled binaries for multiple platforms.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Use the installation script:
|
||||||
|
\`\`\`bash
|
||||||
|
curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Or download the binary for your platform and make it executable.
|
||||||
|
|
||||||
|
### Supported Platforms
|
||||||
|
- Linux x86_64 (x64)
|
||||||
|
- Linux ARM64 (aarch64)
|
||||||
|
- macOS x86_64 (Intel)
|
||||||
|
- macOS ARM64 (Apple Silicon)
|
||||||
|
- Windows x86_64
|
||||||
|
|
||||||
|
### Checksums
|
||||||
|
SHA256 checksums are provided in SHA256SUMS.txt
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# Try to extract section for this version from changelog.md
|
||||||
|
awk "/## \[$VERSION\]/,/## \[/" changelog.md | sed '$d' > /tmp/release_notes.md || cat > /tmp/release_notes.md << EOF
|
||||||
|
## SZCI $VERSION
|
||||||
|
|
||||||
|
See changelog.md for full details.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Use the installation script:
|
||||||
|
\`\`\`bash
|
||||||
|
curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash
|
||||||
|
\`\`\`
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Release notes:"
|
||||||
|
cat /tmp/release_notes.md
|
||||||
|
|
||||||
|
- name: Delete existing release if it exists
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
echo "Checking for existing release $VERSION..."
|
||||||
|
|
||||||
|
# Try to get existing release by tag
|
||||||
|
EXISTING_RELEASE_ID=$(curl -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/ship.zone/szci/releases/tags/$VERSION" \
|
||||||
|
| jq -r '.id // empty')
|
||||||
|
|
||||||
|
if [ -n "$EXISTING_RELEASE_ID" ]; then
|
||||||
|
echo "Found existing release (ID: $EXISTING_RELEASE_ID), deleting..."
|
||||||
|
curl -X DELETE -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/ship.zone/szci/releases/$EXISTING_RELEASE_ID"
|
||||||
|
echo "Existing release deleted"
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
echo "No existing release found, proceeding with creation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create Gitea Release
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
RELEASE_NOTES=$(cat /tmp/release_notes.md)
|
||||||
|
|
||||||
|
# Create the release
|
||||||
|
echo "Creating release for $VERSION..."
|
||||||
|
RELEASE_ID=$(curl -X POST -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://code.foss.global/api/v1/repos/ship.zone/szci/releases" \
|
||||||
|
-d "{
|
||||||
|
\"tag_name\": \"$VERSION\",
|
||||||
|
\"name\": \"SZCI $VERSION\",
|
||||||
|
\"body\": $(jq -Rs . /tmp/release_notes.md),
|
||||||
|
\"draft\": false,
|
||||||
|
\"prerelease\": false
|
||||||
|
}" | jq -r '.id')
|
||||||
|
|
||||||
|
echo "Release created with ID: $RELEASE_ID"
|
||||||
|
|
||||||
|
# Upload binaries as release assets
|
||||||
|
for binary in dist/binaries/*; do
|
||||||
|
filename=$(basename "$binary")
|
||||||
|
echo "Uploading $filename..."
|
||||||
|
curl -X POST -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary "@$binary" \
|
||||||
|
"https://code.foss.global/api/v1/repos/ship.zone/szci/releases/$RELEASE_ID/assets?name=$filename"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All assets uploaded successfully"
|
||||||
|
|
||||||
|
- name: Clean up old releases
|
||||||
|
run: |
|
||||||
|
echo "Cleaning up old releases (keeping only last 3)..."
|
||||||
|
|
||||||
|
# Fetch all releases sorted by creation date
|
||||||
|
RELEASES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/ship.zone/szci/releases" | \
|
||||||
|
jq -r 'sort_by(.created_at) | reverse | .[3:] | .[].id')
|
||||||
|
|
||||||
|
# Delete old releases
|
||||||
|
if [ -n "$RELEASES" ]; then
|
||||||
|
echo "Found releases to delete:"
|
||||||
|
for release_id in $RELEASES; do
|
||||||
|
echo " Deleting release ID: $release_id"
|
||||||
|
curl -X DELETE -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/ship.zone/szci/releases/$release_id"
|
||||||
|
done
|
||||||
|
echo "Old releases deleted successfully"
|
||||||
|
else
|
||||||
|
echo "No old releases to delete (less than 4 releases total)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
- name: Release Summary
|
||||||
|
run: |
|
||||||
|
echo "================================================"
|
||||||
|
echo " Release ${{ steps.version.outputs.version }} Complete!"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
echo "Binaries published:"
|
||||||
|
ls -lh dist/binaries/
|
||||||
|
echo ""
|
||||||
|
echo "Release URL:"
|
||||||
|
echo "https://code.foss.global/ship.zone/szci/releases/tag/${{ steps.version.outputs.version }}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation command:"
|
||||||
|
echo "curl -sSL https://code.foss.global/ship.zone/szci/raw/branch/master/install.sh | sudo bash"
|
||||||
|
echo ""
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,4 +17,8 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
|
# deno
|
||||||
|
deno.lock
|
||||||
|
.deno/
|
||||||
|
|
||||||
# custom
|
# custom
|
||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -5,9 +5,9 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"npmci": {
|
"szci": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "settings for npmci"
|
"description": "settings for szci"
|
||||||
},
|
},
|
||||||
"gitzone": {
|
"gitzone": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
108
bin/szci-wrapper.js
Executable file
108
bin/szci-wrapper.js
Executable 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
115
changelog.md
Normal 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.
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
process.env.CLI_CALL = 'true';
|
|
||||||
import * as cliTool from './ts/index.js';
|
|
||||||
cliTool.runCli();
|
|
||||||
4
cli.js
4
cli.js
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
process.env.CLI_CALL = 'true';
|
|
||||||
const cliTool = await import('./dist_ts/index.js');
|
|
||||||
cliTool.runCli();
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
process.env.CLI_CALL = 'true';
|
|
||||||
|
|
||||||
import * as tsrun from '@gitzone/tsrun';
|
|
||||||
tsrun.runPath('./cli.child.js', import.meta.url);
|
|
||||||
67
deno.json
Normal file
67
deno.json
Normal 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
257
install.sh
Executable 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 ""
|
||||||
48
mod.ts
Normal file
48
mod.ts
Normal 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';
|
||||||
@@ -1,22 +1,39 @@
|
|||||||
{
|
{
|
||||||
"npmci": {
|
"@ship.zone/szci": {
|
||||||
"npmGlobalTools": [],
|
"npmGlobalTools": [],
|
||||||
"npmAccessLevel": "public",
|
"npmAccessLevel": "public",
|
||||||
"npmRegistryUrl": "registry.npmjs.org"
|
"npmRegistryUrl": "registry.npmjs.org"
|
||||||
},
|
},
|
||||||
"npmdocker": {
|
"@git.zone/tsdocker": {
|
||||||
"baseImage": "hosttoday/ht-docker-node:npmci",
|
"baseImage": "hosttoday/ht-docker-node:npmci",
|
||||||
"command": "npmci test stable"
|
"command": "szci test stable"
|
||||||
},
|
},
|
||||||
"gitzone": {
|
"@git.zone/cli": {
|
||||||
"projectType": "npm",
|
"projectType": "npm",
|
||||||
"module": {
|
"module": {
|
||||||
"githost": "gitlab.com",
|
"githost": "gitlab.com",
|
||||||
"gitscope": "shipzone",
|
"gitscope": "ship.zone",
|
||||||
"gitrepo": "npmci",
|
"gitrepo": "szci",
|
||||||
"description": "node and docker in gitlab ci on steroids",
|
"description": "A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities.",
|
||||||
"npmPackagename": "@shipzone/npmci",
|
"npmPackagename": "@ship.zone/szci",
|
||||||
"license": "MIT"
|
"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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
120
package.json
120
package.json
@@ -1,77 +1,67 @@
|
|||||||
{
|
{
|
||||||
"name": "@shipzone/npmci",
|
"name": "@ship.zone/szci",
|
||||||
"version": "4.1.21",
|
"version": "6.0.1",
|
||||||
"private": false,
|
"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.",
|
||||||
"description": "node and docker in gitlab ci on steroids",
|
"keywords": [
|
||||||
"main": "dist_ts/index.js",
|
"Node.js",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"Docker",
|
||||||
"type": "module",
|
"GitLab CI",
|
||||||
"bin": {
|
"GitHub CI",
|
||||||
"npmci": "cli.js"
|
"Gitea CI",
|
||||||
},
|
"CI/CD",
|
||||||
"scripts": {
|
"automation",
|
||||||
"test": "tstest test/",
|
"npm",
|
||||||
"build": "tsbuild --allowimplicitany && (npm run testVersion)",
|
"TypeScript",
|
||||||
"testVersion": "(cd test/assets/ && node ../../cli.js -v)",
|
"Deno",
|
||||||
"buildDocs": "tsdoc"
|
"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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+ssh://git@gitlab.com/gitzone/npmci.git"
|
"url": "git+https://code.foss.global/ship.zone/szci.git"
|
||||||
},
|
},
|
||||||
"author": "Lossless GmbH",
|
"author": "Lossless GmbH",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"type": "module",
|
||||||
"url": "https://gitlab.com/gitzone/npmci/issues"
|
"bin": {
|
||||||
|
"szci": "./bin/szci-wrapper.js"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/gitzone/npmci#README",
|
"scripts": {
|
||||||
"devDependencies": {
|
"postinstall": "node scripts/install-binary.js",
|
||||||
"@gitzone/tsbuild": "^2.1.66",
|
"prepublishOnly": "echo 'Publishing SZCI binaries to npm...'",
|
||||||
"@gitzone/tsrun": "^1.2.42",
|
"test": "echo 'Tests are run with Deno: deno task test'",
|
||||||
"@gitzone/tstest": "^1.0.74",
|
"build": "echo 'no build needed'"
|
||||||
"@push.rocks/tapbundle": "^5.0.8",
|
|
||||||
"@types/node": "^20.4.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@apiglobal/typedrequest": "^2.0.12",
|
|
||||||
"@push.rocks/lik": "^6.0.3",
|
|
||||||
"@push.rocks/npmextra": "^3.0.9",
|
|
||||||
"@push.rocks/projectinfo": "^5.0.1",
|
|
||||||
"@push.rocks/qenv": "^5.0.2",
|
|
||||||
"@push.rocks/smartanalytics": "^2.0.15",
|
|
||||||
"@push.rocks/smartcli": "^4.0.6",
|
|
||||||
"@push.rocks/smartdelay": "^3.0.1",
|
|
||||||
"@push.rocks/smartenv": "^5.0.5",
|
|
||||||
"@push.rocks/smartfile": "^10.0.28",
|
|
||||||
"@push.rocks/smartgit": "^3.0.0",
|
|
||||||
"@push.rocks/smartlog": "^3.0.2",
|
|
||||||
"@push.rocks/smartlog-destination-local": "^9.0.0",
|
|
||||||
"@push.rocks/smartobject": "^1.0.10",
|
|
||||||
"@push.rocks/smartpath": "^5.0.11",
|
|
||||||
"@push.rocks/smartpromise": "^4.0.2",
|
|
||||||
"@push.rocks/smartrequest": "^2.0.18",
|
|
||||||
"@push.rocks/smartshell": "^3.0.3",
|
|
||||||
"@push.rocks/smartsocket": "^2.0.19",
|
|
||||||
"@push.rocks/smartssh": "^2.0.0",
|
|
||||||
"@push.rocks/smartstring": "^4.0.7",
|
|
||||||
"@servezone/interfaces": "^1.0.3",
|
|
||||||
"@tsclass/tsclass": "^4.0.42",
|
|
||||||
"@types/through2": "^2.0.38",
|
|
||||||
"through2": "^4.0.2"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"ts/**/*",
|
"bin/",
|
||||||
"ts_web/**/*",
|
"scripts/install-binary.js",
|
||||||
"dist/**/*",
|
"readme.md",
|
||||||
"dist_*/**/*",
|
"license",
|
||||||
"dist_ts/**/*",
|
"changelog.md"
|
||||||
"dist_ts_web/**/*",
|
|
||||||
"assets/**/*",
|
|
||||||
"cli.js",
|
|
||||||
"npmextra.json",
|
|
||||||
"readme.md"
|
|
||||||
],
|
],
|
||||||
"browserslist": [
|
"engines": {
|
||||||
"last 1 chrome versions"
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
4917
pnpm-lock.yaml
generated
4917
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
31
readme.hints.md
Normal file
31
readme.hints.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
- focus on cli usage in CI environments.
|
||||||
|
- show Gitlab CI, GitHub CI and Gitea CI examples.
|
||||||
|
|
||||||
|
## Deno Migration Status
|
||||||
|
|
||||||
|
The project has been fully migrated from Node.js to Deno runtime.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
All environment variables have been rebranded from NPMCI_* to SZCI_*:
|
||||||
|
|
||||||
|
| Old Name | New Name |
|
||||||
|
|----------|----------|
|
||||||
|
| `NPMCI_COMPUTED_REPOURL` | `SZCI_COMPUTED_REPOURL` |
|
||||||
|
| `NPMCI_URL_CLOUDLY` | `SZCI_URL_CLOUDLY` |
|
||||||
|
| `NPMCI_GIT_GITHUBTOKEN` | `SZCI_GIT_GITHUBTOKEN` |
|
||||||
|
| `NPMCI_GIT_GITHUBGROUP` | `SZCI_GIT_GITHUBGROUP` |
|
||||||
|
| `NPMCI_GIT_GITHUB` | `SZCI_GIT_GITHUB` |
|
||||||
|
| `NPMCI_TRIGGER_*` | `SZCI_TRIGGER_*` |
|
||||||
|
| `NPMCI_LOGIN_CLOUDRON` | `SZCI_LOGIN_CLOUDRON` |
|
||||||
|
| `NPMCI_SSHKEY_*` | `SZCI_SSHKEY_*` |
|
||||||
|
| `NPMCI_LOGIN_DOCKER*` | `SZCI_LOGIN_DOCKER*` |
|
||||||
|
| `NPMCI_TOKEN_NPM*` | `SZCI_TOKEN_NPM*` |
|
||||||
|
| `NPMTS_TEST` | `SZCI_TEST` |
|
||||||
|
| `DEBUG_NPMCI` | `DEBUG_SZCI` |
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
|
||||||
|
- Uses Deno APIs (`Deno.env`, `Deno.cwd`, `Deno.exit`)
|
||||||
|
- Logger runtime set to 'deno'
|
||||||
|
- Dynamic imports use `.ts` extensions
|
||||||
367
readme.md
367
readme.md
@@ -1,110 +1,307 @@
|
|||||||
# @shipzone/npmci
|
# @ship.zone/szci
|
||||||
node and docker in gitlab ci on steroids
|
|
||||||
|
|
||||||
## Availabililty and Links
|
**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.
|
||||||
* [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/)
|
|
||||||
|
|
||||||
## Status for master
|
## ✨ Features
|
||||||
|
|
||||||
Status Category | Status Badge
|
- 🚀 **Standalone Executables** - No Node.js installation required
|
||||||
-- | --
|
- 🐳 **Docker Integration** - Build, tag, and push Docker images
|
||||||
GitLab Pipelines | [](https://lossless.cloud)
|
- 📦 **NPM Management** - Install, test, and publish npm packages
|
||||||
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
- 🔧 **Node.js Version Management** - Install and switch between Node versions
|
||||||
npm | [](https://lossless.cloud)
|
- 🔐 **SSH Key Management** - Deploy SSH keys from environment variables
|
||||||
Snyk | [](https://lossless.cloud)
|
- ☁️ **Multi-Registry Support** - Push to multiple Docker registries
|
||||||
TypeScript Support | [](https://lossless.cloud)
|
- 🎯 **Cross-Platform** - Binaries for Linux, macOS, and Windows
|
||||||
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
|
||||||
Code Style | [](https://lossless.cloud)
|
|
||||||
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
|
||||||
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
|
||||||
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
|
||||||
|
|
||||||
## Usage
|
## 📥 Installation
|
||||||
|
|
||||||
Use TypeScript for best in class instellisense.
|
### NPM (Recommended)
|
||||||
|
|
||||||
npmci is designed to work in docker CI environments. The following docker images come with npmci presinstalled:
|
```sh
|
||||||
|
npm install -g @ship.zone/szci
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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
|
```json
|
||||||
{
|
{
|
||||||
"npmci": {
|
"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 |
|
SZCI supports automatic authentication with:
|
||||||
| -------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
||||||
| globalNpmTools | "globalNpmTools": ["gitbook"] | Will look for the specified package names locally and (if not yet present) install them from npm. |
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
## Contribution
|
```sh
|
||||||
|
NPMCI_LOGIN_DOCKER_1="registry.example.com|username|password"
|
||||||
|
NPMCI_LOGIN_DOCKER_2="another-registry.com|user2|pass2"
|
||||||
|
```
|
||||||
|
|
||||||
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
## 🏗️ Development
|
||||||
|
|
||||||
For further information read the linked docs at the top of this readme.
|
### Prerequisites
|
||||||
|
|
||||||
## Legal
|
- Deno 1.40+ installed
|
||||||
> MIT licensed | **©** [Task Venture Capital GmbH](https://task.vc)
|
|
||||||
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
### 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
66
scripts/compile-all.sh
Executable 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
228
scripts/install-binary.js
Normal 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);
|
||||||
|
});
|
||||||
2
test/assets/Dockerfile_hello_##version##
Normal file
2
test/assets/Dockerfile_hello_##version##
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FROM mygroup/myrepo:sometag2
|
||||||
|
RUN apt-get update
|
||||||
@@ -1 +1,9 @@
|
|||||||
{}
|
{
|
||||||
|
"gitzone": {
|
||||||
|
"module": {
|
||||||
|
"githost": "code.foss.global",
|
||||||
|
"gitscope": "mygroup",
|
||||||
|
"gitrepo": "myrepo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
// Disable TLS certificate validation for testing
|
||||||
import { tap, expect } from '@push.rocks/tapbundle';
|
Deno.env.set('NODE_TLS_REJECT_UNAUTHORIZED', '0');
|
||||||
|
Deno.env.set('SZCI_TEST', 'true');
|
||||||
|
Deno.env.set('CI_REPOSITORY_URL', 'https://yyyyyy:xxxxxxxx@gitlab.com/mygroup/myrepo.git');
|
||||||
|
|
||||||
import * as cloudlyConnectorMod from '../ts/connector.cloudly/cloudlyconnector.js';
|
import { CloudlyConnector } from '../ts/connector.cloudly/cloudlyconnector.ts';
|
||||||
|
import { Szci } from '../ts/szci.classes.szci.ts';
|
||||||
|
|
||||||
tap.test('should be able to announce a container to cloudly', async () => {
|
Deno.test({
|
||||||
const cloudlyConnector = new cloudlyConnectorMod.CloudlyConnector(null);
|
name: 'should be able to announce a container to cloudly',
|
||||||
|
sanitizeResources: false,
|
||||||
|
sanitizeOps: false,
|
||||||
|
fn: async () => {
|
||||||
|
// Create a proper Szci instance for the connector
|
||||||
|
const szciInstance = new Szci();
|
||||||
|
await szciInstance.start();
|
||||||
|
|
||||||
|
const cloudlyConnector = new CloudlyConnector(szciInstance);
|
||||||
await cloudlyConnector.announceDockerContainer(
|
await cloudlyConnector.announceDockerContainer(
|
||||||
{
|
{
|
||||||
registryUrl: 'registry.losssless.com',
|
registryUrl: 'registry.losssless.com',
|
||||||
@@ -14,13 +25,5 @@ tap.test('should be able to announce a container to cloudly', async () => {
|
|||||||
},
|
},
|
||||||
'cloudly.lossless.one'
|
'cloudly.lossless.one'
|
||||||
);
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should close the program despite socket timeout', async (toolsArg) => {
|
|
||||||
// TODO: remove when unreffed timeouts in webrequest have been solved.
|
|
||||||
toolsArg.delayFor(0).then(() => {
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.start();
|
|
||||||
|
|||||||
178
test/test.ts
178
test/test.ts
@@ -1,106 +1,131 @@
|
|||||||
import { tap, expect } from '@push.rocks/tapbundle';
|
import { assertEquals, assertExists } from '@std/assert';
|
||||||
import * as path from 'path';
|
import * as path from '@std/path';
|
||||||
import * as smartpath from '@push.rocks/smartpath';
|
import * as smartpath from '@push.rocks/smartpath';
|
||||||
|
|
||||||
process.env.NPMTS_TEST = 'true';
|
// Set up test environment with the NEW SZCI environment variables
|
||||||
process.env.NPMCI_URL_CLOUDLY = 'localhost';
|
Deno.env.set('SZCI_TEST', 'true');
|
||||||
|
Deno.env.set('SZCI_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('SZCI_LOGIN_DOCKER', 'docker.io|someuser|somepass');
|
||||||
|
Deno.env.set('SZCI_SSHKEY_1', 'hostString|somePrivKey|##');
|
||||||
|
|
||||||
// set up environment
|
// Get the test assets directory
|
||||||
process.env.CI_REPOSITORY_URL = 'https://yyyyyy:xxxxxxxx@gitlab.com/mygroup/myrepo.git';
|
const testAssetsDir = path.join(smartpath.get.dirnameFromImportMetaUrl(import.meta.url), 'assets/');
|
||||||
process.env.CI_BUILD_TOKEN = 'kjlkjfiudofiufs';
|
|
||||||
|
|
||||||
// Docker
|
// Save original cwd and change to test assets
|
||||||
process.env.NPMCI_LOGIN_DOCKER = 'docker.io|someuser|somepass';
|
const originalCwd = Deno.cwd();
|
||||||
|
Deno.chdir(testAssetsDir);
|
||||||
|
|
||||||
// SSH env
|
import type { Dockerfile } from '../ts/manager.docker/mod.classes.dockerfile.ts';
|
||||||
process.env.NPMCI_SSHKEY_1 = 'hostString|somePrivKey|##';
|
import { Szci } from '../ts/szci.classes.szci.ts';
|
||||||
|
import * as DockerfileModule from '../ts/manager.docker/mod.classes.dockerfile.ts';
|
||||||
process.cwd = () => {
|
|
||||||
return path.join(smartpath.get.dirnameFromImportMetaUrl(import.meta.url), 'assets/');
|
|
||||||
};
|
|
||||||
|
|
||||||
let npmci: typeof import('../ts/index.js');
|
|
||||||
|
|
||||||
type TNpmciTypes = typeof import('../ts/index.js');
|
|
||||||
|
|
||||||
tap.preTask('should import npmci', async () => {
|
|
||||||
npmci = await import('../ts/index.js');
|
|
||||||
});
|
|
||||||
|
|
||||||
// ======
|
// ======
|
||||||
// Docker
|
// Docker
|
||||||
// ======
|
// ======
|
||||||
|
|
||||||
let dockerfile1: npmci.Dockerfile;
|
let dockerfile1: Dockerfile;
|
||||||
let dockerfile2: npmci.Dockerfile;
|
let dockerfile2: Dockerfile;
|
||||||
let sortableArray: npmci.Dockerfile[];
|
let sortableArray: Dockerfile[];
|
||||||
|
|
||||||
tap.test('should return valid Dockerfiles', async () => {
|
Deno.test('should return valid Dockerfiles', async () => {
|
||||||
const npmciInstance = new npmci.Npmci();
|
const szciInstance = new Szci();
|
||||||
dockerfile1 = new npmci.Dockerfile(npmciInstance.dockerManager, {
|
await szciInstance.start();
|
||||||
|
dockerfile1 = new DockerfileModule.Dockerfile(szciInstance.dockerManager, {
|
||||||
filePath: './Dockerfile',
|
filePath: './Dockerfile',
|
||||||
read: true,
|
read: true,
|
||||||
});
|
});
|
||||||
dockerfile2 = new npmci.Dockerfile(npmciInstance.dockerManager, {
|
dockerfile2 = new DockerfileModule.Dockerfile(szciInstance.dockerManager, {
|
||||||
filePath: './Dockerfile_sometag1',
|
filePath: './Dockerfile_sometag1',
|
||||||
read: true,
|
read: true,
|
||||||
});
|
});
|
||||||
expect(dockerfile1.version).toEqual('latest');
|
assertEquals(dockerfile1.version, 'latest');
|
||||||
return expect(dockerfile2.version).toEqual('sometag1');
|
assertEquals(dockerfile2.version, 'sometag1');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should read a directory of Dockerfiles', async () => {
|
Deno.test('should read a directory of Dockerfiles', async () => {
|
||||||
const npmciInstance = new npmci.Npmci();
|
const szciInstance = new Szci();
|
||||||
return npmci.Dockerfile.readDockerfiles(npmciInstance.dockerManager).then(
|
await szciInstance.start();
|
||||||
async (readDockerfilesArrayArg: npmci.Dockerfile[]) => {
|
const readDockerfilesArray = await DockerfileModule.Dockerfile.readDockerfiles(
|
||||||
sortableArray = readDockerfilesArrayArg;
|
szciInstance.dockerManager
|
||||||
return expect(readDockerfilesArrayArg[1].version).toEqual('sometag1');
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
sortableArray = readDockerfilesArray;
|
||||||
|
|
||||||
|
// The test assets directory should have multiple Dockerfiles
|
||||||
|
assertExists(readDockerfilesArray, 'readDockerfilesArray should exist');
|
||||||
|
assertEquals(readDockerfilesArray.length > 0, true, 'Should find at least one Dockerfile');
|
||||||
|
|
||||||
|
// Find the sometag1 dockerfile
|
||||||
|
const sometag1Dockerfile = readDockerfilesArray.find(df => df.version === 'sometag1');
|
||||||
|
assertExists(sometag1Dockerfile, 'Should find Dockerfile_sometag1');
|
||||||
|
assertEquals(sometag1Dockerfile?.version, 'sometag1');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should sort an array of Dockerfiles', async () => {
|
Deno.test('should sort an array of Dockerfiles', async () => {
|
||||||
return npmci.Dockerfile.sortDockerfiles(sortableArray).then(
|
// Use the sortableArray from previous test, or create a new one if empty
|
||||||
async (sortedArrayArg: npmci.Dockerfile[]) => {
|
if (!sortableArray || sortableArray.length === 0) {
|
||||||
console.log(sortedArrayArg);
|
const szciInstance = new Szci();
|
||||||
|
await szciInstance.start();
|
||||||
|
sortableArray = await DockerfileModule.Dockerfile.readDockerfiles(szciInstance.dockerManager);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const sortedArray = await DockerfileModule.Dockerfile.sortDockerfiles(sortableArray);
|
||||||
|
assertExists(sortedArray, 'sortedArray should exist');
|
||||||
|
console.log('Sorted dockerfiles:', sortedArray.map(df => df.cleanTag));
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should build all Dockerfiles', async () => {
|
Deno.test({
|
||||||
const npmciInstance = new npmci.Npmci();
|
name: 'should build all Dockerfiles',
|
||||||
return npmciInstance.dockerManager.handleCli({
|
// Allow resource leaks since smartshell creates background processes
|
||||||
|
sanitizeResources: false,
|
||||||
|
sanitizeOps: false,
|
||||||
|
fn: async () => {
|
||||||
|
const szciInstance = new Szci();
|
||||||
|
await szciInstance.start();
|
||||||
|
await szciInstance.dockerManager.handleCli({
|
||||||
_: ['docker', 'build'],
|
_: ['docker', 'build'],
|
||||||
});
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should test all Dockerfiles', async () => {
|
Deno.test({
|
||||||
const npmciInstance = new npmci.Npmci();
|
name: 'should test all Dockerfiles',
|
||||||
return npmciInstance.dockerManager.handleCli({
|
// Allow resource leaks since smartshell creates background processes
|
||||||
|
sanitizeResources: false,
|
||||||
|
sanitizeOps: false,
|
||||||
|
fn: async () => {
|
||||||
|
const szciInstance = new Szci();
|
||||||
|
await szciInstance.start();
|
||||||
|
await szciInstance.dockerManager.handleCli({
|
||||||
_: ['docker', 'test'],
|
_: ['docker', 'test'],
|
||||||
});
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should test dockerfiles', async () => {
|
Deno.test({
|
||||||
const npmciInstance = new npmci.Npmci();
|
name: 'should login docker daemon',
|
||||||
return npmciInstance.dockerManager.handleCli({
|
// Allow resource leaks since smartshell creates background processes
|
||||||
_: ['docker', 'test'],
|
sanitizeResources: false,
|
||||||
});
|
sanitizeOps: false,
|
||||||
});
|
fn: async () => {
|
||||||
|
const szciInstance = new Szci();
|
||||||
tap.test('should login docker daemon', async () => {
|
await szciInstance.start();
|
||||||
const npmciInstance = new npmci.Npmci();
|
await szciInstance.dockerManager.handleCli({
|
||||||
return npmciInstance.dockerManager.handleCli({
|
|
||||||
_: ['docker', 'login'],
|
_: ['docker', 'login'],
|
||||||
});
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===
|
// ===
|
||||||
// SSH
|
// SSH
|
||||||
// ===
|
// ===
|
||||||
tap.test('should prepare SSH keys', async () => {
|
Deno.test('should prepare SSH keys', async () => {
|
||||||
const npmciModSsh = await import('../ts/mod_ssh/index.js');
|
// Ensure test mode is set so we don't actually write to disk
|
||||||
return await npmciModSsh.handleCli({
|
Deno.env.set('SZCI_TEST', 'true');
|
||||||
|
|
||||||
|
const szciModSsh = await import('../ts/mod_ssh/index.ts');
|
||||||
|
await szciModSsh.handleCli({
|
||||||
_: ['ssh', 'prepare'],
|
_: ['ssh', 'prepare'],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -108,24 +133,27 @@ tap.test('should prepare SSH keys', async () => {
|
|||||||
// ====
|
// ====
|
||||||
// node
|
// node
|
||||||
// ====
|
// ====
|
||||||
tap.test('should install a certain version of node', async () => {
|
Deno.test({
|
||||||
const npmciInstance = new npmci.Npmci();
|
name: 'should install a certain version of node',
|
||||||
await npmciInstance.nodejsManager.handleCli({
|
// Allow resource leaks for this test since nvm creates background processes
|
||||||
|
sanitizeResources: false,
|
||||||
|
sanitizeOps: false,
|
||||||
|
fn: async () => {
|
||||||
|
const szciInstance = new Szci();
|
||||||
|
await szciInstance.start();
|
||||||
|
await szciInstance.nodejsManager.handleCli({
|
||||||
_: ['node', 'install', 'stable'],
|
_: ['node', 'install', 'stable'],
|
||||||
});
|
});
|
||||||
await npmciInstance.nodejsManager.handleCli({
|
await szciInstance.nodejsManager.handleCli({
|
||||||
_: ['node', 'install', 'lts'],
|
_: ['node', 'install', 'lts'],
|
||||||
});
|
});
|
||||||
await npmciInstance.nodejsManager.handleCli({
|
await szciInstance.nodejsManager.handleCli({
|
||||||
_: ['node', 'install', 'legacy'],
|
_: ['node', 'install', 'legacy'],
|
||||||
});
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// make sure test ends all right
|
// Restore original working directory after all tests
|
||||||
tap.test('reset paths', async () => {
|
Deno.test('reset paths', () => {
|
||||||
process.cwd = () => {
|
Deno.chdir(originalCwd);
|
||||||
return path.join(__dirname, '../');
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.start();
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* autocreated commitinfo by @pushrocks/commitinfo
|
* autocreated commitinfo by @push.rocks/commitinfo
|
||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@shipzone/npmci',
|
name: '@ship.zone/szci',
|
||||||
version: '4.1.21',
|
version: '6.0.1',
|
||||||
description: 'node and docker in gitlab ci on steroids'
|
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.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import * as plugins from '../npmci.plugins.js';
|
import * as plugins from '../szci.plugins.ts';
|
||||||
|
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* connects to cloudly
|
* connects to cloudly
|
||||||
*/
|
*/
|
||||||
export class CloudlyConnector {
|
export class CloudlyConnector {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
|
|
||||||
constructor(npmciRefArg: Npmci) {
|
constructor(szciRefArg: Szci) {
|
||||||
this.npmciRef = npmciRefArg;
|
this.szciRef = szciRefArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async announceDockerContainer(
|
public async announceDockerContainer(
|
||||||
optionsArg: plugins.tsclass.container.IContainer,
|
optionsArg: plugins.tsclass.container.IContainer,
|
||||||
testCloudlyUrlArg?: string
|
testCloudlyUrlArg?: string
|
||||||
) {
|
) {
|
||||||
const cloudlyUrl = testCloudlyUrlArg || this.npmciRef.npmciConfig.getConfig().urlCloudly;
|
const cloudlyUrl = testCloudlyUrlArg || this.szciRef.szciConfig.getConfig().urlCloudly;
|
||||||
if (!cloudlyUrl) {
|
if (!cloudlyUrl) {
|
||||||
logger.log(
|
logger.log(
|
||||||
'warn',
|
'warn',
|
||||||
@@ -26,14 +26,6 @@ export class CloudlyConnector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const typedrequest =
|
// lets push to cloudly here
|
||||||
new plugins.typedrequest.TypedRequest<plugins.servezoneInterfaces.requests.IRequest_InformAboutNewContainerImage>(
|
|
||||||
`https://${cloudlyUrl}/typedrequest`,
|
|
||||||
'servezonestandard_InformAboutNewContainerVersion'
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await typedrequest.fire({
|
|
||||||
containerImageInfo: optionsArg,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
ts/index.ts
10
ts/index.ts
@@ -1,10 +1,10 @@
|
|||||||
import { Npmci } from './npmci.classes.npmci.js';
|
import { Szci } from './szci.classes.szci.ts';
|
||||||
import { Dockerfile } from './manager.docker/mod.classes.dockerfile.js';
|
import { Dockerfile } from './manager.docker/mod.classes.dockerfile.ts';
|
||||||
|
|
||||||
export const npmciInstance = new Npmci();
|
export const szciInstance = new Szci();
|
||||||
|
|
||||||
export { Dockerfile, Npmci };
|
export { Dockerfile, Szci };
|
||||||
|
|
||||||
export const runCli = async () => {
|
export const runCli = async () => {
|
||||||
npmciInstance.start();
|
await szciInstance.start();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
import { bash } from '../npmci.bash.js';
|
import { bash } from '../szci.bash.ts';
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
|
|
||||||
export class NpmciCloudronManager {
|
export class SzciCloudronManager {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
|
|
||||||
constructor(npmciArg: Npmci) {
|
constructor(szciArg: Szci) {
|
||||||
this.npmciRef = npmciArg;
|
this.szciRef = szciArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,12 +23,12 @@ export class NpmciCloudronManager {
|
|||||||
await this.deploy();
|
await this.deploy();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log('error', `>>npmci cloudron ...<< action >>${action}<< not supported`);
|
logger.log('error', `>>szci cloudron ...<< action >>${action}<< not supported`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`>>npmci cloudron ...<< cli arguments invalid... Please read the documentation.`
|
`>>szci cloudron ...<< cli arguments invalid... Please read the documentation.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -44,14 +44,18 @@ export class NpmciCloudronManager {
|
|||||||
logger.log('ok', 'cloudron cli installed');
|
logger.log('ok', 'cloudron cli installed');
|
||||||
|
|
||||||
// lets set the version in the CloudronManifest file
|
// lets set the version in the CloudronManifest file
|
||||||
await this.prepareCloudronManifest(this.npmciRef.npmciConfig.getConfig().projectInfo.npm.version);
|
await this.prepareCloudronManifest(this.szciRef.szciConfig.getConfig().projectInfo.npm.version);
|
||||||
logger.log('ok', 'CloudronManifest prepared');
|
logger.log('ok', 'CloudronManifest prepared');
|
||||||
|
|
||||||
// lets figure out the docker image tag
|
// lets figure out the docker image tag
|
||||||
const dockerImageTag = await this.npmciRef.npmciConfig.kvStorage.readKey('latestPushedDockerTag');
|
const dockerImageTag = await this.szciRef.szciConfig.kvStorage.readKey('latestPushedDockerTag');
|
||||||
const appName = this.npmciRef.npmciConfig.getConfig().cloudronAppName;
|
const appName = this.szciRef.szciConfig.getConfig().cloudronAppName;
|
||||||
|
|
||||||
const cloudronEnvVar = process.env.NPMCI_LOGIN_CLOUDRON;
|
const cloudronEnvVar = Deno.env.get("SZCI_LOGIN_CLOUDRON");
|
||||||
|
if (!cloudronEnvVar) {
|
||||||
|
logger.log('error', 'SZCI_LOGIN_CLOUDRON environment variable is not set');
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
const cloudronServer = cloudronEnvVar.split('|')[0];
|
const cloudronServer = cloudronEnvVar.split('|')[0];
|
||||||
const cloudronToken = cloudronEnvVar.split('|')[1];
|
const cloudronToken = cloudronEnvVar.split('|')[1];
|
||||||
await bash(`cloudron update --server ${cloudronServer} --token ${cloudronToken} --image ${dockerImageTag} --app ${appName}`);
|
await bash(`cloudron update --server ${cloudronServer} --token ${cloudronToken} --image ${dockerImageTag} --app ${appName}`);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
import { bash } from '../npmci.bash.js';
|
import { bash } from '../szci.bash.ts';
|
||||||
|
|
||||||
// classes
|
// classes
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
import { Dockerfile } from './mod.classes.dockerfile.js';
|
import { Dockerfile } from './mod.classes.dockerfile.ts';
|
||||||
import { DockerRegistry } from './mod.classes.dockerregistry.js';
|
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
|
||||||
import { RegistryStorage } from './mod.classes.registrystorage.js';
|
import { RegistryStorage } from './mod.classes.registrystorage.ts';
|
||||||
|
|
||||||
export class NpmciDockerManager {
|
export class SzciDockerManager {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
public npmciRegistryStorage = new RegistryStorage();
|
public szciRegistryStorage = new RegistryStorage();
|
||||||
|
|
||||||
constructor(npmciArg: Npmci) {
|
constructor(szciArg: Szci) {
|
||||||
this.npmciRef = npmciArg;
|
this.szciRef = szciArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,12 +42,12 @@ export class NpmciDockerManager {
|
|||||||
await this.pull(argvArg);
|
await this.pull(argvArg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log('error', `>>npmci docker ...<< action >>${action}<< not supported`);
|
logger.log('error', `>>szci docker ...<< action >>${action}<< not supported`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`>>npmci docker ...<< cli arguments invalid... Please read the documentation.`
|
`>>szci docker ...<< cli arguments invalid... Please read the documentation.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -69,7 +69,7 @@ export class NpmciDockerManager {
|
|||||||
*/
|
*/
|
||||||
public login = async () => {
|
public login = async () => {
|
||||||
await this.prepare();
|
await this.prepare();
|
||||||
await this.npmciRegistryStorage.loginAll();
|
await this.szciRegistryStorage.loginAll();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,27 +77,27 @@ export class NpmciDockerManager {
|
|||||||
*/
|
*/
|
||||||
public prepare = async () => {
|
public prepare = async () => {
|
||||||
// Always login to GitLab Registry
|
// Always login to GitLab Registry
|
||||||
if (process.env.GITLAB_CI) {
|
if (Deno.env.get("GITLAB_CI")) {
|
||||||
console.log('gitlab ci detected');
|
console.log('gitlab ci detected');
|
||||||
if (!process.env.CI_JOB_TOKEN || process.env.CI_JOB_TOKEN === '') {
|
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!');
|
logger.log('error', 'Running in Gitlab CI, but no registry token specified by gitlab!');
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
this.npmciRegistryStorage.addRegistry(
|
this.szciRegistryStorage.addRegistry(
|
||||||
new DockerRegistry({
|
new DockerRegistry({
|
||||||
registryUrl: 'registry.gitlab.com',
|
registryUrl: 'registry.gitlab.com',
|
||||||
username: 'gitlab-ci-token',
|
username: 'gitlab-ci-token',
|
||||||
password: process.env.CI_JOB_TOKEN,
|
password: Deno.env.get("CI_JOB_TOKEN")!,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle registries
|
// handle registries
|
||||||
await plugins.smartobject.forEachMinimatch(
|
await plugins.smartobject.forEachMinimatch(
|
||||||
process.env,
|
Deno.env.toObject(),
|
||||||
'NPMCI_LOGIN_DOCKER*',
|
'SZCI_LOGIN_DOCKER*',
|
||||||
async (envString: string) => {
|
async (envString: string) => {
|
||||||
this.npmciRegistryStorage.addRegistry(DockerRegistry.fromEnvString(envString));
|
this.szciRegistryStorage.addRegistry(DockerRegistry.fromEnvString(envString));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -115,14 +115,14 @@ export class NpmciDockerManager {
|
|||||||
if (argvArg._.length >= 3 && argvArg._[2] !== 'npmextra') {
|
if (argvArg._.length >= 3 && argvArg._[2] !== 'npmextra') {
|
||||||
dockerRegistryUrls.push(argvArg._[2]);
|
dockerRegistryUrls.push(argvArg._[2]);
|
||||||
} else {
|
} else {
|
||||||
if (this.npmciRef.npmciConfig.getConfig().dockerRegistries.length === 0) {
|
if (this.szciRef.szciConfig.getConfig().dockerRegistries.length === 0) {
|
||||||
logger.log(
|
logger.log(
|
||||||
'warn',
|
'warn',
|
||||||
`There are no docker registries listed in npmextra.json! This is strange!`
|
`There are no docker registries listed in npmextra.json! This is strange!`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
dockerRegistryUrls = dockerRegistryUrls.concat(
|
dockerRegistryUrls = dockerRegistryUrls.concat(
|
||||||
this.npmciRef.npmciConfig.getConfig().dockerRegistries
|
this.szciRef.szciConfig.getConfig().dockerRegistries
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ export class NpmciDockerManager {
|
|||||||
const dockerfileArray = await Dockerfile.readDockerfiles(this)
|
const dockerfileArray = await Dockerfile.readDockerfiles(this)
|
||||||
.then(Dockerfile.sortDockerfiles)
|
.then(Dockerfile.sortDockerfiles)
|
||||||
.then(Dockerfile.mapDockerfiles);
|
.then(Dockerfile.mapDockerfiles);
|
||||||
const dockerRegistryToPushTo = await this.npmciRegistryStorage.getRegistryByUrl(
|
const dockerRegistryToPushTo = await this.szciRegistryStorage.getRegistryByUrl(
|
||||||
dockerRegistryUrl
|
dockerRegistryUrl
|
||||||
);
|
);
|
||||||
if (!dockerRegistryToPushTo) {
|
if (!dockerRegistryToPushTo) {
|
||||||
@@ -145,7 +145,7 @@ export class NpmciDockerManager {
|
|||||||
'error',
|
'error',
|
||||||
`Cannot push to registry ${dockerRegistryUrl}, because it was not found in the authenticated registry list.`
|
`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) {
|
for (const dockerfile of dockerfileArray) {
|
||||||
await dockerfile.push(dockerRegistryToPushTo, suffix);
|
await dockerfile.push(dockerRegistryToPushTo, suffix);
|
||||||
@@ -163,7 +163,7 @@ export class NpmciDockerManager {
|
|||||||
if (argvArg._.length >= 4) {
|
if (argvArg._.length >= 4) {
|
||||||
suffix = argvArg._[3];
|
suffix = argvArg._[3];
|
||||||
}
|
}
|
||||||
const localDockerRegistry = await this.npmciRegistryStorage.getRegistryByUrl(registryUrlArg);
|
const localDockerRegistry = await this.szciRegistryStorage.getRegistryByUrl(registryUrlArg);
|
||||||
const dockerfileArray = await Dockerfile.readDockerfiles(this)
|
const dockerfileArray = await Dockerfile.readDockerfiles(this)
|
||||||
.then(Dockerfile.sortDockerfiles)
|
.then(Dockerfile.sortDockerfiles)
|
||||||
.then(Dockerfile.mapDockerfiles);
|
.then(Dockerfile.mapDockerfiles);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
|
|
||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import { bash } from '../npmci.bash.js';
|
import { bash } from '../szci.bash.ts';
|
||||||
|
|
||||||
import { DockerRegistry } from './mod.classes.dockerregistry.js';
|
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
|
||||||
import * as helpers from './mod.helpers.js';
|
import * as helpers from './mod.helpers.ts';
|
||||||
import { NpmciDockerManager } from './index.js';
|
import { SzciDockerManager } from './index.ts';
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class Dockerfile represents a Dockerfile on disk in npmci
|
* class Dockerfile represents a Dockerfile on disk in szci
|
||||||
*/
|
*/
|
||||||
export class Dockerfile {
|
export class Dockerfile {
|
||||||
// STATIC
|
// STATIC
|
||||||
@@ -20,16 +20,16 @@ export class Dockerfile {
|
|||||||
* @returns Promise<Dockerfile[]>
|
* @returns Promise<Dockerfile[]>
|
||||||
*/
|
*/
|
||||||
public static async readDockerfiles(
|
public static async readDockerfiles(
|
||||||
npmciDockerManagerRefArg: NpmciDockerManager
|
szciDockerManagerRefArg: SzciDockerManager
|
||||||
): Promise<Dockerfile[]> {
|
): Promise<Dockerfile[]> {
|
||||||
const fileTree = await plugins.smartfile.fs.listFileTree(paths.cwd, 'Dockerfile*');
|
const fileTree = await plugins.smartfile.fs.listFileTree(paths.getCwd(), 'Dockerfile*');
|
||||||
|
|
||||||
// create the Dockerfile array
|
// create the Dockerfile array
|
||||||
const readDockerfilesArray: Dockerfile[] = [];
|
const readDockerfilesArray: Dockerfile[] = [];
|
||||||
logger.log('info', `found ${fileTree.length} Dockerfiles:`);
|
logger.log('info', `found ${fileTree.length} Dockerfiles:`);
|
||||||
console.log(fileTree);
|
console.log(fileTree);
|
||||||
for (const dockerfilePath of fileTree) {
|
for (const dockerfilePath of fileTree) {
|
||||||
const myDockerfile = new Dockerfile(npmciDockerManagerRefArg, {
|
const myDockerfile = new Dockerfile(szciDockerManagerRefArg, {
|
||||||
filePath: dockerfilePath,
|
filePath: dockerfilePath,
|
||||||
read: true,
|
read: true,
|
||||||
});
|
});
|
||||||
@@ -40,43 +40,76 @@ export class Dockerfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sorts Dockerfiles into a dependency chain
|
* Sorts Dockerfiles into a build order based on dependencies.
|
||||||
* @param sortableArrayArg an array of instances of class Dockerfile
|
* @param dockerfiles An array of Dockerfile instances.
|
||||||
* @returns Promise<Dockerfile[]>
|
* @returns A Promise that resolves to a sorted array of Dockerfiles.
|
||||||
*/
|
*/
|
||||||
public static async sortDockerfiles(sortableArrayArg: Dockerfile[]): Promise<Dockerfile[]> {
|
public static async sortDockerfiles(dockerfiles: Dockerfile[]): Promise<Dockerfile[]> {
|
||||||
const done = plugins.smartpromise.defer<Dockerfile[]>();
|
logger.log('info', 'Sorting Dockerfiles based on dependencies...');
|
||||||
logger.log('info', 'sorting Dockerfiles:');
|
|
||||||
const sortedArray: Dockerfile[] = [];
|
// Map from cleanTag to Dockerfile instance for quick lookup
|
||||||
const cleanTagsOriginal = Dockerfile.cleanTagsArrayFunction(sortableArrayArg, sortedArray);
|
const tagToDockerfile = new Map<string, Dockerfile>();
|
||||||
let sorterFunctionCounter: number = 0;
|
dockerfiles.forEach((dockerfile) => {
|
||||||
const sorterFunction = () => {
|
tagToDockerfile.set(dockerfile.cleanTag, dockerfile);
|
||||||
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;
|
// Build the dependency graph
|
||||||
for (const dockerfile of sortedArray) {
|
const graph = new Map<Dockerfile, Dockerfile[]>();
|
||||||
logger.log('info', `tag ${counter}: -> ${dockerfile.cleanTag}`);
|
dockerfiles.forEach((dockerfile) => {
|
||||||
counter++;
|
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;
|
||||||
}
|
}
|
||||||
done.resolve(sortedArray);
|
|
||||||
} else if (sorterFunctionCounter < 10) {
|
graph.set(dockerfile, dependencies);
|
||||||
sorterFunctionCounter++;
|
});
|
||||||
sorterFunction();
|
|
||||||
|
// 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 as Error).message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the sorted order
|
||||||
|
sortedDockerfiles.forEach((dockerfile, index) => {
|
||||||
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Build order ${index + 1}: ${dockerfile.cleanTag}
|
||||||
|
with base image ${dockerfile.baseImage}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortedDockerfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,9 +153,12 @@ export class Dockerfile {
|
|||||||
* returns a version for a docker file
|
* returns a version for a docker file
|
||||||
* @execution SYNC
|
* @execution SYNC
|
||||||
*/
|
*/
|
||||||
public static dockerFileVersion(dockerfileInstanceArg: Dockerfile, dockerfileNameArg: string): string {
|
public static dockerFileVersion(
|
||||||
|
dockerfileInstanceArg: Dockerfile,
|
||||||
|
dockerfileNameArg: string
|
||||||
|
): string {
|
||||||
let versionString: string;
|
let versionString: string;
|
||||||
const versionRegex = /Dockerfile_([^:_]*)$/;
|
const versionRegex = /Dockerfile_(.+)$/;
|
||||||
const regexResultArray = versionRegex.exec(dockerfileNameArg);
|
const regexResultArray = versionRegex.exec(dockerfileNameArg);
|
||||||
if (regexResultArray && regexResultArray.length === 2) {
|
if (regexResultArray && regexResultArray.length === 2) {
|
||||||
versionString = regexResultArray[1];
|
versionString = regexResultArray[1];
|
||||||
@@ -131,25 +167,76 @@ export class Dockerfile {
|
|||||||
}
|
}
|
||||||
versionString = versionString.replace(
|
versionString = versionString.replace(
|
||||||
'##version##',
|
'##version##',
|
||||||
dockerfileInstanceArg.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version
|
dockerfileInstanceArg.szciDockerManagerRef.szciRef.szciConfig.getConfig().projectInfo.npm
|
||||||
|
.version
|
||||||
);
|
);
|
||||||
return versionString;
|
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 {
|
public static dockerBaseImage(dockerfileContentArg: string): string {
|
||||||
const baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/;
|
const lines = dockerfileContentArg.split(/\r?\n/);
|
||||||
const regexResultArray = baseImageRegex.exec(dockerfileContentArg);
|
const args: { [key: string]: string } = {};
|
||||||
return regexResultArray[1];
|
|
||||||
|
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
|
* returns the docker tag
|
||||||
*/
|
*/
|
||||||
public static getDockerTagString(
|
public static getDockerTagString(
|
||||||
npmciDockerManagerRef: NpmciDockerManager,
|
szciDockerManagerRef: SzciDockerManager,
|
||||||
registryArg: string,
|
registryArg: string,
|
||||||
repoArg: string,
|
repoArg: string,
|
||||||
versionArg: string,
|
versionArg: string,
|
||||||
@@ -157,7 +244,7 @@ export class Dockerfile {
|
|||||||
): string {
|
): string {
|
||||||
// determine wether the repo should be mapped accordingly to the registry
|
// determine wether the repo should be mapped accordingly to the registry
|
||||||
const mappedRepo =
|
const mappedRepo =
|
||||||
npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerRegistryRepoMap[registryArg];
|
szciDockerManagerRef.szciRef.szciConfig.getConfig().dockerRegistryRepoMap[registryArg];
|
||||||
const repo = (() => {
|
const repo = (() => {
|
||||||
if (mappedRepo) {
|
if (mappedRepo) {
|
||||||
return mappedRepo;
|
return mappedRepo;
|
||||||
@@ -177,67 +264,51 @@ export class Dockerfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async getDockerBuildArgs(
|
public static async getDockerBuildArgs(
|
||||||
npmciDockerManagerRef: NpmciDockerManager
|
szciDockerManagerRef: SzciDockerManager
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
logger.log('info', 'checking for env vars to be supplied to the docker build');
|
logger.log('info', 'checking for env vars to be supplied to the docker build');
|
||||||
let buildArgsString: string = '';
|
let buildArgsString: string = '';
|
||||||
for (const dockerArgKey of Object.keys(
|
for (const dockerArgKey of Object.keys(
|
||||||
npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerBuildargEnvMap
|
szciDockerManagerRef.szciRef.szciConfig.getConfig().dockerBuildargEnvMap
|
||||||
)) {
|
)) {
|
||||||
const dockerArgOuterEnvVar =
|
const dockerArgOuterEnvVar =
|
||||||
npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerBuildargEnvMap[dockerArgKey];
|
szciDockerManagerRef.szciRef.szciConfig.getConfig().dockerBuildargEnvMap[dockerArgKey];
|
||||||
logger.log(
|
logger.log(
|
||||||
'note',
|
'note',
|
||||||
`docker ARG "${dockerArgKey}" maps to outer env var "${dockerArgOuterEnvVar}"`
|
`docker ARG "${dockerArgKey}" maps to outer env var "${dockerArgOuterEnvVar}"`
|
||||||
);
|
);
|
||||||
const targetValue = process.env[dockerArgOuterEnvVar];
|
const targetValue = Deno.env.get(dockerArgOuterEnvVar);
|
||||||
buildArgsString = `${buildArgsString} --build-arg ${dockerArgKey}="${targetValue}"`;
|
buildArgsString = `${buildArgsString} --build-arg ${dockerArgKey}="${targetValue}"`;
|
||||||
}
|
}
|
||||||
return buildArgsString;
|
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
|
// INSTANCE
|
||||||
public npmciDockerManagerRef: NpmciDockerManager;
|
public szciDockerManagerRef: SzciDockerManager;
|
||||||
|
|
||||||
public filePath: string;
|
public filePath!: string;
|
||||||
public repo: string;
|
public repo: string;
|
||||||
public version: string;
|
public version: string;
|
||||||
public cleanTag: string;
|
public cleanTag: string;
|
||||||
public buildTag: string;
|
public buildTag: string;
|
||||||
public pushTag: string;
|
public pushTag!: string;
|
||||||
public containerName: string;
|
public containerName: string;
|
||||||
public content: string;
|
public content!: string;
|
||||||
public baseImage: string;
|
public baseImage: string;
|
||||||
public localBaseImageDependent: boolean;
|
public localBaseImageDependent: boolean;
|
||||||
public localBaseDockerfile: Dockerfile;
|
public localBaseDockerfile!: Dockerfile;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
dockerManagerRefArg: NpmciDockerManager,
|
dockerManagerRefArg: SzciDockerManager,
|
||||||
options: { filePath?: string; fileContents?: string | Buffer; read?: boolean }
|
options: { filePath?: string; fileContents?: string | Uint8Array; read?: boolean }
|
||||||
) {
|
) {
|
||||||
this.npmciDockerManagerRef = dockerManagerRefArg;
|
this.szciDockerManagerRef = dockerManagerRefArg;
|
||||||
this.filePath = options.filePath;
|
this.filePath = options.filePath!;
|
||||||
this.repo =
|
this.repo =
|
||||||
this.npmciDockerManagerRef.npmciRef.npmciEnv.repo.user +
|
this.szciDockerManagerRef.szciRef.szciEnv.repo.user +
|
||||||
'/' +
|
'/' +
|
||||||
this.npmciDockerManagerRef.npmciRef.npmciEnv.repo.repo;
|
this.szciDockerManagerRef.szciRef.szciEnv.repo.repo;
|
||||||
this.version = Dockerfile.dockerFileVersion(this, plugins.path.parse(options.filePath).base);
|
this.version = Dockerfile.dockerFileVersion(this, plugins.path.parse(this.filePath).base);
|
||||||
this.cleanTag = this.repo + ':' + this.version;
|
this.cleanTag = this.repo + ':' + this.version;
|
||||||
this.buildTag = this.cleanTag;
|
this.buildTag = this.cleanTag;
|
||||||
|
|
||||||
@@ -254,9 +325,9 @@ export class Dockerfile {
|
|||||||
*/
|
*/
|
||||||
public async build() {
|
public async build() {
|
||||||
logger.log('info', 'now building Dockerfile for ' + this.cleanTag);
|
logger.log('info', 'now building Dockerfile for ' + this.cleanTag);
|
||||||
const buildArgsString = await Dockerfile.getDockerBuildArgs(this.npmciDockerManagerRef);
|
const buildArgsString = await Dockerfile.getDockerBuildArgs(this.szciDockerManagerRef);
|
||||||
const buildCommand = `docker build --label="version=${
|
const buildCommand = `docker build --label="version=${
|
||||||
this.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version
|
this.szciDockerManagerRef.szciRef.szciConfig.getConfig().projectInfo.npm.version
|
||||||
}" -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
|
}" -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
|
||||||
await bash(buildCommand);
|
await bash(buildCommand);
|
||||||
return;
|
return;
|
||||||
@@ -265,9 +336,9 @@ export class Dockerfile {
|
|||||||
/**
|
/**
|
||||||
* pushes the Dockerfile to a registry
|
* pushes the Dockerfile to a registry
|
||||||
*/
|
*/
|
||||||
public async push(dockerRegistryArg: DockerRegistry, versionSuffix: string = null) {
|
public async push(dockerRegistryArg: DockerRegistry, versionSuffix?: string) {
|
||||||
this.pushTag = Dockerfile.getDockerTagString(
|
this.pushTag = Dockerfile.getDockerTagString(
|
||||||
this.npmciDockerManagerRef,
|
this.szciDockerManagerRef,
|
||||||
dockerRegistryArg.registryUrl,
|
dockerRegistryArg.registryUrl,
|
||||||
this.repo,
|
this.repo,
|
||||||
this.version,
|
this.version,
|
||||||
@@ -279,21 +350,24 @@ export class Dockerfile {
|
|||||||
await bash(`docker inspect --format="{{index .RepoDigests 0}}" ${this.pushTag}`)
|
await bash(`docker inspect --format="{{index .RepoDigests 0}}" ${this.pushTag}`)
|
||||||
).split('@')[1];
|
).split('@')[1];
|
||||||
console.log(`The image ${this.pushTag} has digest ${imageDigest}`);
|
console.log(`The image ${this.pushTag} has digest ${imageDigest}`);
|
||||||
await this.npmciDockerManagerRef.npmciRef.cloudlyConnector.announceDockerContainer({
|
await this.szciDockerManagerRef.szciRef.cloudlyConnector.announceDockerContainer({
|
||||||
registryUrl: this.pushTag,
|
registryUrl: this.pushTag,
|
||||||
tag: this.buildTag,
|
tag: this.buildTag,
|
||||||
labels: [],
|
labels: [],
|
||||||
version: this.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version,
|
version: this.szciDockerManagerRef.szciRef.szciConfig.getConfig().projectInfo.npm.version,
|
||||||
});
|
});
|
||||||
await this.npmciDockerManagerRef.npmciRef.npmciConfig.kvStorage.writeKey('latestPushedDockerTag', this.pushTag)
|
await this.szciDockerManagerRef.szciRef.szciConfig.kvStorage.writeKey(
|
||||||
|
'latestPushedDockerTag',
|
||||||
|
this.pushTag
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pulls the Dockerfile from a registry
|
* pulls the Dockerfile from a registry
|
||||||
*/
|
*/
|
||||||
public async pull(registryArg: DockerRegistry, versionSuffixArg: string = null) {
|
public async pull(registryArg: DockerRegistry, versionSuffixArg?: string) {
|
||||||
const pullTag = Dockerfile.getDockerTagString(
|
const pullTag = Dockerfile.getDockerTagString(
|
||||||
this.npmciDockerManagerRef,
|
this.szciDockerManagerRef,
|
||||||
registryArg.registryUrl,
|
registryArg.registryUrl,
|
||||||
this.repo,
|
this.repo,
|
||||||
this.version,
|
this.version,
|
||||||
@@ -307,18 +381,18 @@ export class Dockerfile {
|
|||||||
* tests the Dockerfile;
|
* tests the Dockerfile;
|
||||||
*/
|
*/
|
||||||
public async test() {
|
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);
|
const testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile);
|
||||||
if (testFileExists) {
|
if (testFileExists) {
|
||||||
// run tests
|
// run tests
|
||||||
await bash(
|
await bash(
|
||||||
`docker run --name npmci_test_container --entrypoint="bash" ${this.buildTag} -c "mkdir /npmci_test"`
|
`docker run --name szci_test_container --entrypoint="bash" ${this.buildTag} -c "mkdir /szci_test"`
|
||||||
);
|
);
|
||||||
await bash(`docker cp ${testFile} npmci_test_container:/npmci_test/test.sh`);
|
await bash(`docker cp ${testFile} szci_test_container:/szci_test/test.sh`);
|
||||||
await bash(`docker commit npmci_test_container npmci_test_image`);
|
await bash(`docker commit szci_test_container szci_test_image`);
|
||||||
await bash(`docker run --entrypoint="bash" npmci_test_image -x /npmci_test/test.sh`);
|
await bash(`docker run --entrypoint="bash" szci_test_image -x /szci_test/test.sh`);
|
||||||
await bash(`docker rm npmci_test_container`);
|
await bash(`docker rm szci_test_container`);
|
||||||
await bash(`docker rmi --force npmci_test_image`);
|
await bash(`docker rmi --force szci_test_image`);
|
||||||
} else {
|
} else {
|
||||||
logger.log('warn', 'skipping tests for ' + this.cleanTag + ' because no testfile was found!');
|
logger.log('warn', 'skipping tests for ' + this.cleanTag + ' because no testfile was found!');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import { bash } from '../npmci.bash.js';
|
import { bash } from '../szci.bash.ts';
|
||||||
|
|
||||||
export interface IDockerRegistryConstructorOptions {
|
export interface IDockerRegistryConstructorOptions {
|
||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
@@ -23,8 +23,7 @@ export class DockerRegistry {
|
|||||||
const dockerRegexResultArray = envString.split('|');
|
const dockerRegexResultArray = envString.split('|');
|
||||||
if (dockerRegexResultArray.length !== 3) {
|
if (dockerRegexResultArray.length !== 3) {
|
||||||
logger.log('error', 'malformed docker env var...');
|
logger.log('error', 'malformed docker env var...');
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const registryUrl = dockerRegexResultArray[0].replace('https://', '').replace('http://', '');
|
const registryUrl = dockerRegexResultArray[0].replace('https://', '').replace('http://', '');
|
||||||
const username = dockerRegexResultArray[1];
|
const username = dockerRegexResultArray[1];
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
|
|
||||||
import { DockerRegistry } from './mod.classes.dockerregistry.js';
|
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
|
||||||
|
|
||||||
export class RegistryStorage {
|
export class RegistryStorage {
|
||||||
objectMap = new plugins.lik.ObjectMap<DockerRegistry>();
|
objectMap = new plugins.lik.ObjectMap<DockerRegistry>();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
|
|
||||||
import { Dockerfile } from './mod.classes.dockerfile.js';
|
import { Dockerfile } from './mod.classes.dockerfile.ts';
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import { bash, bashNoError } from '../npmci.bash.js';
|
import { bash, bashNoError } from '../szci.bash.ts';
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
|
|
||||||
export class NpmciGitManager {
|
export class SzciGitManager {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
|
|
||||||
constructor(npmciRefArg: Npmci) {
|
constructor(szciRefArg: Szci) {
|
||||||
this.npmciRef = npmciRefArg;
|
this.szciRef = szciRefArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,20 +22,20 @@ export class NpmciGitManager {
|
|||||||
await this.mirror();
|
await this.mirror();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log('error', `npmci git -> action >>${action}<< not supported!`);
|
logger.log('error', `szci git -> action >>${action}<< not supported!`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log('info', `npmci git -> cli arguments invalid! Please read the documentation.`);
|
logger.log('info', `szci git -> cli arguments invalid! Please read the documentation.`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public mirror = async () => {
|
public mirror = async () => {
|
||||||
const githubToken = process.env.NPMCI_GIT_GITHUBTOKEN;
|
const githubToken = Deno.env.get("SZCI_GIT_GITHUBTOKEN");
|
||||||
const githubUser = process.env.NPMCI_GIT_GITHUBGROUP || this.npmciRef.npmciEnv.repo.user;
|
const githubUser = Deno.env.get("SZCI_GIT_GITHUBGROUP") || this.szciRef.szciEnv.repo.user;
|
||||||
const githubRepo = process.env.NPMCI_GIT_GITHUB || this.npmciRef.npmciEnv.repo.repo;
|
const githubRepo = Deno.env.get("SZCI_GIT_GITHUB") || this.szciRef.szciEnv.repo.repo;
|
||||||
if (
|
if (
|
||||||
this.npmciRef.npmciConfig.getConfig().projectInfo.npm.packageJson.private === true ||
|
this.szciRef.szciConfig.getConfig().projectInfo.npm.packageJson.private === true ||
|
||||||
this.npmciRef.npmciConfig.getConfig().npmAccessLevel === 'private'
|
this.szciRef.szciConfig.getConfig().npmAccessLevel === 'private'
|
||||||
) {
|
) {
|
||||||
logger.log(
|
logger.log(
|
||||||
'warn',
|
'warn',
|
||||||
@@ -64,8 +64,8 @@ export class NpmciGitManager {
|
|||||||
// remove old mirrors
|
// remove old mirrors
|
||||||
await bashNoError('git remote rm mirror');
|
await bashNoError('git remote rm mirror');
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', `cannot find NPMCI_GIT_GITHUBTOKEN env var!`);
|
logger.log('error', `cannot find SZCI_GIT_GITHUBTOKEN env var!`);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import * as plugins from '../npmci.plugins.js';
|
import * as plugins from '../szci.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
|
|
||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import { bash, bashNoError, nvmAvailable } from '../npmci.bash.js';
|
import { bash, bashNoError, nvmAvailable } from '../szci.bash.ts';
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
|
|
||||||
export class NpmciNodeJsManager {
|
export class SzciNodeJsManager {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
|
|
||||||
constructor(npmciRefArg: Npmci) {
|
constructor(szciRefArg: Szci) {
|
||||||
this.npmciRef = npmciRefArg;
|
this.szciRef = szciRefArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,15 +24,15 @@ export class NpmciNodeJsManager {
|
|||||||
await this.install(argvArg._[2]);
|
await this.install(argvArg._[2]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log('error', `>>npmci node ...<< action >>${action}<< not supported`);
|
logger.log('error', `>>szci node ...<< action >>${action}<< not supported`);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log(
|
logger.log(
|
||||||
'error',
|
'error',
|
||||||
`>>npmci node ...<< cli arguments invalid... Please read the documentation.`
|
`>>szci node ...<< cli arguments invalid... Please read the documentation.`
|
||||||
);
|
);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ export class NpmciNodeJsManager {
|
|||||||
await bash('npm -v');
|
await bash('npm -v');
|
||||||
|
|
||||||
// lets look for further config
|
// lets look for further config
|
||||||
const config = await this.npmciRef.npmciConfig.getConfig();
|
const config = await this.szciRef.szciConfig.getConfig();
|
||||||
logger.log('info', 'Now checking for needed global npm tools...');
|
logger.log('info', 'Now checking for needed global npm tools...');
|
||||||
for (const npmTool of config.npmGlobalTools) {
|
for (const npmTool of config.npmGlobalTools) {
|
||||||
logger.log('info', `Checking for global "${npmTool}"`);
|
logger.log('info', `Checking for global "${npmTool}"`);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
|
|
||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import { bash, bashNoError, nvmAvailable } from '../npmci.bash.js';
|
import { bash, bashNoError, nvmAvailable } from '../szci.bash.ts';
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
|
|
||||||
export class NpmciNpmManager {
|
export class SzciNpmManager {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
|
|
||||||
constructor(npmciRefArg: Npmci) {
|
constructor(szciRefArg: Szci) {
|
||||||
this.npmciRef = npmciRefArg;
|
this.szciRef = szciRefArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,15 +36,15 @@ export class NpmciNpmManager {
|
|||||||
await this.publish();
|
await this.publish();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log('error', `>>npmci npm ...<< action >>${action}<< not supported`);
|
logger.log('error', `>>szci npm ...<< action >>${action}<< not supported`);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`>>npmci npm ...<< cli arguments invalid... Please read the documentation.`
|
`>>szci npm ...<< cli arguments invalid... Please read the documentation.`
|
||||||
);
|
);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,12 +52,17 @@ export class NpmciNpmManager {
|
|||||||
* authenticates npm with token from env var
|
* authenticates npm with token from env var
|
||||||
*/
|
*/
|
||||||
public async prepare() {
|
public async prepare() {
|
||||||
const config = this.npmciRef.npmciConfig.getConfig();
|
logger.log('info', 'running >>npm prepare<<');
|
||||||
|
const config = this.szciRef.szciConfig.getConfig();
|
||||||
let npmrcFileString: string = '';
|
let npmrcFileString: string = '';
|
||||||
await plugins.smartobject.forEachMinimatch(
|
await plugins.smartobject.forEachMinimatch(
|
||||||
process.env,
|
Deno.env.toObject(),
|
||||||
'NPMCI_TOKEN_NPM*',
|
'SZCI_TOKEN_NPM*',
|
||||||
(npmEnvArg: string) => {
|
(npmEnvArg: string) => {
|
||||||
|
if (!npmEnvArg) {
|
||||||
|
logger.log('note','found empty token...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const npmRegistryUrl = npmEnvArg.split('|')[0];
|
const npmRegistryUrl = npmEnvArg.split('|')[0];
|
||||||
logger.log('ok', `found token for ${npmRegistryUrl}`);
|
logger.log('ok', `found token for ${npmRegistryUrl}`);
|
||||||
let npmToken = npmEnvArg.split('|')[1];
|
let npmToken = npmEnvArg.split('|')[1];
|
||||||
@@ -78,14 +83,14 @@ export class NpmciNpmManager {
|
|||||||
logger.log('info', 'found one or more access tokens');
|
logger.log('info', 'found one or more access tokens');
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', 'no access token found! Exiting!');
|
logger.log('error', 'no access token found! Exiting!');
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// lets save it to disk
|
// lets save it to disk
|
||||||
plugins.smartfile.memory.toFsSync(npmrcFileString, '/root/.npmrc');
|
plugins.smartfile.memory.toFsSync(npmrcFileString, '/root/.npmrc');
|
||||||
|
|
||||||
// lets set the cache directory
|
// lets set the cache directory
|
||||||
await bash(`npm config set cache ${paths.NpmciCacheDir} --global `);
|
await bash(`npm config set cache ${paths.SzciCacheDir} --global `);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -98,11 +103,11 @@ export class NpmciNpmManager {
|
|||||||
let npmAccessCliString = ``;
|
let npmAccessCliString = ``;
|
||||||
let npmRegistryCliString = ``;
|
let npmRegistryCliString = ``;
|
||||||
let publishVerdaccioAsWell = false;
|
let publishVerdaccioAsWell = false;
|
||||||
const config = this.npmciRef.npmciConfig.getConfig();
|
const config = this.szciRef.szciConfig.getConfig();
|
||||||
const availableRegistries: string[] = [];
|
const availableRegistries: string[] = [];
|
||||||
await plugins.smartobject.forEachMinimatch(
|
await plugins.smartobject.forEachMinimatch(
|
||||||
process.env,
|
Deno.env.toObject(),
|
||||||
'NPMCI_TOKEN_NPM*',
|
'SZCI_TOKEN_NPM*',
|
||||||
(npmEnvArg: string) => {
|
(npmEnvArg: string) => {
|
||||||
availableRegistries.push(npmEnvArg.split('|')[0]);
|
availableRegistries.push(npmEnvArg.split('|')[0]);
|
||||||
}
|
}
|
||||||
@@ -122,7 +127,7 @@ export class NpmciNpmManager {
|
|||||||
npmRegistryCliString = `--registry=https://${config.npmRegistryUrl}`;
|
npmRegistryCliString = `--registry=https://${config.npmRegistryUrl}`;
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', `no registry url specified. Can't publish!`);
|
logger.log('error', `no registry url specified. Can't publish!`);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let publishCommand = `npm publish ${npmAccessCliString} ${npmRegistryCliString} `;
|
let publishCommand = `npm publish ${npmAccessCliString} ${npmRegistryCliString} `;
|
||||||
@@ -161,7 +166,7 @@ export class NpmciNpmManager {
|
|||||||
logger.log('success', `Nice!!! The build for the publication was successfull!`);
|
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:`);
|
logger.log('info', `Lets clean up so we don't publish any packages that don't belong to us:`);
|
||||||
// -> clean up before we publish stuff
|
// -> clean up before we publish stuff
|
||||||
await bashNoError(`rm -r ./.npmci_cache`);
|
await bashNoError(`rm -r ./.szci_cache`);
|
||||||
await bash(`rm -r ./node_modules`);
|
await bash(`rm -r ./node_modules`);
|
||||||
|
|
||||||
logger.log('success', `Cleaned up!:`);
|
logger.log('success', `Cleaned up!:`);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
|
import { logger } from '../szci.logging.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cleans npmci config files
|
* Cleans szci config files from the project directory
|
||||||
*/
|
*/
|
||||||
export let clean = async (): Promise<void> => {
|
export const clean = async (): Promise<void> => {
|
||||||
plugins.smartfile.fs.removeSync(paths.NpmciPackageConfig);
|
try {
|
||||||
return;
|
if (plugins.smartfile.fs.fileExistsSync(paths.SzciPackageConfig)) {
|
||||||
|
plugins.smartfile.fs.removeSync(paths.SzciPackageConfig);
|
||||||
|
logger.log('ok', 'Cleaned szci config files');
|
||||||
|
} else {
|
||||||
|
logger.log('info', 'No szci config files to clean');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to clean config files: ${(error as Error).message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,15 +1,30 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import { bash } from '../szci.bash.ts';
|
||||||
import { bash } from '../npmci.bash.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
|
|
||||||
export let command = async () => {
|
/**
|
||||||
let wrappedCommand: string = '';
|
* Executes a wrapped command passed via CLI arguments.
|
||||||
let argvArray = process.argv;
|
* Usage: szci command <your-command-here>
|
||||||
for (let i = 3; i < argvArray.length; i++) {
|
*
|
||||||
wrappedCommand = wrappedCommand + argvArray[i];
|
* This allows running arbitrary commands through szci's bash wrapper
|
||||||
if (i + 1 !== argvArray.length) {
|
* which handles nvm and other environment setup.
|
||||||
wrappedCommand = wrappedCommand + ' ';
|
*/
|
||||||
}
|
export const command = async (): Promise<void> => {
|
||||||
|
// Skip 'deno', 'mod.ts', 'command' and get the rest
|
||||||
|
const commandArgs = Deno.args.slice(1); // Skip 'command'
|
||||||
|
|
||||||
|
if (commandArgs.length === 0) {
|
||||||
|
logger.log('error', 'No command specified. Usage: szci command <your-command>');
|
||||||
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wrappedCommand = commandArgs.join(' ');
|
||||||
|
logger.log('info', `Executing command: ${wrappedCommand}`);
|
||||||
|
|
||||||
|
try {
|
||||||
await bash(wrappedCommand);
|
await bash(wrappedCommand);
|
||||||
return;
|
logger.log('ok', 'Command executed successfully');
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Command failed: ${(error as Error).message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.ts';
|
||||||
import * as paths from '../npmci.paths.js';
|
import * as paths from '../szci.paths.ts';
|
||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import { Npmci } from '../npmci.classes.npmci.js';
|
import { Szci } from '../szci.classes.szci.ts';
|
||||||
|
|
||||||
export const handleCli = async (npmciRefArg: Npmci, argvArg: any) => {
|
export const handleCli = async (szciRefArg: Szci, argvArg: any) => {
|
||||||
logger.log('info', 'checking execution context');
|
logger.log('info', 'checking execution context');
|
||||||
const presentRunnerTags = process.env.CI_RUNNER_TAGS.split(',').map((stringArg) =>
|
const ciRunnerTags = Deno.env.get("CI_RUNNER_TAGS");
|
||||||
|
if (!ciRunnerTags) {
|
||||||
|
logger.log('error', 'CI_RUNNER_TAGS environment variable is not set');
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
|
const presentRunnerTags = ciRunnerTags.split(',').map((stringArg) =>
|
||||||
stringArg.trim()
|
stringArg.trim()
|
||||||
);
|
);
|
||||||
let allDesiredGitlabRunnerTagsPresent = true;
|
let allDesiredGitlabRunnerTagsPresent = true;
|
||||||
for (const desiredRunnerTag of npmciRefArg.npmciConfig.getConfig().gitlabRunnerTags) {
|
for (const desiredRunnerTag of szciRefArg.szciConfig.getConfig().gitlabRunnerTags) {
|
||||||
if (!presentRunnerTags.includes(desiredRunnerTag)) {
|
if (!presentRunnerTags.includes(desiredRunnerTag)) {
|
||||||
allDesiredGitlabRunnerTagsPresent = false;
|
allDesiredGitlabRunnerTagsPresent = false;
|
||||||
logger.log(
|
logger.log(
|
||||||
@@ -19,6 +24,6 @@ export const handleCli = async (npmciRefArg: Npmci, argvArg: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!allDesiredGitlabRunnerTagsPresent) {
|
if (!allDesiredGitlabRunnerTagsPresent) {
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,64 +1,97 @@
|
|||||||
import { logger } from '../npmci.logging.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
|
|
||||||
let sshInstance: plugins.smartssh.SshInstance;
|
let sshInstance: plugins.smartssh.SshInstance;
|
||||||
|
|
||||||
export let handleCli = async (argvArg: any) => {
|
/**
|
||||||
|
* Interface for CLI arguments
|
||||||
|
*/
|
||||||
|
interface ICliArgs {
|
||||||
|
_: string[];
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle SSH CLI commands
|
||||||
|
*/
|
||||||
|
export const handleCli = async (argvArg: ICliArgs): Promise<void> => {
|
||||||
if (argvArg._.length >= 2) {
|
if (argvArg._.length >= 2) {
|
||||||
const action: string = argvArg._[1];
|
const action = argvArg._[1];
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'prepare':
|
case 'prepare':
|
||||||
await prepare();
|
await prepare();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log('error', `action >>${action}<< not supported`);
|
logger.log('error', `action >>${action}<< not supported`);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', `>>npmci ssh ...<< please specify an action!`);
|
logger.log('error', `>>szci ssh ...<< please specify an action!`);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks if not undefined
|
* Checks if a string value is defined and not a placeholder
|
||||||
*/
|
*/
|
||||||
const notUndefined = (stringArg: string) => {
|
const isValidValue = (value: string | undefined): boolean => {
|
||||||
return stringArg && stringArg !== 'undefined' && stringArg !== '##';
|
return Boolean(value && value !== 'undefined' && value !== '##');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks for ENV vars in form of NPMCI_SSHKEY_* and deploys any found ones
|
* Checks for ENV vars in form of SZCI_SSHKEY_* and deploys any found ones
|
||||||
*/
|
*/
|
||||||
export let prepare = async () => {
|
export const prepare = async (): Promise<void> => {
|
||||||
sshInstance = new plugins.smartssh.SshInstance(); // init ssh instance
|
sshInstance = new plugins.smartssh.SshInstance();
|
||||||
plugins.smartobject.forEachMinimatch(process.env, 'NPMCI_SSHKEY_*', evaluateSshEnv);
|
|
||||||
if (!process.env.NPMTS_TEST) {
|
// Get all env vars and filter for SSH keys
|
||||||
|
const envVars = Deno.env.toObject();
|
||||||
|
const sshKeyEnvVars = Object.entries(envVars).filter(([key]) =>
|
||||||
|
key.startsWith('SZCI_SSHKEY_')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Process each SSH key env var
|
||||||
|
for (const [key, value] of sshKeyEnvVars) {
|
||||||
|
logger.log('info', `Processing SSH key from ${key}`);
|
||||||
|
addSshKeyFromEnvVar(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only write to disk if not in test mode
|
||||||
|
if (!Deno.env.get('SZCI_TEST')) {
|
||||||
|
try {
|
||||||
sshInstance.writeToDisk();
|
sshInstance.writeToDisk();
|
||||||
|
logger.log('ok', 'SSH keys written to disk');
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to write SSH keys: ${(error as Error).message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log('info', 'In test mode, so not storing SSH keys to disk!');
|
logger.log('info', 'In test mode, so not storing SSH keys to disk!');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets called for each found SSH ENV Var and deploys it
|
* Parses an SSH key env var and adds it to the SSH instance
|
||||||
|
* Format: host|privKeyBase64|pubKeyBase64
|
||||||
*/
|
*/
|
||||||
const evaluateSshEnv = async (sshkeyEnvVarArg: string) => {
|
const addSshKeyFromEnvVar = (sshkeyEnvVarArg: string): void => {
|
||||||
const sshEnvArray = sshkeyEnvVarArg.split('|');
|
const [host, privKeyBase64, pubKeyBase64] = sshkeyEnvVarArg.split('|');
|
||||||
const sshKey = new plugins.smartssh.SshKey();
|
const sshKey = new plugins.smartssh.SshKey();
|
||||||
logger.log('info', 'Found SSH identity for ' + sshEnvArray[1]);
|
|
||||||
if (notUndefined(sshEnvArray[0])) {
|
logger.log('info', `Found SSH identity for ${host || 'unknown host'}`);
|
||||||
|
|
||||||
|
if (isValidValue(host)) {
|
||||||
logger.log('info', '---> host defined!');
|
logger.log('info', '---> host defined!');
|
||||||
sshKey.host = sshEnvArray[0];
|
sshKey.host = host;
|
||||||
}
|
}
|
||||||
if (notUndefined(sshEnvArray[1])) {
|
if (isValidValue(privKeyBase64)) {
|
||||||
logger.log('info', '---> privKey defined!');
|
logger.log('info', '---> privKey defined!');
|
||||||
sshKey.privKeyBase64 = sshEnvArray[1];
|
sshKey.privKeyBase64 = privKeyBase64;
|
||||||
}
|
}
|
||||||
if (notUndefined(sshEnvArray[2])) {
|
if (isValidValue(pubKeyBase64)) {
|
||||||
logger.log('info', '---> pubKey defined!');
|
logger.log('info', '---> pubKey defined!');
|
||||||
sshKey.pubKeyBase64 = sshEnvArray[2];
|
sshKey.pubKeyBase64 = pubKeyBase64;
|
||||||
}
|
}
|
||||||
|
|
||||||
sshInstance.addKey(sshKey);
|
sshInstance.addKey(sshKey);
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,43 +1,101 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.ts';
|
||||||
import { bash } from '../npmci.bash.js';
|
import { logger } from '../szci.logging.ts';
|
||||||
import { logger } from '../npmci.logging.js';
|
|
||||||
|
|
||||||
const triggerValueRegex =
|
/**
|
||||||
/^([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|([a-zA-Z0-9\.]*)\|?([a-zA-Z0-9\.\-\/]*)/;
|
* Interface for parsed trigger configuration
|
||||||
|
*/
|
||||||
|
interface ITriggerConfig {
|
||||||
|
domain: string;
|
||||||
|
projectId: string;
|
||||||
|
triggerToken: string;
|
||||||
|
refName: string;
|
||||||
|
triggerName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export let trigger = async () => {
|
/**
|
||||||
|
* Regex to parse trigger env var format:
|
||||||
|
* domain|projectId|triggerToken|refName|triggerName (optional)
|
||||||
|
*/
|
||||||
|
const TRIGGER_VALUE_REGEX =
|
||||||
|
/^([a-zA-Z0-9.]+)\|([a-zA-Z0-9.]+)\|([a-zA-Z0-9.]+)\|([a-zA-Z0-9.]+)\|?([a-zA-Z0-9.\-/]*)$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute all configured triggers from environment variables
|
||||||
|
*/
|
||||||
|
export const trigger = async (): Promise<void> => {
|
||||||
logger.log('info', 'now running triggers');
|
logger.log('info', 'now running triggers');
|
||||||
await plugins.smartobject.forEachMinimatch(process.env, 'NPMCI_TRIGGER_*', evaluateTrigger);
|
|
||||||
|
// Get all env vars and filter for triggers
|
||||||
|
const envVars = Deno.env.toObject();
|
||||||
|
const triggerEnvVars = Object.entries(envVars).filter(([key]) =>
|
||||||
|
key.startsWith('SZCI_TRIGGER_')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (triggerEnvVars.length === 0) {
|
||||||
|
logger.log('info', 'no triggers configured');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each trigger
|
||||||
|
for (const [key, value] of triggerEnvVars) {
|
||||||
|
logger.log('info', `Processing trigger from ${key}`);
|
||||||
|
await executeTrigger(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('ok', `executed ${triggerEnvVars.length} trigger(s)`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const evaluateTrigger = async (triggerEnvVarArg) => {
|
/**
|
||||||
const triggerRegexResultArray = triggerValueRegex.exec(triggerEnvVarArg);
|
* Parse a trigger env var string into a config object
|
||||||
const regexDomain = triggerRegexResultArray[1];
|
*/
|
||||||
const regexProjectId = triggerRegexResultArray[2];
|
const parseTriggerConfig = (triggerEnvVar: string): ITriggerConfig | null => {
|
||||||
const regexProjectTriggerToken = triggerRegexResultArray[3];
|
const match = TRIGGER_VALUE_REGEX.exec(triggerEnvVar);
|
||||||
const regexRefName = triggerRegexResultArray[4];
|
if (!match) {
|
||||||
let regexTriggerName;
|
return null;
|
||||||
if (triggerRegexResultArray.length === 6) {
|
|
||||||
regexTriggerName = triggerRegexResultArray[5];
|
|
||||||
} else {
|
|
||||||
regexTriggerName = 'Unnamed Trigger';
|
|
||||||
}
|
}
|
||||||
logger.log('info', 'Found Trigger!');
|
|
||||||
logger.log('info', 'triggering build for ref ' + regexRefName + ' of ' + regexTriggerName);
|
return {
|
||||||
plugins.smartrequest.postFormData(
|
domain: match[1],
|
||||||
'https://gitlab.com/api/v3/projects/' + regexProjectId + '/trigger/builds',
|
projectId: match[2],
|
||||||
|
triggerToken: match[3],
|
||||||
|
refName: match[4],
|
||||||
|
triggerName: match[5] || 'Unnamed Trigger',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a single trigger by calling the GitLab API
|
||||||
|
*/
|
||||||
|
const executeTrigger = async (triggerEnvVar: string): Promise<void> => {
|
||||||
|
const config = parseTriggerConfig(triggerEnvVar);
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
logger.log('error', 'malformed trigger env var, expected format: domain|projectId|token|ref|name');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', `Found Trigger: ${config.triggerName}`);
|
||||||
|
logger.log('info', `Triggering build for ref "${config.refName}" of "${config.triggerName}"`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await plugins.smartrequest.postFormData(
|
||||||
|
`https://${config.domain}/api/v3/projects/${config.projectId}/trigger/builds`,
|
||||||
{},
|
{},
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 'token',
|
name: 'token',
|
||||||
payload: regexProjectTriggerToken,
|
payload: config.triggerToken,
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ref',
|
name: 'ref',
|
||||||
payload: regexRefName,
|
payload: config.refName,
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
logger.log('ok', `Trigger "${config.triggerName}" executed successfully`);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to execute trigger: ${(error as Error).message}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from '../npmci.plugins.js';
|
export * from '../szci.plugins.ts';
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import * as plugins from './npmci.plugins.js';
|
|
||||||
|
|
||||||
|
|
||||||
// env
|
|
||||||
import { NpmciEnv } from './npmci.classes.npmcienv.js';
|
|
||||||
import { NpmciInfo } from './npmci.classes.npmciinfo.js';
|
|
||||||
import { NpmciCli } from './npmci.classes.npmcicli.js';
|
|
||||||
import { NpmciConfig } from './npmci.classes.npmciconfig.js';
|
|
||||||
|
|
||||||
// connectors
|
|
||||||
import { CloudlyConnector } from './connector.cloudly/cloudlyconnector.js';
|
|
||||||
|
|
||||||
// managers
|
|
||||||
import { NpmciCloudronManager } from './manager.cloudron/index.js';
|
|
||||||
import { NpmciDockerManager } from './manager.docker/index.js';
|
|
||||||
import { NpmciGitManager } from './manager.git/index.js';
|
|
||||||
import { NpmciNodeJsManager } from './manager.nodejs/index.js';
|
|
||||||
import { NpmciNpmManager } from './manager.npm/index.js';
|
|
||||||
|
|
||||||
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 cloudronManager: NpmciCloudronManager;
|
|
||||||
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.cloudronManager = new NpmciCloudronManager(this);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import * as plugins from './npmci.plugins.js';
|
|
||||||
import * as paths from './npmci.paths.js';
|
|
||||||
|
|
||||||
import { logger } from './npmci.logging.js';
|
|
||||||
import { Npmci } from './npmci.classes.npmci.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the main config interface for npmci
|
|
||||||
*/
|
|
||||||
export interface INpmciOptions {
|
|
||||||
projectInfo: plugins.projectinfo.ProjectInfo;
|
|
||||||
|
|
||||||
// npm
|
|
||||||
npmGlobalTools: string[];
|
|
||||||
npmAccessLevel?: 'private' | 'public';
|
|
||||||
npmRegistryUrl: string;
|
|
||||||
|
|
||||||
// docker
|
|
||||||
dockerRegistries: string[];
|
|
||||||
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;
|
|
||||||
|
|
||||||
public npmciNpmextra: plugins.npmextra.Npmextra;
|
|
||||||
public kvStorage: plugins.npmextra.KeyValueStore;
|
|
||||||
public npmciQenv: plugins.qenv.Qenv;
|
|
||||||
|
|
||||||
private configObject: INpmciOptions;
|
|
||||||
|
|
||||||
constructor(npmciRefArg: Npmci) {
|
|
||||||
this.npmciRef = npmciRefArg;
|
|
||||||
|
|
||||||
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.npmciQenv = new plugins.qenv.Qenv(
|
|
||||||
paths.NpmciProjectDir,
|
|
||||||
paths.NpmciProjectNogitDir,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
this.configObject = {
|
|
||||||
projectInfo: new plugins.projectinfo.ProjectInfo(paths.cwd),
|
|
||||||
npmGlobalTools: [],
|
|
||||||
dockerRegistries: [],
|
|
||||||
dockerRegistryRepoMap: {},
|
|
||||||
npmAccessLevel: 'private',
|
|
||||||
npmRegistryUrl: 'registry.npmjs.org',
|
|
||||||
gitlabRunnerTags: [],
|
|
||||||
dockerBuildargEnvMap: {},
|
|
||||||
urlCloudly: this.npmciQenv.getEnvVarOnDemand('NPMCI_URL_CLOUDLY'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async init() {
|
|
||||||
this.configObject = this.npmciNpmextra.dataFor<INpmciOptions>('npmci', this.configObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getConfig(): INpmciOptions {
|
|
||||||
return this.configObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import * as plugins from './npmci.plugins.js';
|
|
||||||
import { Npmci } from './npmci.classes.npmci.js';
|
|
||||||
|
|
||||||
export class NpmciEnv {
|
|
||||||
public npmciRef: Npmci;
|
|
||||||
|
|
||||||
public repoString: string;
|
|
||||||
public repo: plugins.smartstring.GitRepo;
|
|
||||||
|
|
||||||
constructor(npmciRefArg: Npmci) {
|
|
||||||
this.npmciRef = npmciRefArg;
|
|
||||||
if (process.env.GITLAB_CI) {
|
|
||||||
this.repoString = process.env.CI_REPOSITORY_URL;
|
|
||||||
}
|
|
||||||
if (process.env.NPMCI_COMPUTED_REPOURL) {
|
|
||||||
this.repoString = process.env.NPMCI_COMPUTED_REPOURL;
|
|
||||||
}
|
|
||||||
if (!this.repoString) {
|
|
||||||
this.repoString = 'https://undefined:undefined@github.com/undefined/undefined.git';
|
|
||||||
}
|
|
||||||
this.repo = new plugins.smartstring.GitRepo(this.repoString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import * as plugins from './npmci.plugins.js';
|
|
||||||
import * as paths from './npmci.paths.js';
|
|
||||||
import { logger } from './npmci.logging.js';
|
|
||||||
import { Npmci } from './npmci.classes.npmci.js';
|
|
||||||
|
|
||||||
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}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import * as plugins from './npmci.plugins.js';
|
|
||||||
|
|
||||||
export const cwd = process.cwd();
|
|
||||||
|
|
||||||
// package paths
|
|
||||||
export const NpmciPackageRoot = plugins.path.join(
|
|
||||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
|
||||||
'../'
|
|
||||||
);
|
|
||||||
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');
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import { logger } from './npmci.logging.js';
|
import { logger } from './szci.logging.ts';
|
||||||
import * as plugins from './npmci.plugins.js';
|
import * as plugins from './szci.plugins.ts';
|
||||||
import * as paths from './npmci.paths.js';
|
import * as paths from './szci.paths.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wether nvm is available or not
|
* wether nvm is available or not
|
||||||
*/
|
*/
|
||||||
export let nvmAvailable = plugins.smartpromise.defer<boolean>();
|
export let nvmAvailable = plugins.smartpromise.defer<boolean>();
|
||||||
/**
|
/**
|
||||||
* the smartshell instance for npmci
|
* the smartshell instance for szci
|
||||||
*/
|
*/
|
||||||
const npmciSmartshell = new plugins.smartshell.Smartshell({
|
const szciSmartshell = new plugins.smartshell.Smartshell({
|
||||||
executor: 'bash',
|
executor: 'bash',
|
||||||
sourceFilePaths: [],
|
sourceFilePaths: [],
|
||||||
});
|
});
|
||||||
@@ -19,16 +19,16 @@ const npmciSmartshell = new plugins.smartshell.Smartshell({
|
|||||||
*/
|
*/
|
||||||
const checkToolsAvailable = async () => {
|
const checkToolsAvailable = async () => {
|
||||||
// check for nvm
|
// check for nvm
|
||||||
if (!process.env.NPMTS_TEST) {
|
if (!Deno.env.get('SZCI_TEST')) {
|
||||||
if (
|
if (
|
||||||
(await npmciSmartshell.execSilent(`bash -c "source /usr/local/nvm/nvm.sh"`)).exitCode === 0
|
(await szciSmartshell.execSilent(`bash -c "source /usr/local/nvm/nvm.sh"`)).exitCode === 0
|
||||||
) {
|
) {
|
||||||
npmciSmartshell.shellEnv.addSourceFiles([`/usr/local/nvm/nvm.sh`]);
|
szciSmartshell.shellEnv.addSourceFiles([`/usr/local/nvm/nvm.sh`]);
|
||||||
nvmAvailable.resolve(true);
|
nvmAvailable.resolve(true);
|
||||||
} else if (
|
} else if (
|
||||||
(await npmciSmartshell.execSilent(`bash -c "source ~/.nvm/nvm.sh"`)).exitCode === 0
|
(await szciSmartshell.execSilent(`bash -c "source ~/.nvm/nvm.sh"`)).exitCode === 0
|
||||||
) {
|
) {
|
||||||
npmciSmartshell.shellEnv.addSourceFiles([`~/.nvm/nvm.sh`]);
|
szciSmartshell.shellEnv.addSourceFiles([`~/.nvm/nvm.sh`]);
|
||||||
nvmAvailable.resolve(true);
|
nvmAvailable.resolve(true);
|
||||||
} else {
|
} else {
|
||||||
nvmAvailable.resolve(false);
|
nvmAvailable.resolve(false);
|
||||||
@@ -46,7 +46,7 @@ checkToolsAvailable();
|
|||||||
*/
|
*/
|
||||||
export let bash = async (commandArg: string, retryArg: number = 2): Promise<string> => {
|
export let bash = async (commandArg: string, retryArg: number = 2): Promise<string> => {
|
||||||
await nvmAvailable.promise; // make sure nvm check has run
|
await nvmAvailable.promise; // make sure nvm check has run
|
||||||
let execResult: plugins.smartshell.IExecResult;
|
let execResult!: plugins.smartshell.IExecResult;
|
||||||
|
|
||||||
// determine if we fail
|
// determine if we fail
|
||||||
let failOnError: boolean = true;
|
let failOnError: boolean = true;
|
||||||
@@ -55,20 +55,20 @@ export let bash = async (commandArg: string, retryArg: number = 2): Promise<stri
|
|||||||
retryArg = 0;
|
retryArg = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process.env.NPMTS_TEST) {
|
if (!Deno.env.get('SZCI_TEST')) {
|
||||||
// NPMTS_TEST is used during testing
|
// SZCI_TEST is used during testing
|
||||||
for (let i = 0; i <= retryArg; i++) {
|
for (let i = 0; i <= retryArg; i++) {
|
||||||
if (process.env.DEBUG_NPMCI === 'true') {
|
if (Deno.env.get('DEBUG_SZCI') === 'true') {
|
||||||
console.log(commandArg);
|
console.log(commandArg);
|
||||||
}
|
}
|
||||||
execResult = await npmciSmartshell.exec(commandArg);
|
execResult = await szciSmartshell.exec(commandArg);
|
||||||
|
|
||||||
// determine how bash reacts to error and success
|
// determine how bash reacts to error and success
|
||||||
if (execResult.exitCode !== 0 && i === retryArg) {
|
if (execResult.exitCode !== 0 && i === retryArg) {
|
||||||
// something went wrong and retries are exhausted
|
// something went wrong and retries are exhausted
|
||||||
if (failOnError) {
|
if (failOnError) {
|
||||||
logger.log('error', 'something went wrong and retries are exhausted');
|
logger.log('error', 'something went wrong and retries are exhausted');
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
} else if (execResult.exitCode === 0) {
|
} else if (execResult.exitCode === 0) {
|
||||||
// everything went fine, or no error wanted
|
// everything went fine, or no error wanted
|
||||||
61
ts/szci.classes.szci.ts
Normal file
61
ts/szci.classes.szci.ts
Normal 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 szciEnv!: SzciEnv;
|
||||||
|
public szciInfo!: SzciInfo;
|
||||||
|
public szciConfig!: SzciConfig;
|
||||||
|
public szciCli!: 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.szciEnv = new SzciEnv(this);
|
||||||
|
this.szciInfo = new SzciInfo(this);
|
||||||
|
await this.szciInfo.printToConsole();
|
||||||
|
this.szciCli = new SzciCli(this);
|
||||||
|
this.szciConfig = new SzciConfig(this);
|
||||||
|
await this.szciConfig.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.szciCli.startParse();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,89 +1,89 @@
|
|||||||
import { logger } from './npmci.logging.js';
|
import { logger } from './szci.logging.ts';
|
||||||
import * as plugins from './npmci.plugins.js';
|
import * as plugins from './szci.plugins.ts';
|
||||||
import * as paths from './npmci.paths.js';
|
import * as paths from './szci.paths.ts';
|
||||||
import { Npmci } from './npmci.classes.npmci.js';
|
import { Szci } from './szci.classes.szci.ts';
|
||||||
|
|
||||||
export class NpmciCli {
|
export class SzciCli {
|
||||||
public npmciRef: Npmci;
|
public szciRef: Szci;
|
||||||
public smartcli: plugins.smartcli.Smartcli;
|
public smartcli: plugins.smartcli.Smartcli;
|
||||||
|
|
||||||
constructor(npmciArg: Npmci) {
|
constructor(szciArg: Szci) {
|
||||||
this.npmciRef = npmciArg;
|
this.szciRef = szciArg;
|
||||||
this.smartcli = new plugins.smartcli.Smartcli();
|
this.smartcli = new plugins.smartcli.Smartcli();
|
||||||
this.smartcli.addVersion(this.npmciRef.npmciInfo.projectInfo.version);
|
this.smartcli.addVersion(this.szciRef.szciInfo.version);
|
||||||
|
|
||||||
// clean
|
// clean
|
||||||
this.smartcli.addCommand('clean').subscribe(
|
this.smartcli.addCommand('clean').subscribe(
|
||||||
async (argv) => {
|
async (argv) => {
|
||||||
const modClean = await import('./mod_clean/index.js');
|
const modClean = await import('./mod_clean/index.ts');
|
||||||
await modClean.clean();
|
await modClean.clean();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// cloudron
|
// cloudron
|
||||||
this.smartcli.addCommand('cloudron').subscribe(
|
this.smartcli.addCommand('cloudron').subscribe(
|
||||||
async (argv) => {
|
async (argv) => {
|
||||||
await this.npmciRef.cloudronManager.handleCli(argv);
|
await this.szciRef.cloudronManager.handleCli(argv);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// command
|
// command
|
||||||
this.smartcli.addCommand('command').subscribe(
|
this.smartcli.addCommand('command').subscribe(
|
||||||
async (argv) => {
|
async (argv) => {
|
||||||
const modCommand = await import('./mod_command/index.js');
|
const modCommand = await import('./mod_command/index.ts');
|
||||||
await modCommand.command();
|
await modCommand.command();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// git
|
// git
|
||||||
this.smartcli.addCommand('git').subscribe(
|
this.smartcli.addCommand('git').subscribe(
|
||||||
async (argvArg) => {
|
async (argvArg) => {
|
||||||
await this.npmciRef.gitManager.handleCli(argvArg);
|
await this.szciRef.gitManager.handleCli(argvArg);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// build
|
// build
|
||||||
this.smartcli.addCommand('docker').subscribe(
|
this.smartcli.addCommand('docker').subscribe(
|
||||||
async (argvArg) => {
|
async (argvArg) => {
|
||||||
await this.npmciRef.dockerManager.handleCli(argvArg);
|
await this.szciRef.dockerManager.handleCli(argvArg);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
this.smartcli.addCommand('node').subscribe(
|
this.smartcli.addCommand('node').subscribe(
|
||||||
async (argvArg) => {
|
async (argvArg) => {
|
||||||
await this.npmciRef.nodejsManager.handleCli(argvArg);
|
await this.szciRef.nodejsManager.handleCli(argvArg);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// npm
|
// npm
|
||||||
this.smartcli.addCommand('npm').subscribe(
|
this.smartcli.addCommand('npm').subscribe(
|
||||||
async (argvArg) => {
|
async (argvArg) => {
|
||||||
await this.npmciRef.npmManager.handleCli(argvArg);
|
await this.szciRef.npmManager.handleCli(argvArg);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -91,25 +91,25 @@ export class NpmciCli {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.smartcli.addCommand('precheck').subscribe(async (argvArg) => {
|
this.smartcli.addCommand('precheck').subscribe(async (argvArg) => {
|
||||||
const modPrecheck = await import('./mod_precheck/index.js');
|
const modPrecheck = await import('./mod_precheck/index.ts');
|
||||||
await modPrecheck.handleCli(this.npmciRef, argvArg);
|
await modPrecheck.handleCli(this.szciRef, argvArg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// trigger
|
// trigger
|
||||||
this.smartcli.addCommand('ssh').subscribe(async (argvArg) => {
|
this.smartcli.addCommand('ssh').subscribe(async (argvArg) => {
|
||||||
const modSsh = await import('./mod_ssh/index.js');
|
const modSsh = await import('./mod_ssh/index.ts');
|
||||||
await modSsh.handleCli(argvArg);
|
await modSsh.handleCli(argvArg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// trigger
|
// trigger
|
||||||
this.smartcli.addCommand('trigger').subscribe(
|
this.smartcli.addCommand('trigger').subscribe(
|
||||||
async (argv) => {
|
async (argv) => {
|
||||||
const modTrigger = await import('./mod_trigger/index.js');
|
const modTrigger = await import('./mod_trigger/index.ts');
|
||||||
await modTrigger.trigger();
|
await modTrigger.trigger();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
78
ts/szci.classes.szciconfig.ts
Normal file
78
ts/szci.classes.szciconfig.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the main config interface for szci
|
||||||
|
*/
|
||||||
|
export interface ISzciOptions {
|
||||||
|
projectInfo: plugins.projectinfo.ProjectInfo;
|
||||||
|
|
||||||
|
// npm
|
||||||
|
npmGlobalTools: string[];
|
||||||
|
npmAccessLevel?: 'private' | 'public';
|
||||||
|
npmRegistryUrl: string;
|
||||||
|
|
||||||
|
// docker
|
||||||
|
dockerRegistries: string[];
|
||||||
|
dockerRegistryRepoMap: { [key: string]: string };
|
||||||
|
dockerBuildargEnvMap: { [key: string]: string };
|
||||||
|
|
||||||
|
// gitlab
|
||||||
|
gitlabRunnerTags: string[];
|
||||||
|
|
||||||
|
// urls
|
||||||
|
urlCloudly?: string;
|
||||||
|
|
||||||
|
// cloudron
|
||||||
|
cloudronAppName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a config class for Szci
|
||||||
|
*/
|
||||||
|
export class SzciConfig {
|
||||||
|
public szciRef: Szci;
|
||||||
|
|
||||||
|
public szciNpmextra!: plugins.npmextra.Npmextra;
|
||||||
|
public kvStorage!: plugins.npmextra.KeyValueStore;
|
||||||
|
public szciQenv!: plugins.qenv.Qenv;
|
||||||
|
|
||||||
|
private configObject!: ISzciOptions;
|
||||||
|
|
||||||
|
constructor(szciRefArg: Szci) {
|
||||||
|
this.szciRef = szciRefArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init() {
|
||||||
|
this.szciNpmextra = new plugins.npmextra.Npmextra(paths.cwd);
|
||||||
|
this.kvStorage = new plugins.npmextra.KeyValueStore({
|
||||||
|
typeArg: 'userHomeDir',
|
||||||
|
identityArg: `.szci_${this.szciRef.szciEnv.repo.user}_${this.szciRef.szciEnv.repo.repo}`,
|
||||||
|
});
|
||||||
|
this.szciQenv = new plugins.qenv.Qenv(
|
||||||
|
paths.SzciProjectDir,
|
||||||
|
paths.SzciProjectNogitDir,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
this.configObject = {
|
||||||
|
projectInfo: new plugins.projectinfo.ProjectInfo(paths.cwd),
|
||||||
|
npmGlobalTools: [],
|
||||||
|
dockerRegistries: [],
|
||||||
|
dockerRegistryRepoMap: {},
|
||||||
|
npmAccessLevel: 'private',
|
||||||
|
npmRegistryUrl: 'registry.npmjs.org',
|
||||||
|
gitlabRunnerTags: [],
|
||||||
|
dockerBuildargEnvMap: {},
|
||||||
|
urlCloudly: await this.szciQenv.getEnvVarOnDemand('SZCI_URL_CLOUDLY'),
|
||||||
|
};
|
||||||
|
this.configObject = this.szciNpmextra.dataFor<ISzciOptions>('@ship.zone/szci', this.configObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfig(): ISzciOptions {
|
||||||
|
return this.configObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
ts/szci.classes.szcienv.ts
Normal file
25
ts/szci.classes.szcienv.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
// Determine repo string from environment
|
||||||
|
let repoUrl: string | undefined;
|
||||||
|
if (Deno.env.get("GITLAB_CI")) {
|
||||||
|
repoUrl = Deno.env.get("CI_REPOSITORY_URL");
|
||||||
|
}
|
||||||
|
if (!repoUrl && Deno.env.get("SZCI_COMPUTED_REPOURL")) {
|
||||||
|
repoUrl = Deno.env.get("SZCI_COMPUTED_REPOURL");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repoString = repoUrl || 'https://undefined:undefined@github.com/undefined/undefined.git';
|
||||||
|
this.repo = new plugins.smartstring.GitRepo(this.repoString);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
ts/szci.classes.szciinfo.ts
Normal file
18
ts/szci.classes.szciinfo.ts
Normal 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as plugins from './npmci.plugins.js';
|
import * as plugins from './szci.plugins.ts';
|
||||||
|
|
||||||
export const logger = new plugins.smartlog.Smartlog({
|
export const logger = new plugins.smartlog.Smartlog({
|
||||||
logContext: {
|
logContext: {
|
||||||
@@ -6,7 +6,7 @@ export const logger = new plugins.smartlog.Smartlog({
|
|||||||
companyunit: 'Some Unit',
|
companyunit: 'Some Unit',
|
||||||
containerName: 'Some ContainerName',
|
containerName: 'Some ContainerName',
|
||||||
environment: 'test',
|
environment: 'test',
|
||||||
runtime: 'node',
|
runtime: 'deno',
|
||||||
zone: 'Some Zone',
|
zone: 'Some Zone',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
31
ts/szci.paths.ts
Normal file
31
ts/szci.paths.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import * as plugins from './szci.plugins.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current working directory (evaluated at call time, not module load time)
|
||||||
|
*/
|
||||||
|
export const getCwd = (): string => Deno.cwd();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current working directory - use getCwd() if you need the live value after chdir
|
||||||
|
* @deprecated Use getCwd() for dynamic cwd resolution
|
||||||
|
*/
|
||||||
|
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 - use functions for dynamic resolution
|
||||||
|
export const getSzciProjectDir = (): string => getCwd();
|
||||||
|
export const getSzciProjectNogitDir = (): string => plugins.path.join(getCwd(), './.nogit');
|
||||||
|
export const getSzciTestDir = (): string => plugins.path.join(getCwd(), './test');
|
||||||
|
export const getSzciCacheDir = (): string => plugins.path.join(getCwd(), './.szci_cache');
|
||||||
|
|
||||||
|
// Static paths (for backwards compatibility - captured at module load)
|
||||||
|
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, './.szci_cache');
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
// node native
|
// Deno std libraries
|
||||||
import * as path from 'path';
|
import * as path from '@std/path';
|
||||||
|
|
||||||
export { path };
|
export { path };
|
||||||
|
|
||||||
// @apiglobal
|
// @apiglobal
|
||||||
import * as typedrequest from '@apiglobal/typedrequest';
|
import * as typedrequest from '@api.global/typedrequest';
|
||||||
|
|
||||||
export { typedrequest };
|
export { typedrequest };
|
||||||
|
|
||||||
// @servezone
|
// @servezone
|
||||||
import * as servezoneInterfaces from '@servezone/interfaces';
|
import * as servezoneApi from '@serve.zone/api';
|
||||||
|
|
||||||
export { servezoneInterfaces };
|
export { servezoneApi };
|
||||||
|
|
||||||
// @push.rocks
|
// @push.rocks
|
||||||
import * as lik from '@push.rocks/lik';
|
import * as lik from '@push.rocks/lik';
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"useDefineForClassFields": false,
|
|
||||||
"target": "ES2022",
|
|
||||||
"module": "ES2022",
|
|
||||||
"moduleResolution": "nodenext",
|
|
||||||
"esModuleInterop": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
64
uninstall.sh
Executable file
64
uninstall.sh
Executable 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 ""
|
||||||
Reference in New Issue
Block a user