gaji 0.2.7 → 0.3.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 +340 -0
- package/package.json +6 -6
package/README.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="logo.png" alt="gaji logo" width="200"/>
|
|
3
|
+
<h1>gaji</h1>
|
|
4
|
+
<p>Type-safe GitHub Actions workflows in TypeScript</p>
|
|
5
|
+
<p><em>GitHub Actions Justified Improvements</em></p>
|
|
6
|
+
<p>🍆 Named after the Korean word "가지" (gaji, eggplant) - a versatile ingredient that makes everything better!</p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
`gaji` is a CLI tool that allows developers to write GitHub Actions workflows in TypeScript with full type safety, then compile them to YAML. It automatically fetches `action.yml` definitions and generates typed wrappers, so you get autocomplete and type checking for every action input and output.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- TypeScript-based workflow authoring with full type safety
|
|
16
|
+
- Automatic type generation from `action.yml` files
|
|
17
|
+
- Composite action and reusable workflow support
|
|
18
|
+
- File watching for development (`--watch`)
|
|
19
|
+
- Built-in QuickJS execution with `npx tsx` fallback
|
|
20
|
+
- GitHub Enterprise support
|
|
21
|
+
- Single binary distribution (Rust)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
### From npm
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -D gaji
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### From cargo
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cargo install gaji
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Initialize a new project (creates workflows/ and generated/ directories)
|
|
41
|
+
gaji init
|
|
42
|
+
|
|
43
|
+
# Add actions and generate types
|
|
44
|
+
gaji add actions/checkout@v5
|
|
45
|
+
gaji add actions/setup-node@v4
|
|
46
|
+
|
|
47
|
+
# Run a one-time dev scan to generate types
|
|
48
|
+
gaji dev
|
|
49
|
+
|
|
50
|
+
# Build workflows to YAML
|
|
51
|
+
gaji build
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Writing Workflows
|
|
57
|
+
|
|
58
|
+
Create TypeScript files in the `workflows/` directory:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { getAction, Job, Workflow } from "../generated/index.js";
|
|
62
|
+
|
|
63
|
+
const checkout = getAction("actions/checkout@v5");
|
|
64
|
+
const setupNode = getAction("actions/setup-node@v4");
|
|
65
|
+
|
|
66
|
+
const build = new Job("ubuntu-latest")
|
|
67
|
+
.addStep(checkout({
|
|
68
|
+
name: "Checkout code",
|
|
69
|
+
with: { "fetch-depth": 1 },
|
|
70
|
+
}))
|
|
71
|
+
.addStep(setupNode({
|
|
72
|
+
with: { "node-version": "22" },
|
|
73
|
+
}))
|
|
74
|
+
.addStep({ name: "Install dependencies", run: "npm ci" })
|
|
75
|
+
.addStep({ name: "Run tests", run: "npm test" });
|
|
76
|
+
|
|
77
|
+
const workflow = new Workflow({
|
|
78
|
+
name: "CI",
|
|
79
|
+
on: {
|
|
80
|
+
push: { branches: ["main"] },
|
|
81
|
+
pull_request: { branches: ["main"] },
|
|
82
|
+
},
|
|
83
|
+
}).addJob("build", build);
|
|
84
|
+
|
|
85
|
+
workflow.build("ci");
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run `gaji build` and it outputs `.github/workflows/ci.yml`.
|
|
89
|
+
|
|
90
|
+
### Recommended Development Workflow
|
|
91
|
+
|
|
92
|
+
For the best experience, follow this workflow:
|
|
93
|
+
|
|
94
|
+
1. **Start watch mode**:
|
|
95
|
+
```bash
|
|
96
|
+
gaji dev --watch
|
|
97
|
+
```
|
|
98
|
+
Leave this running in a terminal. It will automatically generate types when you add new actions.
|
|
99
|
+
|
|
100
|
+
2. **Edit your TypeScript workflows** in `workflows/*.ts`:
|
|
101
|
+
- Add or modify steps
|
|
102
|
+
- Use `getAction()` with full type safety
|
|
103
|
+
- Types are automatically generated for new actions
|
|
104
|
+
|
|
105
|
+
3. **Build to YAML**:
|
|
106
|
+
```bash
|
|
107
|
+
gaji build
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
4. **Review the generated YAML** in `.github/workflows/`:
|
|
111
|
+
- Verify commands are correct
|
|
112
|
+
- Check that step order is as expected
|
|
113
|
+
- Ensure all required fields are present
|
|
114
|
+
|
|
115
|
+
5. **Commit both TypeScript and YAML**:
|
|
116
|
+
```bash
|
|
117
|
+
git add workflows/ .github/workflows/
|
|
118
|
+
git commit -m "Update workflows"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Why Commit Both?
|
|
122
|
+
|
|
123
|
+
You should commit **both** the TypeScript source (`workflows/*.ts`) and the generated YAML (`.github/workflows/*.yml`):
|
|
124
|
+
|
|
125
|
+
- **TypeScript**: Source of truth for your workflows
|
|
126
|
+
- **YAML**: What GitHub Actions actually executes
|
|
127
|
+
|
|
128
|
+
#### ⚠️ Important: Auto-compilation in CI
|
|
129
|
+
|
|
130
|
+
While you can create a workflow that auto-compiles TypeScript to YAML on push, **this is NOT recommended**. Always compile and review workflows locally before committing.
|
|
131
|
+
|
|
132
|
+
If you're willing to handle the complexity of GitHub Actions triggers (e.g., filtering `paths`, managing PAT tokens, avoiding infinite loops), you can set up an auto-compilation workflow. See [`workflows/update-workflows.ts`](https://github.com/dodok8/gaji/blob/main/workflows/update-workflows.ts) for a working example.
|
|
133
|
+
|
|
134
|
+
### Composite Actions
|
|
135
|
+
|
|
136
|
+
Define reusable composite actions and reference them in workflows:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { CompositeAction, CallAction, Job, Workflow } from "../generated/index.js";
|
|
140
|
+
|
|
141
|
+
const action = new CompositeAction({
|
|
142
|
+
name: "Setup",
|
|
143
|
+
description: "Setup the project environment",
|
|
144
|
+
inputs: {
|
|
145
|
+
"node-version": { description: "Node.js version", required: false, default: "20" },
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
action.addStep({ name: "Install deps", run: "npm ci", shell: "bash" });
|
|
149
|
+
action.build("setup");
|
|
150
|
+
|
|
151
|
+
// Reference the composite action in a workflow
|
|
152
|
+
const job = new Job("ubuntu-latest")
|
|
153
|
+
.addStep(CallAction.from(action).toJSON());
|
|
154
|
+
|
|
155
|
+
const workflow = new Workflow({
|
|
156
|
+
name: "CI",
|
|
157
|
+
on: { push: {} },
|
|
158
|
+
}).addJob("build", job);
|
|
159
|
+
|
|
160
|
+
workflow.build("ci");
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Reusable Workflows
|
|
164
|
+
|
|
165
|
+
Call reusable workflows using `CallJob`:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { CallJob, Workflow } from "../generated/index.js";
|
|
169
|
+
|
|
170
|
+
const deploy = new CallJob("./.github/workflows/deploy.yml")
|
|
171
|
+
.with({ environment: "production" })
|
|
172
|
+
.secrets("inherit")
|
|
173
|
+
.needs(["build"]);
|
|
174
|
+
|
|
175
|
+
const workflow = new Workflow({
|
|
176
|
+
name: "Release",
|
|
177
|
+
on: { push: { tags: ["v*"] } },
|
|
178
|
+
}).addJob("deploy", deploy);
|
|
179
|
+
|
|
180
|
+
workflow.build("release");
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Job Options
|
|
184
|
+
|
|
185
|
+
The `Job` constructor accepts an optional second argument for additional configuration:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const job = new Job("ubuntu-latest", {
|
|
189
|
+
needs: ["setup"],
|
|
190
|
+
env: { NODE_ENV: "test" },
|
|
191
|
+
"timeout-minutes": 30,
|
|
192
|
+
"continue-on-error": true,
|
|
193
|
+
permissions: { contents: "read" },
|
|
194
|
+
strategy: {
|
|
195
|
+
matrix: { node: ["18", "20", "22"] },
|
|
196
|
+
"fail-fast": false,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Builder methods are also available:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const job = new Job("ubuntu-latest")
|
|
205
|
+
.addStep({ name: "Test", run: "npm test" })
|
|
206
|
+
.needs(["setup"])
|
|
207
|
+
.env({ CI: "true" })
|
|
208
|
+
.when("github.event_name == 'push'")
|
|
209
|
+
.permissions({ contents: "read" })
|
|
210
|
+
.outputs({ result: "${{ steps.test.outputs.result }}" })
|
|
211
|
+
.strategy({ matrix: { os: ["ubuntu-latest", "macos-latest"] } })
|
|
212
|
+
.continueOnError(true)
|
|
213
|
+
.timeoutMinutes(30);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Commands
|
|
217
|
+
|
|
218
|
+
### `gaji init`
|
|
219
|
+
|
|
220
|
+
Initialize a new gaji project. Detects the project state (empty, existing project, or has YAML workflows) and sets up accordingly.
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
gaji init [OPTIONS]
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| Option | Description |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `--force` | Overwrite existing files |
|
|
229
|
+
| `--skip-examples` | Skip example workflow creation |
|
|
230
|
+
| `--migrate` | Migrate existing YAML workflows to TypeScript |
|
|
231
|
+
| `-i, --interactive` | Interactive mode |
|
|
232
|
+
|
|
233
|
+
### `gaji dev`
|
|
234
|
+
|
|
235
|
+
Start development mode. Scans workflow files for action references and generates types.
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
gaji dev [OPTIONS]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
| Option | Description |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `-d, --dir <DIR>` | Directory to scan (default: `workflows`) |
|
|
244
|
+
| `--watch` | Keep watching for changes after the initial scan |
|
|
245
|
+
|
|
246
|
+
### `gaji build`
|
|
247
|
+
|
|
248
|
+
Build TypeScript workflows to YAML.
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
gaji build [OPTIONS]
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Option | Description |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `-i, --input <DIR>` | Input directory containing TypeScript workflows (default: `workflows`) |
|
|
257
|
+
| `-o, --output <DIR>` | Output directory for YAML files (default: `.github`) |
|
|
258
|
+
| `--dry-run` | Preview YAML output without writing files |
|
|
259
|
+
|
|
260
|
+
Output files are placed in subdirectories based on type:
|
|
261
|
+
- Workflows: `.github/workflows/<id>.yml`
|
|
262
|
+
- Composite actions: `.github/actions/<id>/action.yml`
|
|
263
|
+
|
|
264
|
+
### `gaji add <action>`
|
|
265
|
+
|
|
266
|
+
Add a new action and generate types.
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
gaji add actions/checkout@v5
|
|
270
|
+
gaji add actions/setup-node@v4
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### `gaji clean`
|
|
274
|
+
|
|
275
|
+
Clean generated files.
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
gaji clean [OPTIONS]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Option | Description |
|
|
282
|
+
|---|---|
|
|
283
|
+
| `--cache` | Also clean cache |
|
|
284
|
+
|
|
285
|
+
## Configuration
|
|
286
|
+
|
|
287
|
+
### `.gaji.toml`
|
|
288
|
+
|
|
289
|
+
Project-level configuration file. Created automatically by `gaji init`.
|
|
290
|
+
|
|
291
|
+
```toml
|
|
292
|
+
[project]
|
|
293
|
+
workflows_dir = "workflows" # TypeScript workflow source directory
|
|
294
|
+
output_dir = ".github" # Output directory for generated YAML
|
|
295
|
+
generated_dir = "generated" # Directory for generated type definitions
|
|
296
|
+
|
|
297
|
+
[watch]
|
|
298
|
+
debounce_ms = 300 # Debounce delay for file watcher
|
|
299
|
+
ignored_patterns = ["node_modules", ".git", "generated"]
|
|
300
|
+
|
|
301
|
+
[build]
|
|
302
|
+
validate = true # Validate workflow YAML (requires 'on' and 'jobs')
|
|
303
|
+
format = true # Format YAML output
|
|
304
|
+
|
|
305
|
+
[github]
|
|
306
|
+
token = "ghp_..." # GitHub token (prefer .gaji.local.toml for this)
|
|
307
|
+
api_url = "https://github.example.com" # GitHub Enterprise URL
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### `.gaji.local.toml`
|
|
311
|
+
|
|
312
|
+
Local overrides for sensitive values. Add this to `.gitignore`.
|
|
313
|
+
|
|
314
|
+
```toml
|
|
315
|
+
[github]
|
|
316
|
+
token = "ghp_your_token_here"
|
|
317
|
+
api_url = "https://github.example.com" # for GitHub Enterprise
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Token resolution priority: `GITHUB_TOKEN` env var > `.gaji.local.toml` > `.gaji.toml`
|
|
321
|
+
|
|
322
|
+
## Documentation
|
|
323
|
+
|
|
324
|
+
📚 **[Full Documentation](https://gaji.gaebalgom.work)** (English & 한국어)
|
|
325
|
+
|
|
326
|
+
- [Getting Started](https://gaji.gaebalgom.work/guide/getting-started)
|
|
327
|
+
- [Writing Workflows](https://gaji.gaebalgom.work/guide/writing-workflows)
|
|
328
|
+
- [CLI Reference](https://gaji.gaebalgom.work/reference/cli)
|
|
329
|
+
- [API Reference](https://gaji.gaebalgom.work/reference/api)
|
|
330
|
+
- [Examples](examples/)
|
|
331
|
+
|
|
332
|
+
## Examples
|
|
333
|
+
|
|
334
|
+
Check out the [examples/](examples/) directory for complete working examples:
|
|
335
|
+
|
|
336
|
+
- **[ts-package](examples/ts-package/)** - TypeScript package with gaji CI workflow using pnpm
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
MIT License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gaji",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Type-safe GitHub Actions workflows in TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"provenance": true
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|
|
36
|
-
"@gaji/linux-x64": "0.
|
|
37
|
-
"@gaji/linux-arm64": "0.
|
|
38
|
-
"@gaji/darwin-x64": "0.
|
|
39
|
-
"@gaji/darwin-arm64": "0.
|
|
40
|
-
"@gaji/win32-x64": "0.
|
|
36
|
+
"@gaji/linux-x64": "0.3.0",
|
|
37
|
+
"@gaji/linux-arm64": "0.3.0",
|
|
38
|
+
"@gaji/darwin-x64": "0.3.0",
|
|
39
|
+
"@gaji/darwin-arm64": "0.3.0",
|
|
40
|
+
"@gaji/win32-x64": "0.3.0"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"github-actions",
|