task-pipeliner 0.2.14 → 0.2.16
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/README.ko.md +114 -2
- package/README.md +115 -2
- package/dist/index.cjs +53 -49
- package/dist/index.d.ts +2 -2
- package/dist/task-pipeliner-rs.node +0 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 조건 기반 작업 파이프라인 실행기로 아름다운 CLI 출력을 제공합니다
|
|
4
4
|
|
|
5
|
-
**버전:** 0.2.
|
|
5
|
+
**버전:** 0.2.16
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
|
|
26
26
|
- **변수 치환** - 워크플로우 전반에서 `{{variables}}` 사용
|
|
27
27
|
|
|
28
|
+
- **프로필** - 미리 정의한 변수로 비대화형 실행 (`tp run --profile <name>`); 프로필에 설정된 변수에 대해서는 choose/prompt 단계 생략
|
|
29
|
+
|
|
28
30
|
- **실행 히스토리** - 상세한 단계별 기록으로 과거 워크플로우 실행 추적 및 검토
|
|
29
31
|
|
|
30
32
|
## 리소스
|
|
@@ -47,6 +49,8 @@
|
|
|
47
49
|
```bash
|
|
48
50
|
tp run workflow.yaml # 워크플로우 실행
|
|
49
51
|
tp run # 가장 가까운 tp 디렉토리에서 워크플로우 선택하여 실행
|
|
52
|
+
tp run workflow.yaml --profile Test # 프로필로 실행 (프로필에 설정된 변수는 choose/prompt 생략)
|
|
53
|
+
tp run workflow.yaml -p Test # 프로필 짧은 형식
|
|
50
54
|
tp run workflow.yaml --silent # 사일런트 모드로 실행 (모든 콘솔 출력 억제)
|
|
51
55
|
tp run workflow.yaml -s # 사일런트 모드 짧은 형식
|
|
52
56
|
```
|
|
@@ -293,6 +297,15 @@ baseDir: ./ # 선택사항: 명령 실행 기본 디
|
|
|
293
297
|
# - 상대 경로: YAML 파일 위치 기준으로 해석
|
|
294
298
|
# - 절대 경로: 그대로 사용
|
|
295
299
|
# - 생략 시: 현재 작업 디렉토리 사용
|
|
300
|
+
shell: # 선택사항: 모든 run 명령의 전역 쉘 설정
|
|
301
|
+
- bash # - 첫 번째 요소: 쉘 프로그램 (bash, zsh, sh 등)
|
|
302
|
+
- -lc # - 나머지: 쉘 인자 (-c, -lc 등)
|
|
303
|
+
# - 생략 시: 플랫폼 기본 쉘 사용
|
|
304
|
+
profiles: # 선택사항: tp run --profile <name>용 미리 설정된 변수
|
|
305
|
+
- name: Test # - name: 프로필 이름
|
|
306
|
+
var: # - var: 키-값 맵 ({{variable}} 치환 및 choose/prompt 생략에 사용)
|
|
307
|
+
mode: "dev"
|
|
308
|
+
label: "test-label"
|
|
296
309
|
|
|
297
310
|
steps: # 필수: 실행할 단계 배열
|
|
298
311
|
- some-step-1
|
|
@@ -309,6 +322,13 @@ steps: # 필수: 실행할 단계 배열
|
|
|
309
322
|
// - 상대 경로: JSON 파일 위치 기준으로 해석
|
|
310
323
|
// - 절대 경로: 그대로 사용
|
|
311
324
|
// - 생략 시: 현재 작업 디렉토리 사용
|
|
325
|
+
"shell": ["bash", "-lc"], // 선택사항: 모든 run 명령의 전역 쉘 설정
|
|
326
|
+
// - 첫 번째 요소: 쉘 프로그램
|
|
327
|
+
// - 나머지: 쉘 인자
|
|
328
|
+
// - 생략 시: 플랫폼 기본 쉘 사용
|
|
329
|
+
"profiles": [ // 선택사항: tp run --profile <name>용 미리 설정된 변수
|
|
330
|
+
{ "name": "Test", "var": { "mode": "dev", "label": "test-label" } }
|
|
331
|
+
],
|
|
312
332
|
"steps": [ // 필수: 실행할 단계 배열
|
|
313
333
|
{ /* some-step-1 */ },
|
|
314
334
|
{ /* some-step-2 */ }
|
|
@@ -334,6 +354,53 @@ steps: # 필수: 실행할 단계 배열
|
|
|
334
354
|
baseDir: /app/frontend # 절대 경로
|
|
335
355
|
```
|
|
336
356
|
|
|
357
|
+
#### `shell` (선택)
|
|
358
|
+
- **타입**: `string`의 `array`
|
|
359
|
+
- **설명**: 워크플로우 내 모든 `run` 명령에 대한 전역 쉘 설정
|
|
360
|
+
- **형식**: `[프로그램, ...인자]` - 첫 번째 요소는 쉘 프로그램, 나머지는 인자
|
|
361
|
+
- **우선순위**: 스텝별 `shell` > 워크플로우 `shell` > 사용자의 현재 쉘
|
|
362
|
+
- **사용자의 현재 쉘** (생략 시):
|
|
363
|
+
- **Linux/macOS**: `$SHELL` 환경변수 사용 (예: `/bin/zsh`, `/bin/bash`)
|
|
364
|
+
- **Windows**: `%COMSPEC%` 사용 (일반적으로 `cmd.exe`)
|
|
365
|
+
- **동작**: `tp run`을 실행한 쉘 환경과 동일한 환경에서 명령 실행
|
|
366
|
+
- **예제**:
|
|
367
|
+
```yaml
|
|
368
|
+
# Unix/Linux/macOS
|
|
369
|
+
shell: [bash, -lc] # bash 로그인 쉘 사용
|
|
370
|
+
shell: [zsh, -c] # zsh 사용
|
|
371
|
+
shell: [sh, -c] # sh 사용 (POSIX)
|
|
372
|
+
|
|
373
|
+
# Windows
|
|
374
|
+
shell: [cmd, /c] # 명령 프롬프트
|
|
375
|
+
shell: [powershell, -Command] # Windows PowerShell
|
|
376
|
+
shell: [pwsh, -Command] # PowerShell Core
|
|
377
|
+
```
|
|
378
|
+
- **크로스 플랫폼 예시**:
|
|
379
|
+
- **Linux/macOS**: `[bash, -lc]`, `[zsh, -c]`, `[/bin/bash, -c]`
|
|
380
|
+
- **Windows**: `[cmd, /c]`, `[powershell, -Command]`, `[pwsh, -Command]`
|
|
381
|
+
- **Git Bash (Windows)**: `[bash, -c]`
|
|
382
|
+
- **WSL**: `[wsl, bash, -c]` 또는 `wsl` 명령 직접 사용
|
|
383
|
+
|
|
384
|
+
#### `profiles` (선택)
|
|
385
|
+
- **타입**: `{ name: string, var: Record<string, string> }` 의 `array`
|
|
386
|
+
- **설명**: 비대화형 실행을 위한 이름 붙은 변수 세트. `tp run --profile <name>` 과 함께 사용.
|
|
387
|
+
- **동작**: 프로필을 사용하면, 프로필에 이미 설정된 변수에 값을 저장하는 **choose** 또는 **prompt** 단계는 생략되고, 프로필 값이 `{{variable}}` 치환 및 조건에 사용됩니다.
|
|
388
|
+
- **예제**:
|
|
389
|
+
```yaml
|
|
390
|
+
profiles:
|
|
391
|
+
- name: Test
|
|
392
|
+
var:
|
|
393
|
+
mode: "dev"
|
|
394
|
+
label: "test-label"
|
|
395
|
+
- name: Prod
|
|
396
|
+
var:
|
|
397
|
+
mode: "prod"
|
|
398
|
+
label: "prod-label"
|
|
399
|
+
```
|
|
400
|
+
```bash
|
|
401
|
+
tp run workflow.yaml --profile Test # Test 프로필 변수 사용; mode, label에 대한 choose/prompt 생략
|
|
402
|
+
```
|
|
403
|
+
|
|
337
404
|
#### `steps` (필수)
|
|
338
405
|
- **타입**: `Step` 객체의 `array`
|
|
339
406
|
- **설명**: 순차적으로 실행할 단계 목록
|
|
@@ -355,6 +422,7 @@ steps: # 필수: 실행할 단계 배열
|
|
|
355
422
|
when?: <condition> # 선택: 조건이 충족될 때만 실행
|
|
356
423
|
timeout?: <number> # 선택: 타임아웃 (초 단위)
|
|
357
424
|
retry?: <number> # 선택: 실패 시 재시도 횟수 (기본값: 0)
|
|
425
|
+
shell?: <array> # 선택: 쉘 설정 (workflow.shell 오버라이드)
|
|
358
426
|
continue?: <bool> # 선택: 이 스텝 이후 다음 스텝으로 진행할지 여부 (성공/실패 무관)
|
|
359
427
|
onError?: # 선택: 에러 처리 동작
|
|
360
428
|
run: <command> # 메인 run 명령이 실패했을 때 실행할 대체 명령 (사이드 이펙트)
|
|
@@ -368,6 +436,7 @@ steps: # 필수: 실행할 단계 배열
|
|
|
368
436
|
- `when` (선택): `Condition` - 실행 전 확인할 조건
|
|
369
437
|
- `timeout` (선택): `number` - 최대 실행 시간 (초 단위). 이 시간을 초과하면 명령이 종료됩니다.
|
|
370
438
|
- `retry` (선택): `number` - 실패 시 재시도 횟수 (기본값: 0, 재시도 없음)
|
|
439
|
+
- `shell` (선택): `string`의 `array` - 이 스텝의 쉘 설정. 워크플로우의 전역 `shell`을 오버라이드합니다. 형식: `[프로그램, ...인자]`. 예: `[bash, -lc]`, `[zsh, -c]`.
|
|
371
440
|
- `continue` (선택): `boolean` - 이 스텝 완료 후 다음 스텝으로 진행할지 여부를 제어합니다 (성공/실패와 무관).
|
|
372
441
|
- `continue: true` - 항상 다음 스텝으로 진행 (이 스텝이 실패해도)
|
|
373
442
|
- `continue: false` - 항상 워크플로우 중단 (이 스텝이 성공해도)
|
|
@@ -432,6 +501,18 @@ steps:
|
|
|
432
501
|
continue: true
|
|
433
502
|
onError:
|
|
434
503
|
run: echo "Type check failed, but continuing..."
|
|
504
|
+
|
|
505
|
+
# 커스텀 쉘 사용 (스텝별)
|
|
506
|
+
- run: echo $SHELL
|
|
507
|
+
shell:
|
|
508
|
+
- zsh
|
|
509
|
+
- -c
|
|
510
|
+
|
|
511
|
+
# bash 로그인 쉘 사용
|
|
512
|
+
- run: source ~/.bashrc && echo "프로필 로드됨"
|
|
513
|
+
shell:
|
|
514
|
+
- bash
|
|
515
|
+
- -lc
|
|
435
516
|
```
|
|
436
517
|
|
|
437
518
|
**동작:**
|
|
@@ -984,11 +1065,36 @@ when:
|
|
|
984
1065
|
|
|
985
1066
|
### 변수 치환
|
|
986
1067
|
|
|
987
|
-
변수는 `{{variable}}` 문법을 사용하여 명령에서 사용할 수 있습니다.
|
|
1068
|
+
변수는 `{{variable}}` 문법을 사용하여 명령에서 사용할 수 있습니다. 선택적으로 공백을 사용할 수 있습니다: `{{var}}`, `{{ var }}`, `{{ var }}` 모두 작동합니다.
|
|
988
1069
|
|
|
989
1070
|
**문법:**
|
|
990
1071
|
```yaml
|
|
991
1072
|
run: echo "{{variableName}}"
|
|
1073
|
+
# 또는 선택적으로 공백 사용
|
|
1074
|
+
run: echo "{{ variableName }}"
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
**⚠️ 중요: YAML 문법 규칙**
|
|
1078
|
+
|
|
1079
|
+
명령어에서 `{{variable}}`을 사용할 때, 파싱 오류를 방지하기 위해 다음 규칙을 따르세요:
|
|
1080
|
+
|
|
1081
|
+
✅ **안전한 패턴:**
|
|
1082
|
+
```yaml
|
|
1083
|
+
# 단어로 시작 (따옴표 불필요)
|
|
1084
|
+
- run: echo "Building {{version}}..."
|
|
1085
|
+
- run: npm run build --version={{version}}
|
|
1086
|
+
|
|
1087
|
+
# 전체 명령어를 작은따옴표로 감싸기
|
|
1088
|
+
- run: 'echo "Selected: {{mode}}"'
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
❌ **문제가 되는 패턴:**
|
|
1092
|
+
```yaml
|
|
1093
|
+
# 금지: 따옴표 + 변수 앞 콜론
|
|
1094
|
+
- run: echo "mode: {{mode}}" # ❌ YAML 파싱 에러!
|
|
1095
|
+
|
|
1096
|
+
# 해결: 전체 명령어를 작은따옴표로 감싸기
|
|
1097
|
+
- run: 'echo "mode: {{mode}}"' # ✅ 정상 작동
|
|
992
1098
|
```
|
|
993
1099
|
|
|
994
1100
|
**예제:**
|
|
@@ -1077,6 +1183,10 @@ steps:
|
|
|
1077
1183
|
- run: npm run test:integration
|
|
1078
1184
|
- run: npm run lint
|
|
1079
1185
|
|
|
1186
|
+
# 6.5. 스텝별 쉘 오버라이드
|
|
1187
|
+
- run: echo "zsh로 실행"
|
|
1188
|
+
shell: [zsh, -c] # 이 스텝만 워크플로우 쉘 오버라이드
|
|
1189
|
+
|
|
1080
1190
|
# 7. 파일 존재 확인
|
|
1081
1191
|
- when:
|
|
1082
1192
|
file: ./test-results
|
|
@@ -1229,6 +1339,7 @@ tp history remove-all -y # 확인 건너뛰기
|
|
|
1229
1339
|
- **`file-checks.yaml`** - 파일 존재 확인
|
|
1230
1340
|
- **`prompt.yaml`** - 사용자 입력 프롬프트
|
|
1231
1341
|
- **`variables.yaml`** - 변수 치환 예제
|
|
1342
|
+
- **`profiles-example.yaml`** - 비대화형 실행용 프로필 (`tp run --profile <name>`)
|
|
1232
1343
|
|
|
1233
1344
|
### JSON 예제
|
|
1234
1345
|
|
|
@@ -1240,6 +1351,7 @@ tp history remove-all -y # 확인 건너뛰기
|
|
|
1240
1351
|
- **`conditions.json`** - 조건 평가 예제
|
|
1241
1352
|
- **`prompt.json`** - 사용자 입력 프롬프트
|
|
1242
1353
|
- **`variables.json`** - 변수 치환 예제
|
|
1354
|
+
- **`profiles-example.json`** - 비대화형 실행용 프로필 (`tp run --profile <name>`)
|
|
1243
1355
|
|
|
1244
1356
|
**참고:** YAML과 JSON 형식 모두 완전히 지원됩니다. 선호하는 형식을 선택하세요 - 가독성을 위해 YAML, 프로그래밍 방식 생성을 위해 JSON.
|
|
1245
1357
|
- **`variables.yaml`** - 변수 사용 예제
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> A powerful, condition-based task pipeline runner with beautiful CLI output
|
|
4
4
|
|
|
5
|
-
**Version:** 0.2.
|
|
5
|
+
**Version:** 0.2.16
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
|
|
26
26
|
- **Variable substitution** - Use `{{variables}}` throughout your workflows
|
|
27
27
|
|
|
28
|
+
- **Profiles** - Run workflows non-interactively with pre-set variables (`tp run --profile <name>`); choose/prompt steps are skipped when the variable is set in the profile
|
|
29
|
+
|
|
28
30
|
- **Execution history** - Track and review past workflow executions with detailed step-by-step records
|
|
29
31
|
|
|
30
32
|
## 🔗 Resources
|
|
@@ -47,6 +49,8 @@
|
|
|
47
49
|
```bash
|
|
48
50
|
tp run workflow.yaml # Run a workflow
|
|
49
51
|
tp run # Select and run a workflow from nearest tp directory
|
|
52
|
+
tp run workflow.yaml --profile Test # Run with profile (skip choose/prompt for variables set in profile)
|
|
53
|
+
tp run workflow.yaml -p Test # Short form for profile
|
|
50
54
|
tp run workflow.yaml --silent # Run in silent mode (suppress all console output)
|
|
51
55
|
tp run workflow.yaml -s # Short form for silent mode
|
|
52
56
|
```
|
|
@@ -293,6 +297,15 @@ baseDir: ./ # Optional: Base directory for command ex
|
|
|
293
297
|
# - Relative path: resolved from YAML file location
|
|
294
298
|
# - Absolute path: used as-is
|
|
295
299
|
# - If omitted: uses current working directory
|
|
300
|
+
shell: # Optional: Global shell configuration for all run commands
|
|
301
|
+
- bash # - First element: shell program (bash, zsh, sh, etc.)
|
|
302
|
+
- -lc # - Rest: shell arguments (-c, -lc, etc.)
|
|
303
|
+
# - If omitted: uses platform default shell
|
|
304
|
+
profiles: # Optional: Pre-set variables for tp run --profile <name>
|
|
305
|
+
- name: Test # - name: profile name
|
|
306
|
+
var: # - var: key-value map (used for {{variable}} and to skip choose/prompt)
|
|
307
|
+
mode: "dev"
|
|
308
|
+
label: "test-label"
|
|
296
309
|
|
|
297
310
|
steps: # Required: Array of steps to execute
|
|
298
311
|
- some-step-1
|
|
@@ -309,6 +322,13 @@ steps: # Required: Array of steps to execute
|
|
|
309
322
|
// - Relative path: resolved from JSON file location
|
|
310
323
|
// - Absolute path: used as-is
|
|
311
324
|
// - If omitted: uses current working directory
|
|
325
|
+
"shell": ["bash", "-lc"], // Optional: Global shell configuration for all run commands
|
|
326
|
+
// - First element: shell program
|
|
327
|
+
// - Rest: shell arguments
|
|
328
|
+
// - If omitted: uses platform default shell
|
|
329
|
+
"profiles": [ // Optional: Pre-set variables for tp run --profile <name>
|
|
330
|
+
{ "name": "Test", "var": { "mode": "dev", "label": "test-label" } }
|
|
331
|
+
],
|
|
312
332
|
"steps": [ // Required: Array of steps to execute
|
|
313
333
|
{ /* some-step-1 */ },
|
|
314
334
|
{ /* some-step-2 */ }
|
|
@@ -334,6 +354,53 @@ steps: # Required: Array of steps to execute
|
|
|
334
354
|
baseDir: /app/frontend # Absolute path
|
|
335
355
|
```
|
|
336
356
|
|
|
357
|
+
#### `shell` (optional)
|
|
358
|
+
- **Type**: `array` of `string`
|
|
359
|
+
- **Description**: Global shell configuration for all `run` commands in the workflow
|
|
360
|
+
- **Format**: `[program, ...args]` - First element is the shell program, rest are arguments
|
|
361
|
+
- **Priority**: Step-level `shell` > Workflow-level `shell` > User's current shell
|
|
362
|
+
- **User's current shell** (when omitted):
|
|
363
|
+
- **Linux/macOS**: Uses `$SHELL` environment variable (e.g., `/bin/zsh`, `/bin/bash`)
|
|
364
|
+
- **Windows**: Uses `%COMSPEC%` (typically `cmd.exe`)
|
|
365
|
+
- **Behavior**: Commands run in the same shell environment as where you execute `tp run`
|
|
366
|
+
- **Example**:
|
|
367
|
+
```yaml
|
|
368
|
+
# Unix/Linux/macOS
|
|
369
|
+
shell: [bash, -lc] # Use bash login shell
|
|
370
|
+
shell: [zsh, -c] # Use zsh
|
|
371
|
+
shell: [sh, -c] # Use sh (POSIX)
|
|
372
|
+
|
|
373
|
+
# Windows
|
|
374
|
+
shell: [cmd, /c] # Command Prompt
|
|
375
|
+
shell: [powershell, -Command] # Windows PowerShell
|
|
376
|
+
shell: [pwsh, -Command] # PowerShell Core
|
|
377
|
+
```
|
|
378
|
+
- **Cross-platform examples**:
|
|
379
|
+
- **Linux/macOS**: `[bash, -lc]`, `[zsh, -c]`, `[/bin/bash, -c]`
|
|
380
|
+
- **Windows**: `[cmd, /c]`, `[powershell, -Command]`, `[pwsh, -Command]`
|
|
381
|
+
- **Git Bash (Windows)**: `[bash, -c]`
|
|
382
|
+
- **WSL**: `[wsl, bash, -c]` or use `wsl` command directly
|
|
383
|
+
|
|
384
|
+
#### `profiles` (optional)
|
|
385
|
+
- **Type**: `array` of `{ name: string, var: Record<string, string> }`
|
|
386
|
+
- **Description**: Named sets of variables for non-interactive runs. Use with `tp run --profile <name>`.
|
|
387
|
+
- **Behavior**: When a profile is used, any **choose** or **prompt** step that stores into a variable already set in the profile is skipped; the profile value is used for `{{variable}}` substitution and conditions.
|
|
388
|
+
- **Example**:
|
|
389
|
+
```yaml
|
|
390
|
+
profiles:
|
|
391
|
+
- name: Test
|
|
392
|
+
var:
|
|
393
|
+
mode: "dev"
|
|
394
|
+
label: "test-label"
|
|
395
|
+
- name: Prod
|
|
396
|
+
var:
|
|
397
|
+
mode: "prod"
|
|
398
|
+
label: "prod-label"
|
|
399
|
+
```
|
|
400
|
+
```bash
|
|
401
|
+
tp run workflow.yaml --profile Test # Uses Test profile variables; choose/prompt for mode, label are skipped
|
|
402
|
+
```
|
|
403
|
+
|
|
337
404
|
#### `steps` (required)
|
|
338
405
|
- **Type**: `array` of `Step` objects
|
|
339
406
|
- **Description**: List of steps to execute sequentially
|
|
@@ -355,6 +422,7 @@ Execute a shell command.
|
|
|
355
422
|
when?: <condition> # Optional: Execute only if condition is met
|
|
356
423
|
timeout?: <number> # Optional: Timeout in seconds
|
|
357
424
|
retry?: <number> # Optional: Number of retries on failure (default: 0)
|
|
425
|
+
shell?: <array> # Optional: Shell configuration (overrides workflow.shell)
|
|
358
426
|
continue?: <bool> # Optional: Continue to next step after this step completes (regardless of success/failure)
|
|
359
427
|
onError?: # Optional: Error handling behavior
|
|
360
428
|
run: <command> # Fallback command when main run command fails (side effect)
|
|
@@ -368,6 +436,7 @@ Execute a shell command.
|
|
|
368
436
|
- `when` (optional): `Condition` - Condition to check before execution
|
|
369
437
|
- `timeout` (optional): `number` - Maximum execution time in seconds. Command will be killed if it exceeds this time.
|
|
370
438
|
- `retry` (optional): `number` - Number of retry attempts if command fails (default: 0, meaning no retry)
|
|
439
|
+
- `shell` (optional): `array` of `string` - Shell configuration for this step. Overrides workflow's global `shell`. Format: `[program, ...args]`. Example: `[bash, -lc]`, `[zsh, -c]`.
|
|
371
440
|
- `continue` (optional): `boolean` - Controls whether to proceed to the next step after this step completes, regardless of success or failure.
|
|
372
441
|
- `continue: true` - Always proceed to the next step (even if this step fails)
|
|
373
442
|
- `continue: false` - Always stop the workflow after this step (even if this step succeeds)
|
|
@@ -432,6 +501,18 @@ steps:
|
|
|
432
501
|
continue: true
|
|
433
502
|
onError:
|
|
434
503
|
run: echo "Type check failed, but continuing..."
|
|
504
|
+
|
|
505
|
+
# Command with custom shell (step-level)
|
|
506
|
+
- run: echo $SHELL
|
|
507
|
+
shell:
|
|
508
|
+
- zsh
|
|
509
|
+
- -c
|
|
510
|
+
|
|
511
|
+
# Command with bash login shell
|
|
512
|
+
- run: source ~/.bashrc && echo "Loaded profile"
|
|
513
|
+
shell:
|
|
514
|
+
- bash
|
|
515
|
+
- -lc
|
|
435
516
|
```
|
|
436
517
|
|
|
437
518
|
**Behavior:**
|
|
@@ -984,11 +1065,36 @@ Nest conditions to create complex logic.
|
|
|
984
1065
|
|
|
985
1066
|
### Variable Substitution
|
|
986
1067
|
|
|
987
|
-
Variables can be used in commands using the `{{variable}}` syntax.
|
|
1068
|
+
Variables can be used in commands using the `{{variable}}` syntax. Optional whitespace is supported: `{{var}}`, `{{ var }}`, `{{ var }}` all work.
|
|
988
1069
|
|
|
989
1070
|
**Syntax:**
|
|
990
1071
|
```yaml
|
|
991
1072
|
run: echo "{{variableName}}"
|
|
1073
|
+
# or with optional spaces
|
|
1074
|
+
run: echo "{{ variableName }}"
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
**⚠️ Important YAML Syntax Rules:**
|
|
1078
|
+
|
|
1079
|
+
When using `{{variable}}` in commands, follow these rules to avoid parsing errors:
|
|
1080
|
+
|
|
1081
|
+
✅ **Safe patterns:**
|
|
1082
|
+
```yaml
|
|
1083
|
+
# Start with a word (no quotes needed)
|
|
1084
|
+
- run: echo "Building {{version}}..."
|
|
1085
|
+
- run: npm run build --version={{version}}
|
|
1086
|
+
|
|
1087
|
+
# Wrap entire command in single quotes
|
|
1088
|
+
- run: 'echo "Selected: {{mode}}"'
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
❌ **Problematic patterns:**
|
|
1092
|
+
```yaml
|
|
1093
|
+
# DO NOT: quotes + colons before variables
|
|
1094
|
+
- run: echo "mode: {{mode}}" # ❌ YAML parsing error!
|
|
1095
|
+
|
|
1096
|
+
# FIX: Wrap entire command in single quotes
|
|
1097
|
+
- run: 'echo "mode: {{mode}}"' # ✅ Works correctly
|
|
992
1098
|
```
|
|
993
1099
|
|
|
994
1100
|
**Examples:**
|
|
@@ -1026,6 +1132,7 @@ A complete example demonstrating all features:
|
|
|
1026
1132
|
```yaml
|
|
1027
1133
|
name: Complete Workflow Example
|
|
1028
1134
|
baseDir: ./
|
|
1135
|
+
shell: [bash, -c] # Optional: Use bash for all steps (default: user's current shell)
|
|
1029
1136
|
|
|
1030
1137
|
steps:
|
|
1031
1138
|
# 1. Simple command
|
|
@@ -1077,6 +1184,10 @@ steps:
|
|
|
1077
1184
|
- run: npm run test:integration
|
|
1078
1185
|
- run: npm run lint
|
|
1079
1186
|
|
|
1187
|
+
# 6.5. Step-level shell override
|
|
1188
|
+
- run: echo "Running with zsh"
|
|
1189
|
+
shell: [zsh, -c] # Override workflow shell for this step only
|
|
1190
|
+
|
|
1080
1191
|
# 7. File existence check
|
|
1081
1192
|
- when:
|
|
1082
1193
|
file: ./test-results
|
|
@@ -1229,6 +1340,7 @@ Check out `examples/yaml-examples/` for YAML workflow examples:
|
|
|
1229
1340
|
- **`file-checks.yaml`** - File existence checks
|
|
1230
1341
|
- **`prompt.yaml`** - User input prompts
|
|
1231
1342
|
- **`variables.yaml`** - Variable substitution examples
|
|
1343
|
+
- **`profiles-example.yaml`** - Profiles for non-interactive runs (`tp run --profile <name>`)
|
|
1232
1344
|
|
|
1233
1345
|
### JSON Examples
|
|
1234
1346
|
|
|
@@ -1240,6 +1352,7 @@ Check out `examples/json-examples/` for JSON workflow examples (equivalent to YA
|
|
|
1240
1352
|
- **`conditions.json`** - Condition evaluation examples
|
|
1241
1353
|
- **`prompt.json`** - User input prompts
|
|
1242
1354
|
- **`variables.json`** - Variable substitution examples
|
|
1355
|
+
- **`profiles-example.json`** - Profiles for non-interactive runs (`tp run --profile <name>`)
|
|
1243
1356
|
|
|
1244
1357
|
**Note:** Both YAML and JSON formats are fully supported. Choose the format that fits your preference - YAML for readability, JSON for programmatic generation.
|
|
1245
1358
|
- **`variables.yaml`** - Variable usage examples
|
package/dist/index.cjs
CHANGED
|
@@ -1,37 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`){i.length>0&&(
|
|
4
|
-
`)),
|
|
5
|
-
`)),
|
|
6
|
-
`),
|
|
7
|
-
`);let $=k===0,P=Date.now()-
|
|
8
|
-
`);){let i=
|
|
9
|
-
`),a=
|
|
10
|
-
`).forEach(r=>{r.trim()&&console.log(`| ${r}`)}):console.log(e)}displayBufferedOutput(e,t,r=!1,o
|
|
11
|
-
`)}),e.stderr.forEach(u=>{let
|
|
12
|
-
`)});let a=
|
|
13
|
-
Total execution time: ${
|
|
14
|
-
`)):(0,q.default)(
|
|
15
|
-
`))}displayParallelResults(e,t,r){let
|
|
2
|
+
"use strict";var Fe=Object.create;var pe=Object.defineProperty;var Ve=Object.getOwnPropertyDescriptor;var We=Object.getOwnPropertyNames;var Oe=Object.getPrototypeOf,_e=Object.prototype.hasOwnProperty;var He=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of We(e))!_e.call(s,n)&&n!==t&&pe(s,n,{get:()=>e[n],enumerable:!(r=Ve(e,n))||r.enumerable});return s};var x=(s,e,t)=>(t=s!=null?Fe(Oe(s)):{},He(e||!s||!s.__esModule?pe(t,"default",{value:s,enumerable:!0}):t,s));var Ae=require("child_process"),ce=require("fs"),Ie=require("fs/promises"),V=require("path"),Be=require("util"),ue=x(require("boxen"),1),p=x(require("chalk"),1),De=require("commander"),Le=x(require("dayjs"),1);var T=require("path"),Se=x(require("chalk"),1),q=x(require("log-update"),1);var me=x(require("readline"),1),v=x(require("chalk"),1),K=x(require("inquirer"),1),fe=15,M=class{searchable;constructor(e=!1){this.searchable=e}async prompt(e,t){if(this.searchable)return this.promptWithSearch(e,t);let{choice:r}=await K.default.prompt([{type:"list",name:"choice",message:v.default.cyan(e),choices:t.map(o=>({name:o.label,value:o.id})),pageSize:fe}]),n=t.find(o=>o.id===r);if(!n)throw new Error(`Invalid choice: ${r}`);return n}async promptWithSearch(e,t){return new Promise(r=>{let n="",o=0,i=[...t],a=me.createInterface({input:process.stdin,output:process.stdout,terminal:!1});process.stdin.isTTY&&process.stdin.setRawMode(!0),process.stdout.write("\x1B[?1049h"),process.stdout.write("\x1B[?25l");let u=()=>{process.stdout.write("\x1B[H\x1B[2J"),console.log(v.default.cyan(`? ${e}`));let d=n?v.default.gray(` Filter: ${n}`)+v.default.gray(` (${i.length}/${t.length})`):v.default.gray(" Type to filter, \u2191\u2193 to navigate, Enter to select");console.log(d),console.log();let f=fe,g=0,b=i.length;if(i.length>f){let y=Math.floor(f/2);g=Math.max(0,o-y),b=Math.min(i.length,g+f),b===i.length&&(g=Math.max(0,b-f))}if(i.length===0)console.log(v.default.yellow(" No matches found"));else{g>0&&console.log(v.default.gray(` \u2191 ${g} more above`));for(let y=g;y<b;y++){let k=i[y];console.log(y===o?v.default.cyan(`\u276F ${k.label}`):v.default.white(` ${k.label}`))}b<i.length&&console.log(v.default.gray(` \u2193 ${i.length-b} more below`))}},c=()=>{let d=n.toLowerCase();i=d?t.filter(f=>f.label.toLowerCase().includes(d)):[...t],o>=i.length&&(o=Math.max(0,i.length-1))},m=d=>{let f=d.toString();if(f===""&&(h(),process.exit(0)),f==="\r"||f===`
|
|
3
|
+
`){i.length>0&&(h(),r(i[o]));return}if(f==="\x1B"&&d.length===1){n&&(n="",c(),u());return}if(f==="\x1B[A"){i.length>0&&(o=o>0?o-1:i.length-1,u());return}if(f==="\x1B[B"){i.length>0&&(o=o<i.length-1?o+1:0,u());return}if(f==="\x7F"||f==="\b"){n.length>0&&(n=n.slice(0,-1),c(),u());return}f.length===1&&f>=" "&&f<="~"&&(n+=f,c(),u())},h=()=>{process.stdin.removeListener("data",m),process.stdin.isTTY&&process.stdin.setRawMode(!1),a.close(),process.stdout.write("\x1B[?25h"),process.stdout.write("\x1B[?1049l")};u(),process.stdin.on("data",m)})}},_=class{async prompt(e,t){let{value:r}=await K.default.prompt([{type:"input",name:"value",message:v.default.cyan(e),default:t}]);return r}};var H=x(require("boxen"),1),A=x(require("chalk"),1);function Q(s,e,t,r={}){let{borderColor:n="cyan",isNested:o=!1}=r,i;e!==void 0&&(t?i=`line ${e} in ${t}`:i=`line ${e}`);let a=o?`\u2502 ${s}`:`> ${s}`;return(0,H.default)(a,{title:i,borderStyle:"round",padding:{top:0,bottom:0,left:1,right:1},margin:{top:0,bottom:0,left:0,right:0},borderColor:n})}function I(s,e=!1,t){let r=s?"\u2713 Completed":"\u2717 Failed",n=s?A.default.green(r):A.default.red(r);if(t!==void 0){let o=B(t);return`${n} ${A.default.gray(`(${o})`)}`}return n}function W(s){return(0,H.default)(`\u2717 ${s}`,{borderStyle:"round",padding:{top:0,bottom:0,left:1,right:1},margin:{top:0,bottom:0,left:0,right:0},borderColor:"red"})}function de(s){return(0,H.default)(`> Starting parallel execution (${s} branches)`,{borderStyle:"round",padding:{top:0,bottom:0,left:1,right:1},margin:{top:0,bottom:0,left:0,right:0},borderColor:"yellow"})}function he(s){let e=s?"\u2713 All parallel branches completed":"\u2717 Some parallel branches failed";return s?A.default.green(e):A.default.red(e)}function ee(s,e=!1){return`${e?"| \u2502 ":"\u2502 "}${s}`}function B(s){return`${(s/1e3).toFixed(3)}s`}var ge=require("fs"),we=require("path"),D=class{constructor(e){this.workspace=e}evaluate(e){return"var"in e||"has"in e?this.evaluateVarExists(e):"file"in e?this.evaluateFileExists(e):"choice"in e?this.evaluateChoice(e):"all"in e?this.evaluateAll(e):"any"in e?this.evaluateAny(e):"not"in e?this.evaluateNot(e):!1}evaluateVarExists(e){if(e.has)return this.workspace.hasVariable(e.has)||this.workspace.hasFact(e.has);if(!e.var)return!1;if(typeof e.var=="object"){for(let[r,n]of Object.entries(e.var)){let o=this.workspace.getVariable(r),i=this.workspace.getFact(r),a=o??(i!==void 0?i.toString():void 0);if(a===void 0||a!==n)return!1}return!0}let t=e.var;return this.workspace.hasVariable(t)||this.workspace.hasFact(t)}evaluateFileExists(e){try{let t=e.file.trim(),r=(0,we.resolve)(process.cwd(),t);return(0,ge.existsSync)(r)}catch{return!1}}evaluateChoice(e){return this.workspace.hasChoice(e.choice)}evaluateAll(e){return e.all.every(t=>this.evaluate(t))}evaluateAny(e){return e.any.some(t=>this.evaluate(t))}evaluateNot(e){return!this.evaluate(e.not)}};var R=require("fs/promises"),be=require("os"),N=require("path"),ye=x(require("dayjs"),1),L=(0,N.join)((0,be.homedir)(),".pipeliner","workflow-history"),F=class{constructor(){}async saveHistory(e){await(0,R.mkdir)(L,{recursive:!0});let t=(0,ye.default)().format("YYYY-MM-DD_HH-mm-ss"),r=Math.random().toString(36).slice(2,6),n=(0,N.join)(L,`workflow-${t}-${r}.json`);return await(0,R.writeFile)(n,JSON.stringify(e,null,2),{encoding:"utf8"}),n}async clearAllHistories(){await(0,R.rm)(L,{recursive:!0,force:!0})}async removeHistory(e){await(0,R.rm)((0,N.join)(L,e),{force:!0})}async getHistoryNames(){try{let t=(await(0,R.readdir)(L)).map(r=>(0,N.basename)(r));return t.sort((r,n)=>{let o=u=>{let c=u.match(/workflow-(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})-/);return c?c[1]:""},i=o(r),a=o(n);return i===a?n.localeCompare(r):a.localeCompare(i)}),t}catch(e){if(e instanceof Error&&"code"in e&&e.code==="ENOENT")return[];throw e}}async getHistory(e){let t=await(0,R.readFile)((0,N.join)(L,e),{encoding:"utf8"});return JSON.parse(t)}};var J=class{records=[];initialTimestamp=Date.now();recordStartTimestamp=Date.now();constructor(){this.records=[]}recordStart(){this.recordStartTimestamp=Date.now()}recordEnd(e,t,r,n){let o=this.getDuration();return this.records.push({step:e,context:t,output:r,duration:o,status:n}),o}reset(){this.records=[],this.initialTimestamp=Date.now()}async save(){let e=new F,t={initialTimestamp:this.initialTimestamp,records:this.records};return await e.saveHistory(t)}getDuration(){return Date.now()-this.recordStartTimestamp}};var te=require("child_process");var U=class{async run(e,t,r,n,o=!1,i=!1,a,u,c,m,h){return o?this.runBuffered(e,c,m,h):this.runRealtime(e,r||e,i,a,u,c,m,h)}async runBuffered(e,t,r,n){return new Promise((o,i)=>{let a=this.spawnWithShell(e,t,n),u=[],c=[],m="",h="",d=null;r&&r>0&&(d=setTimeout(()=>{a.kill("SIGTERM");let f=`Command timed out after ${r} seconds`;c.push(f),o({success:!1,stdout:u,stderr:c})},r*1e3)),a.stdout?.on("data",f=>{let g=f.toString(),{lines:b,remaining:y}=this.processStreamBuffer(g,m);u.push(...b),m=y}),a.stderr?.on("data",f=>{let g=f.toString(),{lines:b,remaining:y}=this.processStreamBuffer(g,h);c.push(...b),h=y}),a.on("close",f=>{d&&clearTimeout(d),m.trim()&&u.push(m),h.trim()&&c.push(h),o({success:f===0,stdout:u,stderr:c})}),a.on("error",f=>{d&&clearTimeout(d);let g=`Error: ${f.message}`;o({success:!1,stdout:u,stderr:[...c,g]})})})}async runRealtime(e,t,r,n,o,i,a,u){let m=Q(t,n,o,{borderColor:r?"green":"cyan"});console.log(m);let h=Date.now();return new Promise(d=>{let f=this.spawnWithShell(e,i,u),g="",b="",y=null;a&&a>0&&(y=setTimeout(()=>{f.kill("SIGTERM");let k=`Command timed out after ${a} seconds`,$=W(k);console.error($);let P=Date.now()-h,j=I(!1,!1,P);console.log(j),d(!1)},a*1e3)),f.stdout?.on("data",k=>{let $=k.toString(),{lines:P,remaining:j}=this.processStreamBuffer($,g);P.forEach(X=>process.stdout.write(`\u2502 ${X}
|
|
4
|
+
`)),g=j}),f.stderr?.on("data",k=>{let $=k.toString(),{lines:P,remaining:j}=this.processStreamBuffer($,b);P.forEach(X=>process.stderr.write(`\u2502 ${X}
|
|
5
|
+
`)),b=j}),f.on("close",k=>{y&&clearTimeout(y),g.trim()&&process.stdout.write(`\u2502 ${g}
|
|
6
|
+
`),b.trim()&&process.stderr.write(`\u2502 ${b}
|
|
7
|
+
`);let $=k===0,P=Date.now()-h,j=I($,!1,P);console.log(j),d($)}),f.on("error",k=>{y&&clearTimeout(y);let $=W(`Error: ${k.message}`);console.error($),d(!1)})})}createSpawnOptions(e){let t={stdio:["inherit","pipe","pipe"],shell:!0};return e&&(t.cwd=e),t}spawnWithShell(e,t,r){if(r&&r.length>0){let n=r[0],o=[...r.slice(1),e],i={stdio:["inherit","pipe","pipe"]};return t&&(i.cwd=t),(0,te.spawn)(n,o,i)}else{let n=process.env.SHELL||(process.platform==="win32"?"cmd.exe":"/bin/sh"),o=process.platform==="win32"?"/c":"-c",i={stdio:["inherit","pipe","pipe"]};return t&&(i.cwd=t),(0,te.spawn)(n,[o,e],i)}}processStreamBuffer(e,t){let r=t+e,n=[],o=r;for(;o.includes(`
|
|
8
|
+
`);){let i=o.indexOf(`
|
|
9
|
+
`),a=o.substring(0,i);o=o.substring(i+1),n.push(a)}return{lines:n,remaining:o}}formatNestedOutput(e,t){t?e.split(`
|
|
10
|
+
`).forEach(r=>{r.trim()&&console.log(`| ${r}`)}):console.log(e)}displayBufferedOutput(e,t,r=!1,n,o){let i=Q(t,n,o,{borderColor:"cyan",isNested:r});this.formatNestedOutput(i,r),e.stdout.forEach(u=>{let c=ee(u,r);process.stdout.write(`${c}
|
|
11
|
+
`)}),e.stderr.forEach(u=>{let c=ee(u,r);process.stderr.write(`${c}
|
|
12
|
+
`)});let a=I(e.success,r);console.log(a)}};function Je(s,e,t){if(e.hasVariable(s)){let r=e.getVariable(s);return r??t}if(e.hasFact(s)){let r=e.getFact(s);return typeof r=="string"?r:String(r)}if(e.hasChoice(s)){let r=e.getChoice(s);return r??t}return t}function Y(s,e){let t=/\{\{\s*(\w+)\s*\}\}/g;return s.replace(t,(r,n)=>Je(n,e,r))}var z=class s{state;constructor(){this.state={facts:new Map,choices:new Map,variables:new Map,stepResults:new Map,lastStepIndex:-1}}hasFact(e){return this.state.facts.has(e)}getFact(e){return this.state.facts.get(e)}setFact(e,t){this.state.facts.set(e,t)}getFactStatus(e){if(!this.hasFact(e))return"pending";let t=this.getFact(e);return t===!1||t==="failed"?"failed":"ready"}getAllFacts(){return new Map(this.state.facts)}hasChoice(e){return this.state.choices.has(e)}getChoice(e){return this.state.choices.get(e)}setChoice(e,t){this.state.choices.set(e,t)}hasVariable(e){return this.state.variables.has(e)}getVariable(e){return this.state.variables.get(e)}setVariable(e,t){this.state.variables.set(e,t)}getAllVariables(){return new Map(this.state.variables)}setStepResult(e,t,r){this.state.stepResults.set(e,{success:t,exitCode:r}),this.state.lastStepIndex=e}getStepResult(e){return this.state.stepResults.get(e)}getLastStepResult(){if(this.state.lastStepIndex!==-1)return this.state.stepResults.get(this.state.lastStepIndex)}clone(){let e=new s;return e.state.facts=new Map(this.state.facts),e.state.choices=new Map(this.state.choices),e.state.variables=new Map(this.state.variables),e.state.stepResults=new Map(this.state.stepResults),e.state.lastStepIndex=this.state.lastStepIndex,e}};var Z=class s{static PARALLEL_STEP_INDEX_MULTIPLIER=1e3;workspace;taskRunner;choicePrompt;textPrompt;baseDir;globalShell;constructor(){this.workspace=new z,this.taskRunner=new U,this.choicePrompt=new M,this.textPrompt=new _}resolveBaseDir(e){if(e.baseDir)if((0,T.isAbsolute)(e.baseDir))this.baseDir=e.baseDir;else if(e._filePath){let t=(0,T.dirname)(e._filePath);this.baseDir=(0,T.resolve)(t,e.baseDir)}else this.baseDir=(0,T.resolve)(process.cwd(),e.baseDir)}createStepContext(e,t){let r={workspace:this.workspace,stepIndex:e};return t._lineNumbers&&(r.lineNumber=t._lineNumbers.get(e)),t._fileName&&(r.fileName=t._fileName),r}evaluateStepCondition(e){return e.when?new D(this.workspace).evaluate(e.when):!0}calculateBaseStepIndex(e){return e.branchIndex===void 0?e.stepIndex:Math.floor(e.stepIndex/s.PARALLEL_STEP_INDEX_MULTIPLIER)}isRunStep(e){return"run"in e}async execute(e,t){if(t?.profileVars&&Object.keys(t.profileVars).length>0)for(let[a,u]of Object.entries(t.profileVars))this.workspace.setVariable(a,u);this.resolveBaseDir(e),this.globalShell=e.shell;let r=new J,n=Date.now();for(let a=0;a<e.steps.length;a++){let u=e.steps[a],c=this.createStepContext(a,e),m=!!u.when;if(this.evaluateStepCondition(u)){r.recordStart();try{let h=await this.executeStep(u,c,!1,m);this.handleStepResult(u,c,a,h,r)}catch(h){throw this.handleStepError(u,c,a,h,r),h}}}let o=Date.now()-n,i=B(o);console.log(Se.default.cyan(`
|
|
13
|
+
Total execution time: ${i}`)),await r.save(),r.reset()}isStepSuccessful(e,t){return"run"in t?typeof e=="boolean"?e:e&&typeof e=="object"&&"success"in e?e.success:!1:!0}handleStepResult(e,t,r,n,o){let i=this.isRunStep(e)?(()=>{let c=this.workspace.getStepResult(r);return c?c.success:!0})():this.isStepSuccessful(n,e),a=i?"success":"failure",u=o.recordEnd(e,t,n,a);if(!this.isRunStep(e)){let c=I(i,!1,u);console.log(c)}if(this.isRunStep(e)){if(e.continue===!1){let c=t.lineNumber?` (line ${t.lineNumber})`:"",m=i?`Step ${r}${c} completed, but workflow stopped due to continue: false`:`Step ${r}${c} failed`;throw new Error(m)}if(!i&&e.continue!==!0){let c=t.lineNumber?` (line ${t.lineNumber})`:"";throw new Error(`Step ${r}${c} failed`)}}}handleStepError(e,t,r,n,o){this.workspace.setStepResult(r,!1);let i=n instanceof Error?n.message:String(n),a={success:!1,stdout:[],stderr:[i]};o.recordEnd(e,t,a,"failure")}fixMalformedStep(e){let r=e;return"choose"in e&&r.choose===null&&"message"in e&&"options"in e?{choose:{message:r.message,options:r.options,as:r.as},when:r.when}:"prompt"in e&&r.prompt===null&&"message"in e&&"as"in e?{prompt:{message:r.message,as:r.as,default:r.default},when:r.when}:e}async executeStep(e,t,r=!1,n=!1){if(e=this.fixMalformedStep(e),"run"in e){let o=await this.executeRunStep(e,t,r,n);return r&&typeof o=="object"&&"stdout"in o,o}if("choose"in e){await this.executeChooseStep(e,t);return}if("prompt"in e){await this.executePromptStep(e,t);return}if("parallel"in e){await this.executeParallelStep(e,t);return}if("fail"in e){await this.executeFailStep(e,t);return}}async executeSingleRun(e,t,r=!1,n=!1){let o=this.calculateBaseStepIndex(t),i=Y(e.run,this.workspace),a=e.shell||this.globalShell,u=e.retry??0,c=e.timeout,m=!1,h=0;for(;h<=u;){let d=await this.taskRunner.run(i,o,i,t.branchIndex,r,n,t.lineNumber,t.fileName,this.baseDir,c,a),f=typeof d=="boolean"?d:d.success;if(m=d,f||h>=u)break;if(h++,h<=u){let g=Math.min(1e3*Math.pow(2,h-1),1e4);await new Promise(b=>setTimeout(b,g))}}return m}async executeRunStep(e,t,r=!1,n=!1){let o=await this.executeSingleRun({run:e.run,timeout:e.timeout,retry:e.retry,shell:e.shell},t,r,n),i=typeof o=="boolean"?o:o.success;if(this.workspace.setStepResult(t.stepIndex,i),i||!e.onError)return o;let a={run:e.onError.run,timeout:e.onError.timeout,retry:e.onError.retry,onError:e.onError.onError??void 0};return await this.executeRunChain(a,t,r,n)}async executeRunChain(e,t,r,n){let o=await this.executeSingleRun({run:e.run,timeout:e.timeout,retry:e.retry,shell:void 0},t,r,n);return(typeof o=="boolean"?o:o.success)||!e.onError?o:this.executeRunChain(e.onError,t,r,n)}async executeChooseStep(e,t){let r=e.choose.as,n=e.choose.options.map(a=>a.id);if(r&&this.workspace.hasVariable(r)){let a=this.workspace.getVariable(r)??"";if(n.includes(a)){this.workspace.setChoice(a,a),this.workspace.setStepResult(t.stepIndex,!0);return}}let o=await this.choicePrompt.prompt(e.choose.message,e.choose.options);if(!o?.id)throw new Error(`Invalid choice result: ${JSON.stringify(o)}`);let i=r??o.id;this.workspace.setChoice(o.id,o.id),this.workspace.setVariable(i,o.id),this.workspace.setStepResult(t.stepIndex,!0)}async executePromptStep(e,t){let r=e.prompt.as;if(this.workspace.hasVariable(r)){let a=this.workspace.getVariable(r)??"";this.workspace.setFact(r,a),this.workspace.setStepResult(t.stepIndex,!0);return}let n=Y(e.prompt.message,this.workspace),o=e.prompt.default?Y(e.prompt.default,this.workspace):void 0,i=await this.textPrompt.prompt(n,o);this.workspace.setVariable(r,i),this.workspace.setFact(r,i),this.workspace.setStepResult(t.stepIndex,!0)}createParallelContexts(e,t){return e.parallel.map((r,n)=>({workspace:this.workspace.clone(),stepIndex:t.stepIndex*s.PARALLEL_STEP_INDEX_MULTIPLIER+n,branchIndex:n,lineNumber:t.lineNumber,fileName:t.fileName}))}getBranchDisplayName(e,t){return"run"in e?e.run:"choose"in e?`Choose: ${e.choose.message}`:"prompt"in e?`Prompt: ${e.prompt.message}`:"fail"in e?`Fail: ${e.fail.message}`:`Branch ${t+1}`}async executeParallelBranches(e,t){let r=[],n=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],o=0;for(let c=0;c<e.length;c++){let m=e[c],h=t[c];if(m.when&&!new D(h.workspace).evaluate(m.when))continue;let d=this.getBranchDisplayName(m,c);r.push({index:c,name:d,status:"pending"})}let i=setInterval(()=>{o=(o+1)%n.length,this.updateParallelBranchesDisplay(r,n[o])},100),a=r.map(async c=>{let{index:m}=c,h=e[m],d=t[m];c.status="running";try{let f=await this.executeStep(h,d,!0);return c.status="success",this.updateParallelBranchesDisplay(r,n[o]),{index:m,result:f,context:d}}catch(f){d.workspace.setStepResult(d.stepIndex,!1);let g=f instanceof Error?f.message:String(f);return c.status="failed",c.error=g,this.updateParallelBranchesDisplay(r,n[o]),{index:m,error:f,context:d}}}),u=await Promise.all(a);return clearInterval(i),this.updateParallelBranchesDisplay(r,"",!0),q.default.done(),u}updateParallelBranchesDisplay(e,t,r=!1){let n=e.map(o=>{let i=o.index+1,a="",u="";switch(o.status){case"pending":a="\u25CB",u=`Branch ${i}: ${o.name} - Pending`;break;case"running":a=t,u=`Branch ${i}: ${o.name} - Running...`;break;case"success":a="\u2713",u=`Branch ${i}: ${o.name} - Completed`;break;case"failed":a="\u2717",u=`Branch ${i}: ${o.name} - Failed${o.error?`: ${o.error}`:""}`;break}return`${a} ${u}`});r?(0,q.default)(n.join(`
|
|
14
|
+
`)):(0,q.default)(n.join(`
|
|
15
|
+
`))}displayParallelResults(e,t,r){let n=!0,o=!1;console.log("");for(let a of e){if(!a)continue;o=!0;let{index:u,result:c,error:m,context:h}=a;if(m){n=!1;let d=`Branch ${u+1} failed: ${m instanceof Error?m.message:String(m)}`,f=W(d);console.error(f)}else if(c&&typeof c=="object"&&"stdout"in c){let d=c;if(n=n&&d.success,d.stdout.length>0||d.stderr.length>0||!d.success){let f=t[u],g=this.getBranchDisplayName(f,u);this.taskRunner.displayBufferedOutput(d,g,!1,h.lineNumber,h.fileName)}}}o||console.log("\u26A0\uFE0F All parallel branches were skipped (conditions not met)");let i=he(n);return console.log(i),n}mergeParallelResults(e){for(let t of e){let r=t.workspace.getAllFacts(),n=t.workspace.getAllVariables();for(let[o,i]of r)this.workspace.setFact(o,i);for(let[o,i]of n)this.workspace.setVariable(o,i)}}countExecutableBranches(e,t){let r=0;for(let n=0;n<e.length;n++){let o=e[n],i=t[n];o.when&&!new D(i.workspace).evaluate(o.when)||r++}return r}async executeParallelStep(e,t){let r=this.createParallelContexts(e,t),n=this.countExecutableBranches(e.parallel,r),o=de(n);console.log(o);let i=await this.executeParallelBranches(e.parallel,r),a=this.displayParallelResults(i,e.parallel,t);if(this.workspace.setStepResult(t.stepIndex,a),!a){let u=t.lineNumber?` (line ${t.lineNumber})`:"";throw new Error(`Parallel step ${t.stepIndex}${u} failed: one or more branches failed`)}this.mergeParallelResults(r)}async executeFailStep(e,t){let r=new Error(e.fail.message);throw r.stack=void 0,r}};var $e=require("yaml"),se=require("zod");var l=require("zod"),Ue=l.z.object({file:l.z.string()}),Ye=l.z.object({var:l.z.union([l.z.string(),l.z.record(l.z.string(),l.z.string())]).optional(),has:l.z.string().optional()}),ze=l.z.object({status:l.z.object({fact:l.z.string(),is:l.z.enum(["ready","failed","pending"])})}),qe=l.z.object({step:l.z.object({success:l.z.boolean()}).optional(),last_step:l.z.enum(["success","failure"]).optional()}),Ze=l.z.object({choice:l.z.string()}),Ge=l.z.union([Ue,Ze,Ye,ze,qe]),E=l.z.lazy(()=>l.z.union([Ge,l.z.object({all:l.z.array(E)}),l.z.object({any:l.z.array(E)}),l.z.object({not:E})])),ke=l.z.lazy(()=>l.z.object({run:l.z.string(),timeout:l.z.number().optional(),retry:l.z.number().optional(),onError:ke.optional()})),ve=l.z.object({run:l.z.string(),when:E.optional(),timeout:l.z.number().optional(),retry:l.z.number().optional(),shell:l.z.array(l.z.string()).min(1,"shell must have at least one element").optional(),continue:l.z.boolean().optional(),onError:ke.optional()}),Xe=l.z.object({choose:l.z.object({message:l.z.string(),options:l.z.array(l.z.object({id:l.z.string(),label:l.z.string()})),as:l.z.string().optional()}),when:E.optional()}),Ke=l.z.object({prompt:l.z.object({message:l.z.string(),as:l.z.string(),default:l.z.string().optional(),validate:l.z.string().optional()}),when:E.optional()});function xe(s){if(!s||typeof s!="object")return{found:!1};let e=s;if("choose"in e)return{found:!0,type:"choose"};if("prompt"in e)return{found:!0,type:"prompt"};if("parallel"in e&&Array.isArray(e.parallel))for(let t of e.parallel){let r=xe(t);if(r.found)return r}return{found:!1}}var Re=l.z.lazy(()=>l.z.union([ve,l.z.object({parallel:l.z.array(l.z.lazy(()=>Re)),when:E.optional()}),l.z.object({fail:l.z.object({message:l.z.string()}),when:E.optional()})]).superRefine((s,e)=>{let t=xe(s);t.found&&e.addIssue({code:l.z.ZodIssueCode.custom,message:`'${t.type}' step is not allowed inside 'parallel' block (user input cannot run in parallel)`})})),Qe=l.z.lazy(()=>l.z.union([ve,Xe,Ke,l.z.object({parallel:l.z.array(Re),when:E.optional()}),l.z.object({fail:l.z.object({message:l.z.string()}),when:E.optional()})])),et=l.z.object({name:l.z.string().min(1,"Profile name must be non-empty"),var:l.z.record(l.z.string(),l.z.union([l.z.string(),l.z.number(),l.z.boolean()]).transform(String))}),tt=l.z.object({name:l.z.string().optional(),baseDir:l.z.string().optional(),shell:l.z.array(l.z.string()).min(1,"shell must have at least one element").optional(),profiles:l.z.array(et).optional(),steps:l.z.array(Qe).min(1,"Workflow must have at least one step")});function re(s){return tt.parse(s)}function Ee(s,e){let t=s.path;if(s.code==="custom"){let n=oe(t);return` - ${s.message}${n}`}if(s.message==="Invalid input"){let n=oe(t),o=rt(t,e);return o?` - ${o}${n}`:` - Invalid step type${n}`}let r=oe(t);return` - ${s.message}${r}`}function oe(s){if(s.length===0)return"";let e=[];for(let t=0;t<s.length;t++){let r=s[t],n=s[t+1];r==="steps"&&typeof n=="number"?(e.push(`step ${n+1}`),t++):r==="parallel"&&typeof n=="number"?(e.push(`parallel branch ${n+1}`),t++):typeof r=="string"&&r!=="steps"&&r!=="parallel"&&e.push(r)}return e.length>0?` (${e.join(" \u2192 ")})`:""}function w(s,e,t){let r=t?`
|
|
16
16
|
Reason: ${t}`:"";throw new Error(`Invalid workflow structure:
|
|
17
|
-
- ${e} (step ${s+1})${r}`)}function Ce(s,e,t=!1,r=[]){let
|
|
17
|
+
- ${e} (step ${s+1})${r}`)}function Ce(s,e,t=!1,r=[]){let n=["run","choose","prompt","parallel","fail"],o=n.find(i=>i in s);if(!o){let i=Object.keys(s).filter(a=>a!=="when");w(e,`Unknown step type. Found keys: [${i.join(", ")}]. Valid types: ${n.join(", ")}`)}if(o==="run"){let i=s.run;if(typeof i!="string"&&w(e,"'run' must be a string command"),i===""&&w(e,"'run' command cannot be empty"),"shell"in s&&s.shell!==void 0){Array.isArray(s.shell)||w(e,"'shell' must be an array");let a=s.shell;a.length===0&&w(e,"'shell' cannot be empty","Shell configuration must have at least one element (program name)");for(let u=0;u<a.length;u++)typeof a[u]!="string"&&w(e,`'shell[${u}]' must be a string`)}}if(o==="choose"){if(t){let u=r.join(" \u2192 ");throw new Error(`Invalid workflow structure:
|
|
18
18
|
- 'choose' step is not allowed inside 'parallel' block (step ${e+1}, ${u})
|
|
19
|
-
Reason: User input prompts cannot run in parallel`)}let i=s.choose;(!i||typeof i!="object")&&w(e,"'choose' must be an object with 'message' and 'options'");let a=i;(!a.message||typeof a.message!="string")&&w(e,"'choose.message' is required and must be a string"),Array.isArray(a.options)||w(e,"'choose.options' is required and must be an array"),a.options.length===0&&w(e,"'choose.options' cannot be empty","At least one option is required");for(let u=0;u<a.options.length;u++){let
|
|
19
|
+
Reason: User input prompts cannot run in parallel`)}let i=s.choose;(!i||typeof i!="object")&&w(e,"'choose' must be an object with 'message' and 'options'");let a=i;(!a.message||typeof a.message!="string")&&w(e,"'choose.message' is required and must be a string"),Array.isArray(a.options)||w(e,"'choose.options' is required and must be an array"),a.options.length===0&&w(e,"'choose.options' cannot be empty","At least one option is required");for(let u=0;u<a.options.length;u++){let c=a.options[u];(!c||typeof c!="object")&&w(e,`'choose.options[${u}]' must be an object with 'id' and 'label'`),(!c.id||typeof c.id!="string")&&w(e,`'choose.options[${u}].id' is required and must be a string`),(!c.label||typeof c.label!="string")&&w(e,`'choose.options[${u}].label' is required and must be a string`)}}if(o==="prompt"){if(t){let u=r.join(" \u2192 ");throw new Error(`Invalid workflow structure:
|
|
20
20
|
- 'prompt' step is not allowed inside 'parallel' block (step ${e+1}, ${u})
|
|
21
|
-
Reason: User input prompts cannot run in parallel`)}let i=s.prompt;(!i||typeof i!="object")&&w(e,"'prompt' must be an object with 'message' and 'as'");let a=i;(!a.message||typeof a.message!="string")&&w(e,"'prompt.message' is required and must be a string"),(!a.as||typeof a.as!="string")&&w(e,"'prompt.as' is required and must be a string","The 'as' field specifies the variable name to store the user's input")}if(
|
|
21
|
+
Reason: User input prompts cannot run in parallel`)}let i=s.prompt;(!i||typeof i!="object")&&w(e,"'prompt' must be an object with 'message' and 'as'");let a=i;(!a.message||typeof a.message!="string")&&w(e,"'prompt.message' is required and must be a string"),(!a.as||typeof a.as!="string")&&w(e,"'prompt.as' is required and must be a string","The 'as' field specifies the variable name to store the user's input")}if(o==="parallel"){let i=s.parallel;Array.isArray(i)||w(e,"'parallel' must be an array of steps"),i.length===0&&w(e,"'parallel' cannot be empty","At least one step is required");for(let a=0;a<i.length;a++){let u=i[a];(!u||typeof u!="object")&&w(e,`'parallel[${a}]' must be a valid step object`);let c=[...r,`branch ${a+1}`];Ce(u,e,!0,c)}}if(o==="fail"){let i=s.fail;(!i||typeof i!="object")&&w(e,"'fail' must be an object with 'message'");let a=i;(!a.message||typeof a.message!="string")&&w(e,"'fail.message' is required and must be a string")}}function Pe(s){if(!s||typeof s!="object")throw new Error(`Invalid workflow structure:
|
|
22
22
|
- Workflow must be an object`);let e=s;if("name"in e&&e.name!==void 0&&typeof e.name!="string")throw new Error(`Invalid workflow structure:
|
|
23
|
-
- 'name' must be a string`);if(
|
|
23
|
+
- 'name' must be a string`);if("shell"in e&&e.shell!==void 0){if(!Array.isArray(e.shell))throw new Error(`Invalid workflow structure:
|
|
24
|
+
- 'shell' must be an array`);if(e.shell.length===0)throw new Error(`Invalid workflow structure:
|
|
25
|
+
- 'shell' cannot be empty
|
|
26
|
+
Reason: Shell configuration must have at least one element (program name)`);for(let t=0;t<e.shell.length;t++)if(typeof e.shell[t]!="string")throw new Error(`Invalid workflow structure:
|
|
27
|
+
- 'shell[${t}]' must be a string`)}if(!("steps"in e))throw new Error(`Invalid workflow structure:
|
|
24
28
|
- 'steps' is required`);if(!Array.isArray(e.steps))throw new Error(`Invalid workflow structure:
|
|
25
29
|
- 'steps' must be an array`);if(e.steps.length===0)throw new Error(`Invalid workflow structure:
|
|
26
30
|
- 'steps' cannot be empty
|
|
27
31
|
Reason: Workflow must have at least one step`);for(let t=0;t<e.steps.length;t++){let r=e.steps[t];if(!r||typeof r!="object")throw new Error(`Invalid workflow structure:
|
|
28
|
-
- Step ${t+1} must be an object`);Ce(r,t)}}function
|
|
32
|
+
- Step ${t+1} must be an object`);Ce(r,t)}}function rt(s,e){try{let t=e;for(let o of s)if(typeof o!="symbol")if(t&&typeof t=="object")t=t[o];else return null;if(!t||typeof t!="object")return null;let n=Object.keys(t);if(n.length>0){let o=["run","choose","prompt","parallel","fail"];if(!n.some(a=>o.includes(a)))return`Unknown step type. Found keys: [${n.join(", ")}]. Valid types: run, choose, prompt, parallel, fail`}return null}catch{return null}}function ie(s){let e=s;return"choose"in e&&(e.choose===null||e.choose===void 0)&&"message"in e&&"options"in e?{choose:{message:e.message,options:e.options,as:e.as},when:e.when}:"prompt"in e&&(e.prompt===null||e.prompt===void 0)&&"message"in e&&"as"in e?{prompt:{message:e.message,as:e.as,default:e.default,validate:e.validate},when:e.when}:"parallel"in e&&Array.isArray(e.parallel)?{...e,parallel:e.parallel.map(t=>ie(t))}:s}var G=class{parse(e){let t;try{t=(0,$e.parse)(e)}catch(r){throw new Error(`Invalid YAML format: ${r instanceof Error?r.message:String(r)}`)}if(t&&typeof t=="object"&&"steps"in t){let r=t;Array.isArray(r.steps)&&(r.steps=r.steps.map(n=>ie(n)))}Pe(t);try{return re(t)}catch(r){if(r instanceof se.ZodError){let n=r.issues.map(o=>Ee(o,t)).filter(o=>o!==null).join(`
|
|
29
33
|
`);throw new Error(`Invalid workflow structure:
|
|
30
|
-
${
|
|
31
|
-
`),
|
|
34
|
+
${n}`)}throw r}}extractStepLineNumbers(e){let t=new Map,r=e.split(`
|
|
35
|
+
`),n=0,o=!1;for(let i=0;i<r.length;i++){let a=r[i].trim();if(a==="steps:"||a.startsWith("steps:")){o=!0;continue}o&&a.startsWith("-")&&t.set(n++,i+1)}return t}},ne=class{parse(e){let t;try{t=JSON.parse(e)}catch(r){throw new Error(`Invalid JSON format: ${r instanceof Error?r.message:String(r)}`)}if(t&&typeof t=="object"&&"steps"in t){let r=t;Array.isArray(r.steps)&&(r.steps=r.steps.map(n=>ie(n)))}Pe(t);try{return re(t)}catch(r){if(r instanceof se.ZodError){let n=r.issues.map(o=>Ee(o,t)).filter(o=>o!==null).join(`
|
|
32
36
|
`);throw new Error(`Invalid workflow structure:
|
|
33
|
-
${
|
|
34
|
-
`),
|
|
37
|
+
${n}`)}throw r}}extractStepLineNumbers(e){let t=new Map,r=e.split(`
|
|
38
|
+
`),n=0,o=!1,i=!1;for(let a=0;a<r.length;a++){let c=r[a].trim();if(c.startsWith('"steps"')||c.startsWith("'steps'")){o=!0,c.includes("[")&&(i=!0);continue}if(o&&c==="["){i=!0;continue}if(i&&c==="]"){i=!1,o=!1;continue}i&&c.startsWith("{")&&t.set(n++,a+1)}return t}};function ae(s){switch(s.toLowerCase().split(".").pop()){case"yaml":case"yml":return new G;case"json":return new ne;default:return new G}}var C=require("fs"),S=require("path"),le=require("url"),je={};function Me(){console.log=()=>{},console.error=()=>{},console.warn=()=>{},console.info=()=>{},process.stdout.write=()=>!0,process.stderr.write=()=>!0}function Ne(){return"0.2.16"}function Te(s){let e=s?(0,S.resolve)(s):process.cwd(),t=50,r=0;for(;r<t;){let n=(0,S.resolve)(e,"tp");try{if((0,C.existsSync)(n)&&(0,C.statSync)(n).isDirectory())return n}catch{}let o=(0,S.dirname)(e);if(o===e)break;e=o,r++}return null}var ot=(0,Be.promisify)(Ae.exec),O=new De.Command;O.name("task-pipeliner").description(`A powerful task pipeline runner with condition-based workflow execution.
|
|
35
39
|
|
|
36
40
|
Define workflows in YAML or JSON files with conditional execution, parallel tasks,
|
|
37
41
|
interactive prompts, and variable substitution.
|
|
@@ -69,7 +73,7 @@ Quick Start:
|
|
|
69
73
|
tp history remove # Remove a specific history
|
|
70
74
|
tp history remove-all # Remove all histories
|
|
71
75
|
|
|
72
|
-
`).version(
|
|
76
|
+
`).version(Ne()).addHelpText("after",`
|
|
73
77
|
Examples:
|
|
74
78
|
$ tp run workflow.yaml
|
|
75
79
|
$ tp run examples/simple-project/workflow.yaml
|
|
@@ -82,7 +86,7 @@ Resources:
|
|
|
82
86
|
\u{1F4DA} Documentation: https://task-pipeliner.racgoo.com/
|
|
83
87
|
\u{1F3A8} Visual Generator: https://task-pipeliner-generator.racgoo.com/
|
|
84
88
|
|
|
85
|
-
See README.md for complete DSL reference.`);
|
|
89
|
+
See README.md for complete DSL reference.`);O.command("run").description("Run a workflow from a YAML or JSON file").argument("[file]","Path to the workflow file (YAML or JSON, relative or absolute). If omitted, will search for workflows in the nearest tp directory.").option("-s, --silent","Run in silent mode (suppress console output)").option("-p, --profile <name>","Run in profile mode (use profile name)").addHelpText("after",`
|
|
86
90
|
Examples:
|
|
87
91
|
$ tp run workflow.yaml
|
|
88
92
|
$ tp run workflow.json
|
|
@@ -107,11 +111,11 @@ Workflow File Structure:
|
|
|
107
111
|
\u2022 all/any/not: Combine conditions
|
|
108
112
|
|
|
109
113
|
Supported formats: YAML (.yaml, .yml) and JSON (.json)
|
|
110
|
-
See README.md for complete DSL documentation.`).action(async(s,e)=>{try{let t=s??await
|
|
111
|
-
\u2717 No workflow file found`)),process.exit(1)),e.silent&&Me();let r=ae(t);console.log(p.default.blue(`Loading workflow from ${t}...`));let
|
|
112
|
-
`)),await new Z().execute(
|
|
114
|
+
See README.md for complete DSL documentation.`).action(async(s,e)=>{try{let t=s??await st()??null;t||(console.error(p.default.red(`
|
|
115
|
+
\u2717 No workflow file found`)),process.exit(1)),e.silent&&Me();let r=ae(t);console.log(p.default.blue(`Loading workflow from ${t}...`));let n=(0,ce.readFileSync)(t,"utf-8"),o=r.parse(n);if(!o.steps||!Array.isArray(o.steps))throw new Error("Invalid workflow: steps array is required");let i;if(e.profile){let u=e.profile.trim();if(!o.profiles?.length)throw new Error(`Profile "${u}" requested but workflow has no "profiles" defined. Add a "profiles" section to your workflow file.`);let c=o.profiles.find(m=>m.name===u);if(!c){let m=o.profiles.map(h=>h.name).join(", ");throw new Error(`Profile "${u}" not found. Available profile(s): ${m}`)}i=c.var}o._lineNumbers=r.extractStepLineNumbers(n),o._fileName=it(t),o._filePath=(0,V.resolve)(t),console.log(p.default.green(`Starting workflow execution...
|
|
116
|
+
`)),await new Z().execute(o,i?{profileVars:i}:void 0),console.log(p.default.green(`
|
|
113
117
|
\u2713 Workflow completed successfully`))}catch(t){let r=t instanceof Error?t.message:String(t);console.error(p.default.red(`
|
|
114
|
-
\u2717 Workflow failed: ${r}`)),process.exit(1)}});
|
|
118
|
+
\u2717 Workflow failed: ${r}`)),process.exit(1)}});O.command("open").description("Open generator or docs website in browser").argument("<target>",'Target to open: "generator" or "docs"').addHelpText("after",`
|
|
115
119
|
Examples:
|
|
116
120
|
$ tp open generator
|
|
117
121
|
$ tp open docs
|
|
@@ -120,27 +124,27 @@ Targets:
|
|
|
120
124
|
generator Open the visual workflow generator (https://task-pipeliner-generator.racgoo.com/)
|
|
121
125
|
docs Open the documentation site (https://task-pipeliner.racgoo.com/)`).action(async s=>{let t={generator:"https://task-pipeliner-generator.racgoo.com/",docs:"https://task-pipeliner.racgoo.com/"}[s.toLowerCase()];t||(console.error(p.default.red(`
|
|
122
126
|
\u2717 Invalid target: ${s}`)),console.log(p.default.yellow(`
|
|
123
|
-
Valid targets:`)),console.log(p.default.yellow(" \u2022 generator - Open the visual workflow generator")),console.log(p.default.yellow(" \u2022 docs - Open the documentation site")),process.exit(1));try{let r=process.platform,
|
|
124
|
-
\u2713 Opening ${s==="generator"?"generator":"documentation"} in browser...`)),console.log(p.default.blue(` ${t}`))}catch(r){let
|
|
125
|
-
\u2717 Failed to open browser: ${
|
|
126
|
-
Please visit manually: ${t}`)),process.exit(1)}});var
|
|
127
|
-
\u2717 Invalid choice`)),process.exit(1));let t=new
|
|
128
|
-
\u26A0 No history found`));return}let
|
|
129
|
-
\u2717 Invalid choice`)),process.exit(1));try{let
|
|
127
|
+
Valid targets:`)),console.log(p.default.yellow(" \u2022 generator - Open the visual workflow generator")),console.log(p.default.yellow(" \u2022 docs - Open the documentation site")),process.exit(1));try{let r=process.platform,n;r==="darwin"?n=`open "${t}"`:r==="win32"?n=`start "${t}"`:n=`xdg-open "${t}"`,await ot(n),console.log(p.default.green(`
|
|
128
|
+
\u2713 Opening ${s==="generator"?"generator":"documentation"} in browser...`)),console.log(p.default.blue(` ${t}`))}catch(r){let n=r instanceof Error?r.message:String(r);console.error(p.default.red(`
|
|
129
|
+
\u2717 Failed to open browser: ${n}`)),console.log(p.default.yellow(`
|
|
130
|
+
Please visit manually: ${t}`)),process.exit(1)}});var nt=O.command("history").description("Manage workflow execution history");nt.action(async()=>{let s=new M,e=await s.prompt("Select an action",[{id:"show",label:"Show - View and select a history to view"},{id:"remove",label:"Remove - Delete a specific history file"},{id:"remove-all",label:"Remove All - Delete all history files"}]);e?.id||(console.error(p.default.red(`
|
|
131
|
+
\u2717 Invalid choice`)),process.exit(1));let t=new F;switch(e.id){case"show":{let r=await t.getHistoryNames();if(r.length===0){console.log(p.default.yellow(`
|
|
132
|
+
\u26A0 No history found`));return}let n=await s.prompt("Select a history to view",r.map(o=>({id:o,label:o})));n?.id||(console.error(p.default.red(`
|
|
133
|
+
\u2717 Invalid choice`)),process.exit(1));try{let o=await t.getHistory(n.id);at(o,n.id)}catch(o){let i=o instanceof Error?o.message:String(o);console.error(p.default.red(`
|
|
130
134
|
\u2717 Failed to load history: ${i}`)),process.exit(1)}break}case"remove":{let r=await t.getHistoryNames();if(r.length===0){console.log(p.default.yellow(`
|
|
131
|
-
\u26A0 No history found`));return}let
|
|
132
|
-
\u2717 Invalid choice`)),process.exit(1));try{await t.removeHistory(
|
|
133
|
-
\u2713 Removed history: ${
|
|
135
|
+
\u26A0 No history found`));return}let n=await s.prompt("Select a history to remove",r.map(o=>({id:o,label:o})));n?.id||(console.error(p.default.red(`
|
|
136
|
+
\u2717 Invalid choice`)),process.exit(1));try{await t.removeHistory(n.id),console.log(p.default.green(`
|
|
137
|
+
\u2713 Removed history: ${n.id}`))}catch(o){let i=o instanceof Error?o.message:String(o);console.error(p.default.red(`
|
|
134
138
|
\u2717 Failed to remove history: ${i}`)),process.exit(1)}break}case"remove-all":{if((await s.prompt("Are you sure you want to remove all histories?",[{id:"yes",label:"Yes, remove all"},{id:"no",label:"No, cancel"}]))?.id!=="yes"){console.log(p.default.yellow(`
|
|
135
139
|
\u2717 Cancelled`));return}try{await t.clearAllHistories(),console.log(p.default.green(`
|
|
136
|
-
\u2713 All histories removed`))}catch(
|
|
137
|
-
\u2717 Failed to remove histories: ${
|
|
138
|
-
\u2717 Unknown action: ${e.id}`)),process.exit(1)}});async function
|
|
139
|
-
\u2717 No tp directory found`)),null;try{let t=(await(0,
|
|
140
|
-
\u2717 No workflow files found in ${s}`)),null;let r=await Promise.all(t.map(async i=>{let a=(0,
|
|
141
|
-
\u2717 Failed to read tp directory: ${t}`)),null}}function
|
|
142
|
-
`);let t=s.records.reduce((
|
|
143
|
-
`);console.log((0,ue.default)(u,{borderStyle:"round",padding:{top:1,bottom:1,left:2,right:2},margin:{top:0,bottom:1,left:0,right:0},borderColor:"cyan"})),s.records.forEach((
|
|
144
|
-
`);console.log((0,ue.default)(u,{borderStyle:"round",padding:{top:1,bottom:1,left:2,right:2},margin:{top:0,bottom:1,left:0,right:0},borderColor:s.status==="success"?"green":"red"})),
|
|
140
|
+
\u2713 All histories removed`))}catch(n){let o=n instanceof Error?n.message:String(n);console.error(p.default.red(`
|
|
141
|
+
\u2717 Failed to remove histories: ${o}`)),process.exit(1)}break}default:console.error(p.default.red(`
|
|
142
|
+
\u2717 Unknown action: ${e.id}`)),process.exit(1)}});async function st(){let s=Te();if(!s)return console.error(p.default.red(`
|
|
143
|
+
\u2717 No tp directory found`)),null;try{let t=(await(0,Ie.readdir)(s)).filter(i=>{let a=(0,V.extname)(i).toLowerCase();return[".yaml",".yml",".json"].includes(a)});if(t.length===0)return console.error(p.default.red(`
|
|
144
|
+
\u2717 No workflow files found in ${s}`)),null;let r=await Promise.all(t.map(async i=>{let a=(0,V.join)(s,i);try{let u=ae(a),c=(0,ce.readFileSync)(a,"utf-8"),h=u.parse(c).name??"Untitled";return{id:a,label:`${i} - ${h}`}}catch{return{id:a,label:i}}}));return(await new M(!0).prompt("Select a workflow to run",r)).id}catch(e){let t=e instanceof Error?e.message:String(e);return console.error(p.default.red(`
|
|
145
|
+
\u2717 Failed to read tp directory: ${t}`)),null}}function it(s){return s.split("/").pop()??s}function at(s,e){console.log(`
|
|
146
|
+
`);let t=s.records.reduce((c,m)=>c+m.duration,0),r=s.records.filter(c=>c.status==="success").length,n=s.records.filter(c=>c.status==="failure").length,o=(0,Le.default)(s.initialTimestamp).format("YYYY-MM-DD HH:mm:ss"),a=B(t),u=[p.default.bold("Workflow Execution History"),"",`${p.default.cyan("File:")} ${e}`,`${p.default.cyan("Started:")} ${o}`,`${p.default.cyan("Total Duration:")} ${a}`,`${p.default.cyan("Total Steps:")} ${s.records.length}`,`${p.default.green("\u2713 Successful:")} ${r}`,n>0?`${p.default.red("\u2717 Failed:")} ${n}`:""].filter(Boolean).join(`
|
|
147
|
+
`);console.log((0,ue.default)(u,{borderStyle:"round",padding:{top:1,bottom:1,left:2,right:2},margin:{top:0,bottom:1,left:0,right:0},borderColor:"cyan"})),s.records.forEach((c,m)=>{lt(c,m+1,s.records.length)}),console.log("")}function lt(s,e,t){let r=ct(s.step),n=ut(s.step),o=s.status==="success"?p.default.green("\u2713"):p.default.red("\u2717"),i=s.status==="success"?p.default.green("Success"):p.default.red("Failed"),a=B(s.duration),u=[`${o} ${p.default.bold(`Step ${e}/${t}`)} - ${p.default.cyan(r)}`,`${p.default.gray("Duration:")} ${a} | ${p.default.gray("Status:")} ${i}`,"",p.default.white(n)].join(`
|
|
148
|
+
`);console.log((0,ue.default)(u,{borderStyle:"round",padding:{top:1,bottom:1,left:2,right:2},margin:{top:0,bottom:1,left:0,right:0},borderColor:s.status==="success"?"green":"red"})),pt(s.output)&&ft(s.output)}function ct(s){return"run"in s?"Run":"choose"in s?"Choose":"prompt"in s?"Prompt":"parallel"in s?"Parallel":"fail"in s?"Fail":"Unknown"}function ut(s){return"run"in s?`Command: ${p.default.yellow(s.run)}`:"choose"in s?`Message: ${p.default.yellow(s.choose.message)}`:"prompt"in s?`Message: ${p.default.yellow(s.prompt.message)} | Variable: ${p.default.cyan(s.prompt.as)}`:"parallel"in s?`Parallel execution with ${s.parallel.length} branches`:"fail"in s?`Error: ${p.default.red(s.fail.message)}`:"Unknown step type"}function pt(s){return typeof s=="object"&&s!==null&&"success"in s&&"stdout"in s&&"stderr"in s}function ft(s){if(s.stdout.length>0){let e=s.stdout.map(t=>p.default.gray(` ${t}`)).join(`
|
|
145
149
|
`);console.log(p.default.green(" Output:")),console.log(e)}if(s.stderr.length>0){let e=s.stderr.map(t=>p.default.gray(` ${t}`)).join(`
|
|
146
|
-
`);console.log(p.default.red(" Errors:")),console.log(e)}}
|
|
150
|
+
`);console.log(p.default.red(" Errors:")),console.log(e)}}O.parse();
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
-
export declare function runTask(command: string): Promise<boolean>
|
|
7
|
-
export declare function runTaskSync(command: string): boolean
|
|
6
|
+
export declare function runTask(command: string, shell?: Array<string> | undefined | null): Promise<boolean>
|
|
7
|
+
export declare function runTaskSync(command: string, shell?: Array<string> | undefined | null): boolean
|
|
Binary file
|