create-sy 2.5.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -2
- package/.eslintrc.cjs +0 -44
- package/CHANGELOG.md +0 -5
- package/docs/E2E_TESTS_REPORT.md +0 -241
- package/e2e/config.ts +0 -21
- package/e2e/container/Dockerfile.node18 +0 -8
- package/e2e/container/Dockerfile.node20 +0 -8
- package/e2e/container/Dockerfile.node22 +0 -8
- package/e2e/container/start-containers.sh +0 -67
- package/e2e/container/stop-containers.sh +0 -24
- package/e2e/fixtures/container.fixture.ts +0 -64
- package/e2e/utils/container-cli.ts +0 -182
- package/e2e/vitest.e2e.config.ts +0 -21
- package/typedoc.config.cjs +0 -6
- package/vitest.config.ts +0 -24
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-sy",
|
|
3
3
|
"displayName": "Syngrisi Setup Package",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.1",
|
|
5
5
|
"description": "Easily install and configure Syngrisi for visual testing.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Viktar Silakou",
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
"create-sy": "./bin/sy.js"
|
|
32
32
|
},
|
|
33
33
|
"type": "module",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.19.0"
|
|
36
|
+
},
|
|
34
37
|
"scripts": {
|
|
35
38
|
"build": "run-s clean compile",
|
|
36
39
|
"clean": "rimraf tsconfig.tsbuildinfo ./build ./coverage",
|
|
@@ -73,5 +76,9 @@
|
|
|
73
76
|
"cross-spawn": "^7.0.3",
|
|
74
77
|
"semver": "^7.7.3"
|
|
75
78
|
},
|
|
76
|
-
"
|
|
79
|
+
"files": [
|
|
80
|
+
"build",
|
|
81
|
+
"bin"
|
|
82
|
+
],
|
|
83
|
+
"gitHead": "0c9cedf39168bce54fd23fc2ded183a5741c373b"
|
|
77
84
|
}
|
package/.eslintrc.cjs
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
parser: '@typescript-eslint/parser',
|
|
4
|
-
plugins: ['@typescript-eslint'],
|
|
5
|
-
extends: [
|
|
6
|
-
'eslint:recommended',
|
|
7
|
-
'plugin:import/errors',
|
|
8
|
-
'plugin:import/warnings'
|
|
9
|
-
],
|
|
10
|
-
env: {
|
|
11
|
-
node: true,
|
|
12
|
-
es6: true,
|
|
13
|
-
jest: true
|
|
14
|
-
},
|
|
15
|
-
parserOptions: {
|
|
16
|
-
ecmaVersion: 2019,
|
|
17
|
-
sourceType: 'module'
|
|
18
|
-
},
|
|
19
|
-
rules: {
|
|
20
|
-
semi: ['error', 'never'],
|
|
21
|
-
quotes: ['error', 'single'],
|
|
22
|
-
indent: [2, 4],
|
|
23
|
-
|
|
24
|
-
'no-constant-condition': 0,
|
|
25
|
-
// see https://stackoverflow.com/questions/55280555/typescript-eslint-eslint-plugin-error-route-is-defined-but-never-used-no-un
|
|
26
|
-
'no-unused-vars': 'off',
|
|
27
|
-
'@typescript-eslint/no-unused-vars': 'error',
|
|
28
|
-
|
|
29
|
-
'import/no-unresolved': 0,
|
|
30
|
-
'import/named': 2,
|
|
31
|
-
'import/namespace': 2,
|
|
32
|
-
'import/default': 2,
|
|
33
|
-
'import/export': 2,
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
overrides: [
|
|
37
|
-
{
|
|
38
|
-
files: ['*.ts'],
|
|
39
|
-
rules: {
|
|
40
|
-
'no-undef': 'off'
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
]
|
|
44
|
-
}
|
package/docs/E2E_TESTS_REPORT.md
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
# E2E Tests Implementation Report
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This document describes the work done on implementing E2E tests for the `create-sy` package using Apple Container CLI.
|
|
6
|
-
|
|
7
|
-
## Completed Work
|
|
8
|
-
|
|
9
|
-
### 1. E2E Test Infrastructure
|
|
10
|
-
|
|
11
|
-
Created a complete e2e test infrastructure using Apple Container CLI:
|
|
12
|
-
|
|
13
|
-
**Files created:**
|
|
14
|
-
- `e2e/vitest.e2e.config.ts` - Vitest configuration for e2e tests
|
|
15
|
-
- `e2e/fixtures/container.fixture.ts` - Container test fixture with helper functions
|
|
16
|
-
- `e2e/utils/container-cli.ts` - Wrapper for Apple Container CLI operations
|
|
17
|
-
- `e2e/container/start-containers.sh` - Script to build container images
|
|
18
|
-
- `e2e/container/Dockerfile.node` - Dockerfile template for Node.js containers
|
|
19
|
-
|
|
20
|
-
**Test files created:**
|
|
21
|
-
- `e2e/tests/node-versions/node18.e2e.ts` - Node.js 18 compatibility tests
|
|
22
|
-
- `e2e/tests/node-versions/node20.e2e.ts` - Node.js 20 compatibility tests
|
|
23
|
-
- `e2e/tests/node-versions/node22.e2e.ts` - Node.js 22 compatibility tests
|
|
24
|
-
- `e2e/tests/installation/npm-tag-latest.e2e.ts` - Latest version installation tests
|
|
25
|
-
- `e2e/tests/installation/npm-tag-specific.e2e.ts` - Specific version installation tests
|
|
26
|
-
- `e2e/tests/installation/npm-tag-invalid.e2e.ts` - Invalid tag validation tests
|
|
27
|
-
- `e2e/tests/full-flow/install-and-start.e2e.ts` - Full installation flow tests
|
|
28
|
-
|
|
29
|
-
### 2. Security Improvements
|
|
30
|
-
|
|
31
|
-
Added `validateNpmTag()` function to prevent command injection:
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
// src/utils.ts
|
|
35
|
-
export function validateNpmTag(tag: string): void {
|
|
36
|
-
const forbiddenChars = /[;&|`$(){}[\]<>\\!#'"*?\n\r]/
|
|
37
|
-
if (forbiddenChars.test(tag)) {
|
|
38
|
-
throw new Error(`Invalid npmTag: contains forbidden characters`)
|
|
39
|
-
}
|
|
40
|
-
if (tag.length > 100) {
|
|
41
|
-
throw new Error(`Invalid npmTag: exceeds maximum length of 100 characters`)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### 3. CLI Improvements
|
|
47
|
-
|
|
48
|
-
Added `--yes` flag support for non-interactive mode:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
// src/createSyngrisiProject.ts
|
|
52
|
-
const args = yargs(hideBin(process.argv))
|
|
53
|
-
.option('yes', {
|
|
54
|
-
alias: 'y',
|
|
55
|
-
type: 'boolean',
|
|
56
|
-
description: 'Skip confirmation prompts',
|
|
57
|
-
default: false,
|
|
58
|
-
})
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 4. Unit Tests
|
|
62
|
-
|
|
63
|
-
All 24 unit tests passing with 100% coverage:
|
|
64
|
-
- `validateNpmTag()` validation tests
|
|
65
|
-
- CLI argument parsing tests
|
|
66
|
-
- Package installation tests
|
|
67
|
-
|
|
68
|
-
## Problems Encountered
|
|
69
|
-
|
|
70
|
-
### 1. XPC Connection Interrupted Errors
|
|
71
|
-
|
|
72
|
-
**Problem:** Apple Container CLI threw `XPC connection interrupted` errors when running containers.
|
|
73
|
-
|
|
74
|
-
**Error message:**
|
|
75
|
-
```
|
|
76
|
-
internalError: "failed to wait for process...XPC connection error: Connection interrupted"
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Solution:** Added `container system start` call before running containers:
|
|
80
|
-
```bash
|
|
81
|
-
# e2e/container/start-containers.sh
|
|
82
|
-
start_container_system() {
|
|
83
|
-
log_info "Starting container system service..."
|
|
84
|
-
container system start 2>/dev/null || true
|
|
85
|
-
log_info "Container system service started"
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
**Reference:** https://github.com/apple/container/issues/699
|
|
90
|
-
|
|
91
|
-
### 2. npm init Flags Not Passed to create-sy
|
|
92
|
-
|
|
93
|
-
**Problem:** Flags like `-y -f` were consumed by `npm init` instead of being passed to create-sy.
|
|
94
|
-
|
|
95
|
-
**Wrong approach:**
|
|
96
|
-
```bash
|
|
97
|
-
npm init sy@latest -y -f # -y -f consumed by npm init
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Solution:** Use `--` separator to pass flags through:
|
|
101
|
-
```bash
|
|
102
|
-
npm init sy@latest -- --yes --force # flags passed to create-sy
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### 3. Shell Command Escaping in Containers
|
|
106
|
-
|
|
107
|
-
**Problem:** Commands weren't being executed properly in containers due to escaping issues.
|
|
108
|
-
|
|
109
|
-
**Solution:** Fixed escaping in `container-cli.ts`:
|
|
110
|
-
```typescript
|
|
111
|
-
// Use single quotes around the shell command
|
|
112
|
-
const shellCommand = command.join(' && ')
|
|
113
|
-
execSync(`container ${args.join(' ')} sh -c '${shellCommand}'`, {...})
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### 4. Container Process Crashes (Exit Code 139)
|
|
117
|
-
|
|
118
|
-
**Problem:** Some tests fail with exit code 139 (SIGSEGV) during npm installation.
|
|
119
|
-
|
|
120
|
-
**Hypothesis tested:**
|
|
121
|
-
- Memory issues in containers (increased memory allocation)
|
|
122
|
-
- Timeout issues (increased to 5 minutes)
|
|
123
|
-
- Concurrent container execution (switched to sequential with `singleFork: true`)
|
|
124
|
-
|
|
125
|
-
**Status:** Not fully resolved. The issue appears to be related to npm installation inside containers crashing intermittently.
|
|
126
|
-
|
|
127
|
-
### 5. Long Test Execution Time
|
|
128
|
-
|
|
129
|
-
**Problem:** Tests take extremely long to complete (5+ minutes per test).
|
|
130
|
-
|
|
131
|
-
**Cause:**
|
|
132
|
-
- Each test starts a new container
|
|
133
|
-
- Downloads create-sy from npm
|
|
134
|
-
- Runs npm install which downloads all syngrisi dependencies
|
|
135
|
-
|
|
136
|
-
**Current configuration:**
|
|
137
|
-
```typescript
|
|
138
|
-
// vitest.e2e.config.ts
|
|
139
|
-
testTimeout: 300_000, // 5 minutes per test
|
|
140
|
-
pool: 'forks',
|
|
141
|
-
poolOptions: {
|
|
142
|
-
forks: {
|
|
143
|
-
singleFork: true, // Sequential execution
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Test Results
|
|
149
|
-
|
|
150
|
-
### Passing Tests
|
|
151
|
-
|
|
152
|
-
| Test | Status | Duration |
|
|
153
|
-
|------|--------|----------|
|
|
154
|
-
| Node.js 18 environment verification | PASS | ~10s |
|
|
155
|
-
| Node.js 20 environment verification | PASS | ~10s |
|
|
156
|
-
| Node.js 22 environment verification | PASS | ~10s |
|
|
157
|
-
|
|
158
|
-
### Failing/Unstable Tests
|
|
159
|
-
|
|
160
|
-
| Test | Status | Issue |
|
|
161
|
-
|------|--------|-------|
|
|
162
|
-
| npm init sy@latest installs syngrisi | FAIL | Exit code 139 (SIGSEGV) |
|
|
163
|
-
| Installation with specific version | UNSTABLE | Container crashes |
|
|
164
|
-
| Full flow installation | UNSTABLE | Long timeout/crashes |
|
|
165
|
-
|
|
166
|
-
## What's Not Done
|
|
167
|
-
|
|
168
|
-
### 1. Stable npm Installation Tests
|
|
169
|
-
The tests that actually run `npm init sy@latest` inside containers are unstable due to:
|
|
170
|
-
- Container crashes (exit code 139)
|
|
171
|
-
- Long execution times
|
|
172
|
-
- Memory/resource issues
|
|
173
|
-
|
|
174
|
-
### 2. Local Package Testing
|
|
175
|
-
Currently tests download published `create-sy` from npm. A mechanism to test local unpublished changes would be beneficial:
|
|
176
|
-
- Could use `npm pack` + volume mount
|
|
177
|
-
- Or npm link approach inside containers
|
|
178
|
-
|
|
179
|
-
### 3. CI Integration
|
|
180
|
-
E2E tests are not yet integrated into CI pipeline:
|
|
181
|
-
- Apple Container CLI only works on macOS
|
|
182
|
-
- Would need self-hosted macOS runners
|
|
183
|
-
- Or alternative containerization approach for Linux CI
|
|
184
|
-
|
|
185
|
-
### 4. Security Validation Tests
|
|
186
|
-
Tests for invalid npmTag are written but not fully verified:
|
|
187
|
-
- `rejects dangerous characters in tag`
|
|
188
|
-
- `rejects tag with shell metacharacters`
|
|
189
|
-
|
|
190
|
-
## Recommendations
|
|
191
|
-
|
|
192
|
-
1. **Short term:** Mark npm installation e2e tests as `@slow` or `@unstable` and run them separately
|
|
193
|
-
2. **Medium term:** Investigate container memory/resource limits for stability
|
|
194
|
-
3. **Long term:** Consider Docker-based tests for CI compatibility
|
|
195
|
-
|
|
196
|
-
## File Structure
|
|
197
|
-
|
|
198
|
-
```
|
|
199
|
-
packages/create-sy/
|
|
200
|
-
├── e2e/
|
|
201
|
-
│ ├── container/
|
|
202
|
-
│ │ ├── Dockerfile.node
|
|
203
|
-
│ │ └── start-containers.sh
|
|
204
|
-
│ ├── fixtures/
|
|
205
|
-
│ │ └── container.fixture.ts
|
|
206
|
-
│ ├── tests/
|
|
207
|
-
│ │ ├── full-flow/
|
|
208
|
-
│ │ │ └── install-and-start.e2e.ts
|
|
209
|
-
│ │ ├── installation/
|
|
210
|
-
│ │ │ ├── npm-tag-invalid.e2e.ts
|
|
211
|
-
│ │ │ ├── npm-tag-latest.e2e.ts
|
|
212
|
-
│ │ │ └── npm-tag-specific.e2e.ts
|
|
213
|
-
│ │ └── node-versions/
|
|
214
|
-
│ │ ├── node18.e2e.ts
|
|
215
|
-
│ │ ├── node20.e2e.ts
|
|
216
|
-
│ │ └── node22.e2e.ts
|
|
217
|
-
│ ├── utils/
|
|
218
|
-
│ │ └── container-cli.ts
|
|
219
|
-
│ └── vitest.e2e.config.ts
|
|
220
|
-
├── src/
|
|
221
|
-
│ ├── utils.ts # Added validateNpmTag()
|
|
222
|
-
│ └── createSyngrisiProject.ts # Added --yes flag
|
|
223
|
-
└── tests/
|
|
224
|
-
└── *.test.ts # 24 unit tests (100% coverage)
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## Commands
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
# Run unit tests
|
|
231
|
-
npm test
|
|
232
|
-
|
|
233
|
-
# Run e2e tests (requires Apple Container CLI)
|
|
234
|
-
npm run test:e2e
|
|
235
|
-
|
|
236
|
-
# Run only environment verification tests (fast)
|
|
237
|
-
npx vitest run --config e2e/vitest.e2e.config.ts --testNamePattern "verifies Node.js"
|
|
238
|
-
|
|
239
|
-
# Build container images only
|
|
240
|
-
npm run e2e:setup
|
|
241
|
-
```
|
package/e2e/config.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
|
|
3
|
-
export const config = {
|
|
4
|
-
// Timeouts (in milliseconds)
|
|
5
|
-
containerStartTimeout: 60_000,
|
|
6
|
-
npmInstallTimeout: 180_000, // 3 minutes for npm install
|
|
7
|
-
serverStartTimeout: 60_000,
|
|
8
|
-
testTimeout: 300_000, // 5 minutes per test
|
|
9
|
-
|
|
10
|
-
// Paths
|
|
11
|
-
createSyRoot: resolve(import.meta.dirname, '..'),
|
|
12
|
-
e2eRoot: resolve(import.meta.dirname),
|
|
13
|
-
|
|
14
|
-
// Container settings
|
|
15
|
-
nodeVersions: ['18', '20', '22'] as const,
|
|
16
|
-
|
|
17
|
-
// npm registry
|
|
18
|
-
npmRegistry: process.env.NPM_REGISTRY || 'https://registry.npmjs.org',
|
|
19
|
-
} as const
|
|
20
|
-
|
|
21
|
-
export type NodeVersion = typeof config.nodeVersions[number]
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
-
|
|
6
|
-
# Colors
|
|
7
|
-
GREEN='\033[0;32m'
|
|
8
|
-
YELLOW='\033[1;33m'
|
|
9
|
-
RED='\033[0;31m'
|
|
10
|
-
NC='\033[0m'
|
|
11
|
-
|
|
12
|
-
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
13
|
-
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
14
|
-
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
15
|
-
|
|
16
|
-
check_container_cli() {
|
|
17
|
-
if ! command -v container &> /dev/null; then
|
|
18
|
-
log_error "Apple container CLI not found"
|
|
19
|
-
echo "See: https://github.com/apple/container"
|
|
20
|
-
exit 1
|
|
21
|
-
fi
|
|
22
|
-
log_info "Apple container CLI found"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
start_container_system() {
|
|
26
|
-
log_info "Starting container system service..."
|
|
27
|
-
# This fixes XPC connection errors
|
|
28
|
-
# See: https://github.com/apple/container/issues/699
|
|
29
|
-
container system start 2>/dev/null || true
|
|
30
|
-
log_info "Container system service started"
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
build_images() {
|
|
34
|
-
log_info "Building container images..."
|
|
35
|
-
|
|
36
|
-
log_info "Building Node 18 image..."
|
|
37
|
-
container build -t create-sy-node18 -f "$SCRIPT_DIR/Dockerfile.node18" "$SCRIPT_DIR"
|
|
38
|
-
|
|
39
|
-
log_info "Building Node 20 image..."
|
|
40
|
-
container build -t create-sy-node20 -f "$SCRIPT_DIR/Dockerfile.node20" "$SCRIPT_DIR"
|
|
41
|
-
|
|
42
|
-
log_info "Building Node 22 image..."
|
|
43
|
-
container build -t create-sy-node22 -f "$SCRIPT_DIR/Dockerfile.node22" "$SCRIPT_DIR"
|
|
44
|
-
|
|
45
|
-
log_info "All images built successfully"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
main() {
|
|
49
|
-
check_container_cli
|
|
50
|
-
start_container_system
|
|
51
|
-
|
|
52
|
-
if [ "$1" = "--build" ] || [ "$1" = "-b" ]; then
|
|
53
|
-
build_images
|
|
54
|
-
else
|
|
55
|
-
log_info "Skipping image build. Use --build to build images."
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
log_info "============================================"
|
|
59
|
-
log_info "E2E Infrastructure Ready!"
|
|
60
|
-
log_info "Available images:"
|
|
61
|
-
log_info " - create-sy-node18"
|
|
62
|
-
log_info " - create-sy-node20"
|
|
63
|
-
log_info " - create-sy-node22"
|
|
64
|
-
log_info "============================================"
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
main "$@"
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
# Colors
|
|
5
|
-
GREEN='\033[0;32m'
|
|
6
|
-
NC='\033[0m'
|
|
7
|
-
|
|
8
|
-
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
9
|
-
|
|
10
|
-
if ! command -v container &> /dev/null; then
|
|
11
|
-
log_info "Apple container CLI not found, skipping cleanup"
|
|
12
|
-
exit 0
|
|
13
|
-
fi
|
|
14
|
-
|
|
15
|
-
log_info "Stopping any running test containers..."
|
|
16
|
-
|
|
17
|
-
# Stop containers matching our naming pattern
|
|
18
|
-
for container_id in $(container list -q 2>/dev/null | grep -E "create-sy-test-" || true); do
|
|
19
|
-
log_info "Stopping container: $container_id"
|
|
20
|
-
container stop "$container_id" 2>/dev/null || true
|
|
21
|
-
container delete -f "$container_id" 2>/dev/null || true
|
|
22
|
-
done
|
|
23
|
-
|
|
24
|
-
log_info "Cleanup complete"
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isContainerCliAvailable,
|
|
3
|
-
containerRunCommand,
|
|
4
|
-
getNodeImage,
|
|
5
|
-
type NodeVersion,
|
|
6
|
-
type ExecResult,
|
|
7
|
-
} from '../utils/container-cli.js'
|
|
8
|
-
import { config } from '../config.js'
|
|
9
|
-
|
|
10
|
-
export interface ContainerTestContext {
|
|
11
|
-
isAvailable: boolean
|
|
12
|
-
runInNode: (
|
|
13
|
-
version: NodeVersion,
|
|
14
|
-
commands: string[],
|
|
15
|
-
options?: { env?: Record<string, string>; timeout?: number }
|
|
16
|
-
) => ExecResult
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Creates a container test context for running commands in Node containers
|
|
21
|
-
*/
|
|
22
|
-
export function createContainerContext(): ContainerTestContext {
|
|
23
|
-
const isAvailable = isContainerCliAvailable()
|
|
24
|
-
|
|
25
|
-
const runInNode = (
|
|
26
|
-
version: NodeVersion,
|
|
27
|
-
commands: string[],
|
|
28
|
-
options: { env?: Record<string, string>; timeout?: number } = {}
|
|
29
|
-
): ExecResult => {
|
|
30
|
-
if (!isAvailable) {
|
|
31
|
-
return {
|
|
32
|
-
stdout: '',
|
|
33
|
-
stderr: 'Apple Container CLI not available',
|
|
34
|
-
exitCode: 1,
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const image = getNodeImage(version)
|
|
39
|
-
const timeout = options.timeout || config.npmInstallTimeout
|
|
40
|
-
|
|
41
|
-
return containerRunCommand(image, commands, {
|
|
42
|
-
env: {
|
|
43
|
-
...options.env,
|
|
44
|
-
NPM_CONFIG_REGISTRY: config.npmRegistry,
|
|
45
|
-
},
|
|
46
|
-
timeout,
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
isAvailable,
|
|
52
|
-
runInNode,
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Singleton context for tests
|
|
57
|
-
let containerContext: ContainerTestContext | null = null
|
|
58
|
-
|
|
59
|
-
export function getContainerContext(): ContainerTestContext {
|
|
60
|
-
if (!containerContext) {
|
|
61
|
-
containerContext = createContainerContext()
|
|
62
|
-
}
|
|
63
|
-
return containerContext
|
|
64
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { execSync, spawn, ChildProcess } from 'node:child_process'
|
|
2
|
-
|
|
3
|
-
export interface ContainerRunOptions {
|
|
4
|
-
name: string
|
|
5
|
-
image: string
|
|
6
|
-
env?: Record<string, string>
|
|
7
|
-
volumes?: string[]
|
|
8
|
-
workdir?: string
|
|
9
|
-
detached?: boolean
|
|
10
|
-
rm?: boolean
|
|
11
|
-
command?: string[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ExecResult {
|
|
15
|
-
stdout: string
|
|
16
|
-
stderr: string
|
|
17
|
-
exitCode: number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if Apple Container CLI is available
|
|
22
|
-
*/
|
|
23
|
-
export function isContainerCliAvailable(): boolean {
|
|
24
|
-
try {
|
|
25
|
-
execSync('container --version', { stdio: 'ignore' })
|
|
26
|
-
return true
|
|
27
|
-
} catch {
|
|
28
|
-
return false
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Run a container with specified options
|
|
34
|
-
*/
|
|
35
|
-
export function containerRun(options: ContainerRunOptions): string {
|
|
36
|
-
const args: string[] = ['run']
|
|
37
|
-
|
|
38
|
-
if (options.detached) args.push('-d')
|
|
39
|
-
if (options.rm) args.push('--rm')
|
|
40
|
-
if (options.name) args.push('--name', options.name)
|
|
41
|
-
if (options.workdir) args.push('-w', options.workdir)
|
|
42
|
-
|
|
43
|
-
if (options.env) {
|
|
44
|
-
Object.entries(options.env).forEach(([k, v]) => {
|
|
45
|
-
args.push('-e', `${k}=${v}`)
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
options.volumes?.forEach(v => args.push('-v', v))
|
|
50
|
-
|
|
51
|
-
args.push(options.image)
|
|
52
|
-
|
|
53
|
-
if (options.command) {
|
|
54
|
-
args.push(...options.command)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return execSync(`container ${args.join(' ')}`, { encoding: 'utf-8' }).trim()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Execute a command in a running container
|
|
62
|
-
*/
|
|
63
|
-
export function containerExec(
|
|
64
|
-
containerName: string,
|
|
65
|
-
command: string[],
|
|
66
|
-
options: { timeout?: number } = {}
|
|
67
|
-
): ExecResult {
|
|
68
|
-
const timeout = options.timeout || 120_000 // 2 minutes default
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const shellCommand = command.join(' ')
|
|
72
|
-
const stdout = execSync(
|
|
73
|
-
`container exec ${containerName} sh -c '${shellCommand}'`,
|
|
74
|
-
{
|
|
75
|
-
encoding: 'utf-8',
|
|
76
|
-
timeout,
|
|
77
|
-
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
78
|
-
}
|
|
79
|
-
)
|
|
80
|
-
return { stdout, stderr: '', exitCode: 0 }
|
|
81
|
-
} catch (error: any) {
|
|
82
|
-
return {
|
|
83
|
-
stdout: error.stdout || '',
|
|
84
|
-
stderr: error.stderr || error.message,
|
|
85
|
-
exitCode: error.status || 1,
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Run a command in a new container and return the result
|
|
92
|
-
*/
|
|
93
|
-
export function containerRunCommand(
|
|
94
|
-
image: string,
|
|
95
|
-
command: string[],
|
|
96
|
-
options: {
|
|
97
|
-
env?: Record<string, string>
|
|
98
|
-
timeout?: number
|
|
99
|
-
} = {}
|
|
100
|
-
): ExecResult {
|
|
101
|
-
const timeout = options.timeout || 180_000 // 3 minutes default
|
|
102
|
-
|
|
103
|
-
const args: string[] = ['run', '--rm']
|
|
104
|
-
|
|
105
|
-
if (options.env) {
|
|
106
|
-
Object.entries(options.env).forEach(([k, v]) => {
|
|
107
|
-
args.push('-e', `${k}=${v}`)
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
args.push(image)
|
|
112
|
-
const shellCommand = command.join(' && ')
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const stdout = execSync(`container ${args.join(' ')} sh -c '${shellCommand}'`, {
|
|
116
|
-
encoding: 'utf-8',
|
|
117
|
-
timeout,
|
|
118
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
119
|
-
})
|
|
120
|
-
return { stdout, stderr: '', exitCode: 0 }
|
|
121
|
-
} catch (error: any) {
|
|
122
|
-
return {
|
|
123
|
-
stdout: error.stdout || '',
|
|
124
|
-
stderr: error.stderr || error.message,
|
|
125
|
-
exitCode: error.status || 1,
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Delete a container
|
|
132
|
-
*/
|
|
133
|
-
export function containerDelete(name: string, force = true): void {
|
|
134
|
-
try {
|
|
135
|
-
execSync(`container delete ${force ? '-f' : ''} ${name}`, { stdio: 'ignore' })
|
|
136
|
-
} catch {
|
|
137
|
-
// Ignore errors - container may not exist
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Stop a container
|
|
143
|
-
*/
|
|
144
|
-
export function containerStop(name: string): void {
|
|
145
|
-
try {
|
|
146
|
-
execSync(`container stop ${name}`, { stdio: 'ignore' })
|
|
147
|
-
} catch {
|
|
148
|
-
// Ignore errors
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Get container logs
|
|
154
|
-
*/
|
|
155
|
-
export function containerLogs(name: string): string {
|
|
156
|
-
try {
|
|
157
|
-
return execSync(`container logs ${name}`, { encoding: 'utf-8' })
|
|
158
|
-
} catch {
|
|
159
|
-
return ''
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* List running containers
|
|
165
|
-
*/
|
|
166
|
-
export function containerList(): string[] {
|
|
167
|
-
try {
|
|
168
|
-
const output = execSync('container list -q', { encoding: 'utf-8' })
|
|
169
|
-
return output.split('\n').filter(Boolean)
|
|
170
|
-
} catch {
|
|
171
|
-
return []
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export type NodeVersion = '18' | '20' | '22'
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Get image name for a Node.js version
|
|
179
|
-
*/
|
|
180
|
-
export function getNodeImage(version: NodeVersion): string {
|
|
181
|
-
return `create-sy-node${version}`
|
|
182
|
-
}
|
package/e2e/vitest.e2e.config.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config'
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
include: ['e2e/tests/**/*.e2e.ts'],
|
|
6
|
-
exclude: ['node_modules', 'build'],
|
|
7
|
-
testTimeout: 300_000, // 5 minutes per test
|
|
8
|
-
hookTimeout: 120_000, // 2 minutes for hooks
|
|
9
|
-
pool: 'forks',
|
|
10
|
-
poolOptions: {
|
|
11
|
-
forks: {
|
|
12
|
-
singleFork: true, // Sequential execution for container tests
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
reporters: ['verbose'],
|
|
16
|
-
passWithNoTests: true,
|
|
17
|
-
env: {
|
|
18
|
-
E2E_MODE: 'true',
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
})
|
package/typedoc.config.cjs
DELETED
package/vitest.config.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/// <reference types="vitest" />
|
|
2
|
-
import { defineConfig } from 'vite'
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
test: {
|
|
6
|
-
include: ['tests/**/*.test.ts'],
|
|
7
|
-
/**
|
|
8
|
-
* not to ESM ported packages
|
|
9
|
-
*/
|
|
10
|
-
exclude: [
|
|
11
|
-
'build', '.idea', '.git', '.cache',
|
|
12
|
-
'**/node_modules/**', '__mocks__'
|
|
13
|
-
],
|
|
14
|
-
coverage: {
|
|
15
|
-
provider: 'v8',
|
|
16
|
-
enabled: true,
|
|
17
|
-
exclude: ['**/build/**', '__mocks__', '**/*.test.ts', 'src/utils*'],
|
|
18
|
-
lines: 97,
|
|
19
|
-
functions: 80,
|
|
20
|
-
branches: 70,
|
|
21
|
-
statements: 97
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
})
|