codemod 1.0.0-rc.8 → 1.0.0
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.md +946 -0
- package/package.json +19 -10
package/README.md
ADDED
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
# Codemod CLI
|
|
2
|
+
|
|
3
|
+
The Codemod CLI is a command-line interface for running and managing Butterflow workflows - a lightweight, self-hostable workflow engine designed for running large-scale code transformation jobs.
|
|
4
|
+
|
|
5
|
+
> **NOTE**: This CLI is currently in alpha and may change over time. So be careful when using it in production environments.
|
|
6
|
+
> For more information read the [blog post announcing the CLI](https://codemod.com/blog/new-codemod-cli).
|
|
7
|
+
> And any feedback is welcome!
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
### Building from Source
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Clone the repository
|
|
15
|
+
git clone https://github.com/codemod-com/codemod.git
|
|
16
|
+
cd codemod
|
|
17
|
+
|
|
18
|
+
# Build the project
|
|
19
|
+
cargo build --release
|
|
20
|
+
|
|
21
|
+
# The executable will be available at target/release/codemod
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### From npm registry
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g codemod
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
1. Create a workflow file `workflow.yaml`:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
version: "1"
|
|
36
|
+
nodes:
|
|
37
|
+
- id: hello-world
|
|
38
|
+
name: Hello World
|
|
39
|
+
type: automatic
|
|
40
|
+
runtime:
|
|
41
|
+
type: direct
|
|
42
|
+
steps:
|
|
43
|
+
- id: hello
|
|
44
|
+
name: Say Hello
|
|
45
|
+
run: echo "Hello, World!"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
2. Run the workflow:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
codemod run -w workflow.yaml
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
### `codemod init`
|
|
57
|
+
|
|
58
|
+
Initialize a new Butterflow workflow project with interactive setup:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Initialize a new project in current directory
|
|
62
|
+
codemod init
|
|
63
|
+
|
|
64
|
+
# Initialize a new project in a specific directory
|
|
65
|
+
codemod init my-project/
|
|
66
|
+
|
|
67
|
+
# Initialize with a specific name
|
|
68
|
+
codemod init --name my-workflow
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Options:**
|
|
72
|
+
- `--name <NAME>`: Set the project name (defaults to directory name)
|
|
73
|
+
- `--force`: Overwrite existing files if they exist
|
|
74
|
+
- Positional argument: Target directory path (defaults to current directory)
|
|
75
|
+
|
|
76
|
+
**Interactive Setup:**
|
|
77
|
+
|
|
78
|
+
The init command will prompt you with several questions to customize your project:
|
|
79
|
+
|
|
80
|
+
1. **Project Type Selection:**
|
|
81
|
+
```
|
|
82
|
+
? What type of workflow would you like to create?
|
|
83
|
+
❯ AST-grep with JavaScript/TypeScript rules
|
|
84
|
+
AST-grep with YAML rules
|
|
85
|
+
Blank workflow (custom commands)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
2. **Language Selection** (if AST-grep is chosen):
|
|
89
|
+
```
|
|
90
|
+
? Which language would you like to target?
|
|
91
|
+
❯ JavaScript/TypeScript
|
|
92
|
+
Python
|
|
93
|
+
Rust
|
|
94
|
+
Go
|
|
95
|
+
Java
|
|
96
|
+
C/C++
|
|
97
|
+
Other
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
3. **Project Configuration:**
|
|
101
|
+
```
|
|
102
|
+
? Project name: my-codemod
|
|
103
|
+
? Description: Converts legacy API calls to new format
|
|
104
|
+
? Author: Your Name <your.email@example.com>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Generated Project Structure:**
|
|
108
|
+
|
|
109
|
+
Depending on your selections, the init command creates different project templates:
|
|
110
|
+
|
|
111
|
+
#### AST-grep JavaScript Project
|
|
112
|
+
```
|
|
113
|
+
my-codemod/
|
|
114
|
+
├── workflow.yaml # Main workflow definition
|
|
115
|
+
├── package.json # Node.js dependencies
|
|
116
|
+
├── rules/
|
|
117
|
+
│ └── example-rule.js # JavaScript AST transformation rules
|
|
118
|
+
├── scripts/
|
|
119
|
+
│ └── apply-codemod.js # Script to apply transformations
|
|
120
|
+
├── tests/
|
|
121
|
+
│ ├── input/ # Test input files
|
|
122
|
+
│ └── expected/ # Expected output files
|
|
123
|
+
└── README.md # Project documentation
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### AST-grep YAML Project
|
|
127
|
+
```
|
|
128
|
+
my-codemod/
|
|
129
|
+
├── workflow.yaml # Main workflow definition
|
|
130
|
+
├── rules/
|
|
131
|
+
│ └── example-rule.yml # YAML AST pattern rules
|
|
132
|
+
├── scripts/
|
|
133
|
+
│ └── apply-rules.sh # Shell script to apply rules
|
|
134
|
+
├── tests/
|
|
135
|
+
│ ├── input/ # Test input files
|
|
136
|
+
│ └── expected/ # Expected output files
|
|
137
|
+
└── README.md # Project documentation
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Blank Workflow Project
|
|
141
|
+
```
|
|
142
|
+
my-workflow/
|
|
143
|
+
├── workflow.yaml # Basic workflow template
|
|
144
|
+
├── scripts/
|
|
145
|
+
│ └── example.sh # Example script
|
|
146
|
+
└── README.md # Project documentation
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Example Usage:**
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Create a new AST-grep JavaScript project
|
|
153
|
+
$ codemod init my-js-codemod
|
|
154
|
+
? What type of workflow would you like to create? AST-grep with JavaScript/TypeScript rules
|
|
155
|
+
? Which language would you like to target? JavaScript/TypeScript
|
|
156
|
+
? Project name: my-js-codemod
|
|
157
|
+
? Description: Migrate from React class components to hooks
|
|
158
|
+
? Author: John Doe <john@example.com>
|
|
159
|
+
|
|
160
|
+
✓ Created workflow.yaml
|
|
161
|
+
✓ Created package.json with @ast-grep/cli dependency
|
|
162
|
+
✓ Created rules/migrate-to-hooks.js
|
|
163
|
+
✓ Created scripts/apply-codemod.js
|
|
164
|
+
✓ Created test structure
|
|
165
|
+
✓ Created README.md
|
|
166
|
+
|
|
167
|
+
Next steps:
|
|
168
|
+
cd my-js-codemod
|
|
169
|
+
npm install
|
|
170
|
+
codemod validate -w workflow.yaml
|
|
171
|
+
codemod run -w workflow.yaml
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `codemod run`
|
|
175
|
+
|
|
176
|
+
Execute a workflow from various sources:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Run from a specific workflow file
|
|
180
|
+
codemod run -w workflow.yaml
|
|
181
|
+
codemod run -w path/to/workflow.yaml
|
|
182
|
+
|
|
183
|
+
# Run from a workflow bundle directory (containing workflow.yaml)
|
|
184
|
+
codemod run ./my-workflow-bundle/
|
|
185
|
+
|
|
186
|
+
# Run from a registry (Conceptual)
|
|
187
|
+
# codemod run my-registry/react-19-codemods:latest
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Options:**
|
|
191
|
+
- `-w, --workflow <FILE>`: Path to the workflow definition file
|
|
192
|
+
- Positional argument: Path to workflow file or bundle directory
|
|
193
|
+
|
|
194
|
+
### `codemod resume`
|
|
195
|
+
|
|
196
|
+
Resume a paused workflow or trigger manual tasks:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Resume a workflow run by ID
|
|
200
|
+
codemod resume -i <workflow-run-id>
|
|
201
|
+
|
|
202
|
+
# Trigger a specific task by UUID
|
|
203
|
+
codemod resume -i <workflow-run-id> -t <task-uuid>
|
|
204
|
+
|
|
205
|
+
# Trigger all tasks currently awaiting manual triggers
|
|
206
|
+
codemod resume -i <workflow-run-id> --trigger-all
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Options:**
|
|
210
|
+
- `-i, --id <UUID>`: Workflow run ID to resume
|
|
211
|
+
- `-t, --task <UUID>`: Specific task UUID to trigger
|
|
212
|
+
- `--trigger-all`: Trigger all tasks in `AwaitingTrigger` state
|
|
213
|
+
|
|
214
|
+
### `codemod validate`
|
|
215
|
+
|
|
216
|
+
Validate a workflow definition without executing it:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Validate a workflow file
|
|
220
|
+
codemod validate -w workflow.yaml
|
|
221
|
+
codemod validate -w path/to/workflow.yaml
|
|
222
|
+
|
|
223
|
+
# Validate a workflow bundle
|
|
224
|
+
codemod validate ./my-workflow-bundle/
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Validation Checks:**
|
|
228
|
+
- Schema validation
|
|
229
|
+
- Node ID uniqueness
|
|
230
|
+
- Step ID uniqueness within nodes
|
|
231
|
+
- Template ID uniqueness
|
|
232
|
+
- Dependency validation
|
|
233
|
+
- Cyclic dependency detection
|
|
234
|
+
- Template reference validation
|
|
235
|
+
- Matrix strategy validation
|
|
236
|
+
- State schema validation
|
|
237
|
+
- Variable reference syntax validation
|
|
238
|
+
|
|
239
|
+
**Example Output:**
|
|
240
|
+
|
|
241
|
+
Valid workflow:
|
|
242
|
+
```bash
|
|
243
|
+
$ codemod validate -w valid-workflow.yaml
|
|
244
|
+
✓ Workflow definition is valid
|
|
245
|
+
Schema validation: Passed
|
|
246
|
+
Node dependencies: Valid (3 nodes, 2 dependency relationships)
|
|
247
|
+
Template references: Valid (2 templates, 3 references)
|
|
248
|
+
Matrix strategies: Valid (1 matrix node)
|
|
249
|
+
State schema: Valid (2 schema definitions)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Invalid workflow:
|
|
253
|
+
```bash
|
|
254
|
+
$ codemod validate -w invalid-workflow.yaml
|
|
255
|
+
✗ Workflow definition is invalid
|
|
256
|
+
Error at nodes[2].strategy: Matrix strategy requires 'values' or 'from_state' property
|
|
257
|
+
Error at nodes[1].depends_on[0]: Referenced node 'non-existent-node' does not exist
|
|
258
|
+
Error: Cyclic dependency detected: node-a → node-b → node-a
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `codemod login`
|
|
262
|
+
|
|
263
|
+
Authenticate with a Butterflow registry to publish and manage codemods:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Login to the default registry
|
|
267
|
+
codemod login
|
|
268
|
+
|
|
269
|
+
# Login to a specific registry
|
|
270
|
+
codemod login --registry https://registry.example.com
|
|
271
|
+
|
|
272
|
+
# Login with a token (for CI/CD)
|
|
273
|
+
codemod login --token <your-token>
|
|
274
|
+
|
|
275
|
+
# Login with username/password
|
|
276
|
+
codemod login --username <username>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Options:**
|
|
280
|
+
- `--registry <URL>`: Registry URL (defaults to official Butterflow registry)
|
|
281
|
+
- `--token <TOKEN>`: Authentication token (for non-interactive login)
|
|
282
|
+
- `--username <USERNAME>`: Username for interactive login
|
|
283
|
+
- `--scope <SCOPE>`: Organization or user scope for publishing
|
|
284
|
+
|
|
285
|
+
**Interactive Login:**
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
$ codemod login
|
|
289
|
+
? Registry URL: https://app.butterflow.com(default)
|
|
290
|
+
? Username: john.doe
|
|
291
|
+
? Password: ********
|
|
292
|
+
? Organization (optional): my-org
|
|
293
|
+
✓ Successfully logged in as john.doe
|
|
294
|
+
✓ Default publish scope set to @my-org
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Token-based Login (for CI/CD):**
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# Set authentication token
|
|
301
|
+
export BUTTERFLOW_TOKEN="your-registry-token"
|
|
302
|
+
codemod login --token $BUTTERFLOW_TOKEN
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `codemod publish`
|
|
306
|
+
|
|
307
|
+
Publish a codemod to a registry for sharing and reuse:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Publish current directory as a codemod
|
|
311
|
+
codemod publish
|
|
312
|
+
|
|
313
|
+
# Publish a specific codemod directory
|
|
314
|
+
codemod publish ./my-codemod/
|
|
315
|
+
|
|
316
|
+
# Publish with specific version
|
|
317
|
+
codemod publish --version 1.2.3
|
|
318
|
+
|
|
319
|
+
# Publish to specific registry
|
|
320
|
+
codemod publish --registry https://registry.example.com
|
|
321
|
+
|
|
322
|
+
# Dry run (validate without publishing)
|
|
323
|
+
codemod publish --dry-run
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Options:**
|
|
327
|
+
- `--version <VERSION>`: Explicit version (overrides manifest version)
|
|
328
|
+
- `--registry <URL>`: Target registry URL
|
|
329
|
+
- `--tag <TAG>`: Tag for the release (e.g., `beta`, `latest`)
|
|
330
|
+
- `--access <LEVEL>`: Access level (`public` or `private`)
|
|
331
|
+
- `--dry-run`: Validate and pack without uploading
|
|
332
|
+
- `--force`: Override existing version (use with caution)
|
|
333
|
+
|
|
334
|
+
**Publishing Flow:**
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
$ codemod publish
|
|
338
|
+
✓ Validating codemod.yaml manifest
|
|
339
|
+
✓ Validating workflow.yaml
|
|
340
|
+
✓ Running tests (if present)
|
|
341
|
+
✓ Building codemod bundle
|
|
342
|
+
✓ Uploading to registry @my-org/react-hooks-migration@1.0.0
|
|
343
|
+
✓ Published successfully!
|
|
344
|
+
|
|
345
|
+
Install with: codemod run @my-org/react-hooks-migration@1.0.0
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Codemod Manifest Standard
|
|
349
|
+
|
|
350
|
+
Published codemods must include a `codemod.yaml` manifest file that defines metadata, dependencies, and publishing information.
|
|
351
|
+
|
|
352
|
+
### Manifest Schema
|
|
353
|
+
|
|
354
|
+
```yaml
|
|
355
|
+
# codemod.yaml
|
|
356
|
+
name: react-hooks-migration
|
|
357
|
+
version: 1.0.0
|
|
358
|
+
description: Migrates React class components to functional components with hooks
|
|
359
|
+
author: John Doe <john@example.com>
|
|
360
|
+
license: MIT
|
|
361
|
+
repository: https://github.com/user/react-hooks-migration
|
|
362
|
+
|
|
363
|
+
registry:
|
|
364
|
+
access: public
|
|
365
|
+
|
|
366
|
+
targets:
|
|
367
|
+
languages:
|
|
368
|
+
- javascript
|
|
369
|
+
- typescript
|
|
370
|
+
frameworks:
|
|
371
|
+
- react
|
|
372
|
+
versions:
|
|
373
|
+
react: ">=16.8.0"
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Required Fields
|
|
377
|
+
|
|
378
|
+
| Field | Type | Description |
|
|
379
|
+
|-------|------|-------------|
|
|
380
|
+
| `name` | string | Codemod package name (must be unique in scope) |
|
|
381
|
+
| `version` | string | Semantic version (e.g., "1.0.0") |
|
|
382
|
+
| `description` | string | Brief description of what the codemod does |
|
|
383
|
+
| `author` | string | Author name and email |
|
|
384
|
+
|
|
385
|
+
### Optional Fields
|
|
386
|
+
|
|
387
|
+
| Field | Type | Description |
|
|
388
|
+
|-------|------|-------------|
|
|
389
|
+
| `license` | string | License identifier (e.g., "MIT", "Apache-2.0") |
|
|
390
|
+
| `repository` | string | Repository URL for the codemod source code |
|
|
391
|
+
| `registry.access` | string | Access level: "public" or "private" |
|
|
392
|
+
| `targets.languages` | array | Supported programming languages |
|
|
393
|
+
| `targets.frameworks` | array | Supported frameworks or libraries |
|
|
394
|
+
| `targets.versions` | object | Version constraints for frameworks |
|
|
395
|
+
|
|
396
|
+
### Publishing Examples
|
|
397
|
+
|
|
398
|
+
#### Basic Codemod
|
|
399
|
+
|
|
400
|
+
```yaml
|
|
401
|
+
name: remove-console-logs
|
|
402
|
+
version: 1.0.0
|
|
403
|
+
description: Removes console.log statements from JavaScript/TypeScript files
|
|
404
|
+
author: Developer <dev@example.com>
|
|
405
|
+
license: MIT
|
|
406
|
+
repository: https://github.com/dev/remove-console-logs
|
|
407
|
+
|
|
408
|
+
targets:
|
|
409
|
+
languages: [javascript, typescript]
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### Framework-Specific Codemod
|
|
413
|
+
|
|
414
|
+
```yaml
|
|
415
|
+
name: api-migration-v2
|
|
416
|
+
version: 2.1.0
|
|
417
|
+
description: Migrates legacy API calls to new v2 endpoints
|
|
418
|
+
author: API Team <api-team@company.com>
|
|
419
|
+
license: Apache-2.0
|
|
420
|
+
repository: https://github.com/company/api-migration-v2
|
|
421
|
+
|
|
422
|
+
registry:
|
|
423
|
+
access: private
|
|
424
|
+
|
|
425
|
+
targets:
|
|
426
|
+
languages: [javascript, typescript]
|
|
427
|
+
frameworks: [react, vue, angular]
|
|
428
|
+
versions:
|
|
429
|
+
react: ">=16.0.0"
|
|
430
|
+
vue: ">=3.0.0"
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Workflow Sources
|
|
434
|
+
|
|
435
|
+
The CLI supports loading workflows from different sources:
|
|
436
|
+
|
|
437
|
+
### File Path
|
|
438
|
+
```bash
|
|
439
|
+
codemod run -w path/to/your/workflow.yaml
|
|
440
|
+
```
|
|
441
|
+
Loads the specific file. The base path for execution is the directory containing this file.
|
|
442
|
+
|
|
443
|
+
### Directory Path (Bundle)
|
|
444
|
+
```bash
|
|
445
|
+
codemod run path/to/your/bundle/
|
|
446
|
+
```
|
|
447
|
+
Butterflow looks for a standard workflow file (e.g., `workflow.yaml`) inside this directory. The base path for execution is this directory.
|
|
448
|
+
|
|
449
|
+
### Registry Identifier
|
|
450
|
+
```bash
|
|
451
|
+
# Install and run a published codemod
|
|
452
|
+
codemod run @my-org/react-hooks-migration@1.0.0
|
|
453
|
+
|
|
454
|
+
# Run latest version
|
|
455
|
+
codemod run @my-org/react-hooks-migration@latest
|
|
456
|
+
|
|
457
|
+
# Run with custom parameters
|
|
458
|
+
codemod run @my-org/api-migration-v2@2.1.0 --param api_base_url=https://staging-api.example.com
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
When running from a registry, Butterflow:
|
|
462
|
+
1. Downloads the codemod bundle from the registry
|
|
463
|
+
2. Caches it locally for faster subsequent runs
|
|
464
|
+
3. Validates the manifest and workflow
|
|
465
|
+
4. Executes with the bundle directory as the base path
|
|
466
|
+
|
|
467
|
+
For your information the defautl registry is `https://app.codemod.com/`. and you can visualize codemods on the [Codemod Registry webapp](https://app.codemod.com/registry).
|
|
468
|
+
|
|
469
|
+
## Workflow Bundles
|
|
470
|
+
|
|
471
|
+
A workflow bundle is a directory containing:
|
|
472
|
+
1. The main workflow definition file (e.g., `workflow.yaml`)
|
|
473
|
+
2. Any scripts, binaries, or configuration files referenced by the workflow steps
|
|
474
|
+
3. Additional assets needed by the tasks
|
|
475
|
+
|
|
476
|
+
When running from a directory, Butterflow uses that directory as the root for resolving relative paths during execution.
|
|
477
|
+
|
|
478
|
+
## Runtime Execution
|
|
479
|
+
|
|
480
|
+
### Container Runtimes
|
|
481
|
+
|
|
482
|
+
The CLI supports multiple execution runtimes:
|
|
483
|
+
|
|
484
|
+
- **Docker**: Uses Docker daemon for container execution
|
|
485
|
+
- **Podman**: Uses Podman for container execution
|
|
486
|
+
- **Direct**: Runs commands directly on the host machine
|
|
487
|
+
|
|
488
|
+
When using `runtime: direct`, commands execute with the same working directory as the `codemod` CLI invocation. Use the `$CODEMOD_PATH` environment variable to access files within the workflow bundle.
|
|
489
|
+
|
|
490
|
+
### Environment Variables
|
|
491
|
+
|
|
492
|
+
The CLI provides several environment variables to running tasks:
|
|
493
|
+
|
|
494
|
+
- `$STATE_OUTPUTS`: File descriptor for writing state updates
|
|
495
|
+
- `$CODEMOD_PATH`: Absolute path to the workflow bundle root (for `direct` runtime)
|
|
496
|
+
- `$BUTTERFLOW_STATE`: Path to state file for programmatic access
|
|
497
|
+
|
|
498
|
+
## Manual Triggers and Resumption
|
|
499
|
+
|
|
500
|
+
### Manual Nodes
|
|
501
|
+
|
|
502
|
+
Nodes with `type: manual` or `trigger: { type: manual }` will pause execution and await manual triggers:
|
|
503
|
+
|
|
504
|
+
```yaml
|
|
505
|
+
nodes:
|
|
506
|
+
- id: deploy-prod
|
|
507
|
+
name: Deploy to Production
|
|
508
|
+
type: automatic
|
|
509
|
+
trigger:
|
|
510
|
+
type: manual # Requires manual approval
|
|
511
|
+
steps:
|
|
512
|
+
- id: deploy
|
|
513
|
+
run: ./deploy.sh production
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### Triggering Manual Tasks
|
|
517
|
+
|
|
518
|
+
When a workflow pauses for manual triggers:
|
|
519
|
+
|
|
520
|
+
1. The workflow state is persisted with tasks marked `AwaitingTrigger`
|
|
521
|
+
2. The CLI provides task UUIDs for manual triggering
|
|
522
|
+
3. Use `codemod resume` to trigger specific tasks or all awaiting tasks
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
# Trigger specific deployment task
|
|
526
|
+
codemod resume -i abc123-workflow-run-id -t def456-task-uuid
|
|
527
|
+
|
|
528
|
+
# Trigger all awaiting tasks and continue
|
|
529
|
+
codemod resume -i abc123-workflow-run-id --trigger-all
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## Error Handling
|
|
533
|
+
|
|
534
|
+
### Automatic Validation
|
|
535
|
+
|
|
536
|
+
Validation is performed automatically when running workflows:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
$ codemod run -w invalid-workflow.yaml
|
|
540
|
+
✗ Workflow definition is invalid
|
|
541
|
+
Error: Cyclic dependency detected: node-a → node-b → node-a
|
|
542
|
+
Workflow execution aborted
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Resume After Failures
|
|
546
|
+
|
|
547
|
+
If a workflow fails or is interrupted:
|
|
548
|
+
|
|
549
|
+
1. The state is automatically persisted
|
|
550
|
+
2. Use `codemod resume` to continue from the last checkpoint
|
|
551
|
+
3. Failed tasks can be retried while preserving completed work
|
|
552
|
+
|
|
553
|
+
## Examples
|
|
554
|
+
|
|
555
|
+
### Basic Workflow Execution
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
# Create and run a simple workflow
|
|
559
|
+
cat > hello.yaml << EOF
|
|
560
|
+
version: "1"
|
|
561
|
+
nodes:
|
|
562
|
+
- id: greet
|
|
563
|
+
name: Greeting
|
|
564
|
+
type: automatic
|
|
565
|
+
runtime:
|
|
566
|
+
type: direct
|
|
567
|
+
steps:
|
|
568
|
+
- id: hello
|
|
569
|
+
run: echo "Hello from Butterflow!"
|
|
570
|
+
EOF
|
|
571
|
+
|
|
572
|
+
codemod run -w hello.yaml
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Matrix Workflow with Manual Approval
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
# Run matrix workflow requiring manual triggers
|
|
579
|
+
codemod run -w deploy-workflow.yaml
|
|
580
|
+
|
|
581
|
+
# When paused, trigger specific environments
|
|
582
|
+
codemod resume -i <run-id> -t <staging-task-uuid>
|
|
583
|
+
codemod resume -i <run-id> -t <prod-task-uuid>
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Workflow Validation and Publishing
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
# Validate before running
|
|
590
|
+
codemod validate -w complex-workflow.yaml
|
|
591
|
+
|
|
592
|
+
# Run if validation passes
|
|
593
|
+
codemod run -w complex-workflow.yaml
|
|
594
|
+
|
|
595
|
+
# Login to registry and publish a codemod
|
|
596
|
+
codemod login
|
|
597
|
+
codemod publish ./my-codemod/
|
|
598
|
+
|
|
599
|
+
# Run published codemod
|
|
600
|
+
codemod run @my-org/my-codemod@1.0.0
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Publishing Workflow
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
# Create and publish a new codemod
|
|
607
|
+
codemod init my-api-migration
|
|
608
|
+
cd my-api-migration
|
|
609
|
+
|
|
610
|
+
# Develop and test your codemod
|
|
611
|
+
# ... edit workflow.yaml, add rules, scripts, etc.
|
|
612
|
+
|
|
613
|
+
# Create manifest
|
|
614
|
+
cat > codemod.yaml << EOF
|
|
615
|
+
name: my-api-migration
|
|
616
|
+
version: 1.0.0
|
|
617
|
+
description: Migrates legacy API calls
|
|
618
|
+
author: Developer <dev@example.com>
|
|
619
|
+
workflow: workflow.yaml
|
|
620
|
+
targets:
|
|
621
|
+
languages: [javascript, typescript]
|
|
622
|
+
EOF
|
|
623
|
+
|
|
624
|
+
# Validate and publish
|
|
625
|
+
codemod validate
|
|
626
|
+
codemod publish --dry-run # Preview
|
|
627
|
+
codemod publish # Publish to registry
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
## Global Options
|
|
631
|
+
|
|
632
|
+
Most commands support these global options:
|
|
633
|
+
|
|
634
|
+
- `--help, -h`: Show help information
|
|
635
|
+
- `--version, -V`: Show version information
|
|
636
|
+
- `--verbose, -v`: Enable verbose output
|
|
637
|
+
- `--quiet, -q`: Suppress non-essential output
|
|
638
|
+
|
|
639
|
+
For detailed help on any command, use:
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
codemod <command> --help
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
## JSSG (JavaScript AST-grep)
|
|
646
|
+
|
|
647
|
+
JSSG is a JavaScript/TypeScript codemod runner and testing framework inspired by [ast-grep](https://ast-grep.github.io/). It enables you to write codemods in JavaScript and apply them to codebases with powerful CLI and test automation support.
|
|
648
|
+
|
|
649
|
+
### Running a Codemod
|
|
650
|
+
|
|
651
|
+
To run a JavaScript codemod on a target directory:
|
|
652
|
+
|
|
653
|
+
```bash
|
|
654
|
+
codemod jssg run my-codemod.js ./src --language javascript
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
**Options:**
|
|
658
|
+
- `--language <LANG>`: Target language (javascript, typescript, etc.)
|
|
659
|
+
- `--extensions <ext1,ext2>`: Comma-separated list of file extensions to process
|
|
660
|
+
- `--no-gitignore`: Do not respect .gitignore files
|
|
661
|
+
- `--include-hidden`: Include hidden files and directories
|
|
662
|
+
- `--max-threads <N>`: Maximum number of concurrent threads
|
|
663
|
+
- `--dry-run`: Perform a dry run without making changes
|
|
664
|
+
|
|
665
|
+
See `codemod jssg run --help` for all options.
|
|
666
|
+
|
|
667
|
+
### Example
|
|
668
|
+
|
|
669
|
+
```bash
|
|
670
|
+
codemod jssg run my-codemod.js ./src --language javascript --dry-run
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
# JSSG Testing Framework Usage Guide
|
|
676
|
+
|
|
677
|
+
## Overview
|
|
678
|
+
|
|
679
|
+
The JSSG Testing Framework provides comprehensive testing capabilities for JavaScript codemods with before/after fixture files. It integrates with the existing ExecutionEngine and provides a familiar test runner interface.
|
|
680
|
+
|
|
681
|
+
## Quick Start
|
|
682
|
+
|
|
683
|
+
### 1. Basic Test Structure
|
|
684
|
+
|
|
685
|
+
Create a test directory with the following structure:
|
|
686
|
+
|
|
687
|
+
```
|
|
688
|
+
tests/
|
|
689
|
+
├── simple-transform/
|
|
690
|
+
│ ├── input.js
|
|
691
|
+
│ └── expected.js
|
|
692
|
+
├── complex-case/
|
|
693
|
+
│ ├── input.ts
|
|
694
|
+
│ └── expected.ts
|
|
695
|
+
└── multi-file/
|
|
696
|
+
├── input/
|
|
697
|
+
│ ├── file1.js
|
|
698
|
+
│ └── file2.js
|
|
699
|
+
└── expected/
|
|
700
|
+
├── file1.js
|
|
701
|
+
└── file2.js
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### 2. Running Tests
|
|
705
|
+
|
|
706
|
+
```bash
|
|
707
|
+
# Basic test run
|
|
708
|
+
codemod jssg test my-codemod.js --language javascript
|
|
709
|
+
|
|
710
|
+
# With custom test directory
|
|
711
|
+
codemod jssg test my-codemod.js --language typescript --test-directory ./my-tests
|
|
712
|
+
|
|
713
|
+
# Update snapshots (create/update expected files)
|
|
714
|
+
codemod jssg test my-codemod.js --language javascript --update-snapshots
|
|
715
|
+
|
|
716
|
+
# Verbose output with detailed diffs
|
|
717
|
+
codemod jssg test my-codemod.js --language javascript --verbose
|
|
718
|
+
|
|
719
|
+
# Watch mode (re-run tests on file changes)
|
|
720
|
+
codemod jssg test my-codemod.js --language javascript --watch
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
## CLI Options
|
|
724
|
+
|
|
725
|
+
### Required Arguments
|
|
726
|
+
- `codemod_file`: Path to the codemod JavaScript file
|
|
727
|
+
- `--language`: Target language (javascript, typescript, etc.)
|
|
728
|
+
|
|
729
|
+
### Test Discovery
|
|
730
|
+
- `--test-directory`: Test directory (default: "tests")
|
|
731
|
+
- `--filter`: Run only tests matching pattern
|
|
732
|
+
|
|
733
|
+
### Output Control
|
|
734
|
+
- `--reporter`: Output format (console, json, terse)
|
|
735
|
+
- `--verbose`: Show detailed output
|
|
736
|
+
- `--context-lines`: Number of diff context lines (default: 3)
|
|
737
|
+
- `--ignore-whitespace`: Ignore whitespace in comparisons
|
|
738
|
+
|
|
739
|
+
### Test Execution
|
|
740
|
+
- `--timeout`: Test timeout in seconds (default: 30)
|
|
741
|
+
- `--max-threads`: Maximum concurrent threads
|
|
742
|
+
- `--sequential`: Run tests sequentially
|
|
743
|
+
- `--fail-fast`: Stop on first failure
|
|
744
|
+
|
|
745
|
+
### Snapshot Management
|
|
746
|
+
- `--update-snapshots`: Create/update expected files
|
|
747
|
+
- `--expect-errors`: Comma-separated patterns for tests expected to fail
|
|
748
|
+
|
|
749
|
+
### Development
|
|
750
|
+
- `--watch`: Watch for changes and re-run tests
|
|
751
|
+
|
|
752
|
+
## Test File Formats
|
|
753
|
+
|
|
754
|
+
### Single File Format
|
|
755
|
+
Each test case is a directory with `input.{ext}` and `expected.{ext}` files:
|
|
756
|
+
|
|
757
|
+
```
|
|
758
|
+
test-case-name/
|
|
759
|
+
├── input.js # Input code
|
|
760
|
+
└── expected.js # Expected output
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Multi-File Format
|
|
764
|
+
For testing multiple files, use `input/` and `expected/` directories:
|
|
765
|
+
|
|
766
|
+
```
|
|
767
|
+
test-case-name/
|
|
768
|
+
├── input/
|
|
769
|
+
│ ├── file1.js
|
|
770
|
+
│ └── file2.js
|
|
771
|
+
└── expected/
|
|
772
|
+
├── file1.js
|
|
773
|
+
└── file2.js
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
## Language Support
|
|
777
|
+
|
|
778
|
+
The framework automatically detects input files based on language extensions:
|
|
779
|
+
|
|
780
|
+
- **JavaScript**: `.js`, `.jsx`, `.mjs`
|
|
781
|
+
- **TypeScript**: `.ts`, `.tsx`, `.mts`
|
|
782
|
+
- **Other languages**: Determined by `get_extensions_for_language()`
|
|
783
|
+
|
|
784
|
+
## Error Handling
|
|
785
|
+
|
|
786
|
+
### Missing Expected Files
|
|
787
|
+
```bash
|
|
788
|
+
# Error: No expected file found
|
|
789
|
+
codemod jssg test my-codemod.js --language javascript
|
|
790
|
+
# Error: No expected file found for input.js in tests/my-test. Run with --update-snapshots to create it.
|
|
791
|
+
|
|
792
|
+
# Solution: Create expected files
|
|
793
|
+
codemod jssg test my-codemod.js --language javascript --update-snapshots
|
|
794
|
+
# Created expected file for my-test/input.js
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Ambiguous Input Files
|
|
798
|
+
```bash
|
|
799
|
+
# Error: Multiple input files found
|
|
800
|
+
# tests/my-test/input.js and tests/my-test/input.ts both exist
|
|
801
|
+
|
|
802
|
+
# Solution: Use only one input file or organize into directories
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### Expected Test Failures
|
|
806
|
+
```bash
|
|
807
|
+
# Test cases that should fail
|
|
808
|
+
codemod jssg test my-codemod.js --language javascript --expect-errors "error-case,invalid-syntax"
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
## Output Formats
|
|
812
|
+
|
|
813
|
+
### Console (Default)
|
|
814
|
+
```
|
|
815
|
+
test my-test ... ok
|
|
816
|
+
test failing-test ... FAILED
|
|
817
|
+
|
|
818
|
+
failures:
|
|
819
|
+
|
|
820
|
+
---- failing-test stdout ----
|
|
821
|
+
Output mismatch for file expected.js:
|
|
822
|
+
-const x = 1;
|
|
823
|
+
+const y = 1;
|
|
824
|
+
|
|
825
|
+
test result: FAILED. 1 passed; 1 failed; 0 ignored
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### JSON
|
|
829
|
+
```bash
|
|
830
|
+
codemod jssg test my-codemod.js --language javascript --reporter json
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
```json
|
|
834
|
+
{
|
|
835
|
+
"type": "suite",
|
|
836
|
+
"event": "started",
|
|
837
|
+
"test_count": 2
|
|
838
|
+
}
|
|
839
|
+
{
|
|
840
|
+
"type": "test",
|
|
841
|
+
"event": "started",
|
|
842
|
+
"name": "my-test"
|
|
843
|
+
}
|
|
844
|
+
{
|
|
845
|
+
"type": "test",
|
|
846
|
+
"name": "my-test",
|
|
847
|
+
"event": "ok"
|
|
848
|
+
}
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### Terse
|
|
852
|
+
```bash
|
|
853
|
+
codemod jssg test my-codemod.js --language javascript --reporter terse
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
```
|
|
857
|
+
..F
|
|
858
|
+
|
|
859
|
+
failures:
|
|
860
|
+
...
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
## Advanced Features
|
|
864
|
+
|
|
865
|
+
### Snapshot Management
|
|
866
|
+
```bash
|
|
867
|
+
# Create initial snapshots
|
|
868
|
+
codemod jssg test my-codemod.js --language javascript --update-snapshots
|
|
869
|
+
|
|
870
|
+
# Update specific test snapshots
|
|
871
|
+
codemod jssg test my-codemod.js --language javascript --filter "specific-test" --update-snapshots
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
### Test Filtering
|
|
875
|
+
```bash
|
|
876
|
+
# Run tests matching pattern
|
|
877
|
+
codemod jssg test my-codemod.js --language javascript --filter "transform"
|
|
878
|
+
|
|
879
|
+
# Run specific test
|
|
880
|
+
codemod jssg test my-codemod.js --language javascript --filter "my-specific-test"
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Performance Tuning
|
|
884
|
+
```bash
|
|
885
|
+
# Limit concurrent threads
|
|
886
|
+
codemod jssg test my-codemod.js --language javascript --max-threads 2
|
|
887
|
+
|
|
888
|
+
# Run sequentially for debugging
|
|
889
|
+
codemod jssg test my-codemod.js --language javascript --sequential
|
|
890
|
+
|
|
891
|
+
# Set custom timeout
|
|
892
|
+
codemod jssg test my-codemod.js --language javascript --timeout 60
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
### Diff Customization
|
|
896
|
+
```bash
|
|
897
|
+
# More context in diffs
|
|
898
|
+
codemod jssg test my-codemod.js --language javascript --context-lines 5
|
|
899
|
+
|
|
900
|
+
# Ignore whitespace differences
|
|
901
|
+
codemod jssg test my-codemod.js --language javascript --ignore-whitespace
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
## Integration with Development Workflow
|
|
905
|
+
|
|
906
|
+
### CI/CD Integration
|
|
907
|
+
```yaml
|
|
908
|
+
# GitHub Actions example
|
|
909
|
+
- name: Run codemod tests
|
|
910
|
+
run: codemod jssg test my-codemod.js --language javascript --reporter json
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
### IDE Integration
|
|
914
|
+
The framework outputs standard test results that can be consumed by IDEs and test runners.
|
|
915
|
+
|
|
916
|
+
### Watch Mode Development
|
|
917
|
+
```bash
|
|
918
|
+
# Continuous testing during development
|
|
919
|
+
codemod jssg test my-codemod.js --language javascript --watch --verbose
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
## Best Practices
|
|
923
|
+
|
|
924
|
+
1. **Organize tests by functionality**: Group related test cases in descriptive directories
|
|
925
|
+
2. **Use meaningful test names**: Directory names become test names in output
|
|
926
|
+
3. **Start with --update-snapshots**: Generate initial expected files, then review and commit
|
|
927
|
+
4. **Use --expect-errors for negative tests**: Test error conditions explicitly
|
|
928
|
+
5. **Leverage --filter during development**: Focus on specific tests while developing
|
|
929
|
+
6. **Use --watch for rapid iteration**: Get immediate feedback on changes
|
|
930
|
+
|
|
931
|
+
## Troubleshooting
|
|
932
|
+
|
|
933
|
+
### Common Issues
|
|
934
|
+
|
|
935
|
+
1. **No tests found**: Check test directory path and file extensions
|
|
936
|
+
2. **Ambiguous input files**: Ensure only one input file per test case
|
|
937
|
+
3. **Timeout errors**: Increase timeout for complex codemods
|
|
938
|
+
4. **Memory issues**: Reduce max-threads for large test suites
|
|
939
|
+
|
|
940
|
+
### Debug Mode
|
|
941
|
+
```bash
|
|
942
|
+
# Verbose output for debugging
|
|
943
|
+
codemod jssg test my-codemod.js --language javascript --verbose --sequential
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
This framework provides a robust foundation for testing JavaScript codemods with familiar tooling and comprehensive features.
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codemod",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"publishConfig": {
|
|
5
|
-
"access": "public"
|
|
6
|
-
"tag": "next"
|
|
5
|
+
"access": "public"
|
|
7
6
|
},
|
|
8
7
|
"description": "Codemod platform for semantic code transformations",
|
|
9
8
|
"homepage": "https://codemod.com",
|
|
@@ -11,8 +10,18 @@
|
|
|
11
10
|
"engines": {
|
|
12
11
|
"node": ">= 16.0.0"
|
|
13
12
|
},
|
|
14
|
-
"keywords": [
|
|
15
|
-
|
|
13
|
+
"keywords": [
|
|
14
|
+
"codemod",
|
|
15
|
+
"ast",
|
|
16
|
+
"transformation",
|
|
17
|
+
"refactoring",
|
|
18
|
+
"migration"
|
|
19
|
+
],
|
|
20
|
+
"files": [
|
|
21
|
+
"codemod",
|
|
22
|
+
"postinstall.js",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
16
25
|
"dependencies": {
|
|
17
26
|
"detect-libc": "^2.0.3"
|
|
18
27
|
},
|
|
@@ -20,11 +29,11 @@
|
|
|
20
29
|
"postinstall": "node postinstall.js"
|
|
21
30
|
},
|
|
22
31
|
"optionalDependencies": {
|
|
23
|
-
"@codemod.com/cli-darwin-arm64": "1.0.0
|
|
24
|
-
"@codemod.com/cli-darwin-x64": "1.0.0
|
|
25
|
-
"@codemod.com/cli-linux-arm64-gnu": "1.0.0
|
|
26
|
-
"@codemod.com/cli-linux-x64-gnu": "1.0.0
|
|
27
|
-
"@codemod.com/cli-win32-x64-msvc": "1.0.0
|
|
32
|
+
"@codemod.com/cli-darwin-arm64": "1.0.0",
|
|
33
|
+
"@codemod.com/cli-darwin-x64": "1.0.0",
|
|
34
|
+
"@codemod.com/cli-linux-arm64-gnu": "1.0.0",
|
|
35
|
+
"@codemod.com/cli-linux-x64-gnu": "1.0.0",
|
|
36
|
+
"@codemod.com/cli-win32-x64-msvc": "1.0.0"
|
|
28
37
|
},
|
|
29
38
|
"bin": {
|
|
30
39
|
"codemod": "codemod"
|