@alien-protocol/cannon 2.2.2
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/.env.example +12 -0
- package/README.md +395 -0
- package/bin/cli.js +410 -0
- package/cannon.config.example.json +17 -0
- package/package.json +72 -0
- package/src/auth.js +188 -0
- package/src/cannon.js +401 -0
- package/src/config.js +131 -0
- package/src/github.js +167 -0
- package/src/index.js +5 -0
- package/src/loaders/csv.js +70 -0
- package/src/loaders/docx.js +45 -0
- package/src/loaders/index.js +51 -0
- package/src/loaders/json.js +10 -0
- package/src/loaders/mysql.js +30 -0
- package/src/loaders/pdf.js +62 -0
- package/src/loaders/postgres.js +32 -0
- package/src/loaders/sqlite.js +32 -0
package/.env.example
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────
|
|
2
|
+
# .env.example — copy to .env (never commit .env!)
|
|
3
|
+
# ─────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
# Your GitHub Personal Access Token
|
|
6
|
+
# Create one at: https://github.com/settings/tokens/new
|
|
7
|
+
# Required scope: repo (or public_repo for public repos only)
|
|
8
|
+
GITHUB_TOKEN=ghp_your_token_here
|
|
9
|
+
|
|
10
|
+
# Database connection strings (only needed for DB sources)
|
|
11
|
+
POSTGRES_URL=postgres://user:password@localhost:5432/mydb
|
|
12
|
+
MYSQL_URL=mysql://user:password@localhost:3306/mydb
|
package/README.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# 🛸 @alien-protocol/cannon
|
|
2
|
+
|
|
3
|
+
> Bulk-create GitHub issues from **CSV, PDF, DOCX, JSON, or any SQL database** — one config file, safe delays, duplicate detection, and resume support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
**Option A — Install globally (recommended)**
|
|
8
|
+
Run once, then use `cannon` anywhere:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @alien-protocol/cannon
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Option B — No install, use npx**
|
|
15
|
+
Prefix every command with `npx @alien-protocol/cannon`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @alien-protocol/cannon init
|
|
19
|
+
npx @alien-protocol/cannon fire --preview
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
> All examples in this README use the global `cannon` command.
|
|
23
|
+
> If you chose Option B, just add `npx @alien-protocol/cannon` in front of each one.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 1. Create your config file
|
|
31
|
+
cannon init
|
|
32
|
+
|
|
33
|
+
# 2. Login with GitHub (no token needed)
|
|
34
|
+
cannon auth login
|
|
35
|
+
|
|
36
|
+
# 3. Preview — nothing gets created
|
|
37
|
+
cannon fire --preview
|
|
38
|
+
|
|
39
|
+
# 4. Create your issues
|
|
40
|
+
cannon fire
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## The Config File
|
|
46
|
+
|
|
47
|
+
`cannon init` creates this in your project. Edit it, then run `cannon fire`.
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"source": {
|
|
52
|
+
"//": "type options: csv | json | pdf | docx | sqlite | postgres | mysql",
|
|
53
|
+
"type": "csv",
|
|
54
|
+
"file": "./issues.csv"
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
"mode": {
|
|
58
|
+
"//": "safeMode adds random delays — always keep true for large batches",
|
|
59
|
+
"safeMode": true,
|
|
60
|
+
"dryRun": false,
|
|
61
|
+
"resumable": true
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"delay": {
|
|
65
|
+
"//": "minutes between each issue — only used when safeMode is true",
|
|
66
|
+
"min": 4,
|
|
67
|
+
"max": 8
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
"labels": {
|
|
71
|
+
"//": "autoCreate will create missing labels in GitHub automatically",
|
|
72
|
+
"autoCreate": false,
|
|
73
|
+
"colors": {
|
|
74
|
+
"bug": "ee0701",
|
|
75
|
+
"enhancement": "0075ca",
|
|
76
|
+
"documentation": "0052cc",
|
|
77
|
+
"security": "e11d48",
|
|
78
|
+
"performance": "f97316",
|
|
79
|
+
"accessibility": "8b5cf6"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
"output": {
|
|
84
|
+
"//": "logFile path to save a JSON log after each run — leave blank to skip",
|
|
85
|
+
"logFile": "",
|
|
86
|
+
"showTable": true
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
"notify": {
|
|
90
|
+
"//": "webhookUrl accepts any Slack / Discord / Teams incoming webhook URL",
|
|
91
|
+
"webhookUrl": "",
|
|
92
|
+
"onSuccess": true,
|
|
93
|
+
"onFailure": true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Config Options
|
|
99
|
+
|
|
100
|
+
| Key | Default | Description |
|
|
101
|
+
| ------------------------- | -------------- | ----------------------------------------------------------------- |
|
|
102
|
+
| `source.type` | `csv` | `csv` · `json` · `pdf` · `docx` · `sqlite` · `postgres` · `mysql` |
|
|
103
|
+
| `source.file` | `./issues.csv` | Path to your issues file |
|
|
104
|
+
| `source.query` | — | SQL query (database sources only) |
|
|
105
|
+
| `source.connectionString` | — | DB connection URL — use `${ENV_VAR}`, never hardcode |
|
|
106
|
+
| `mode.safeMode` | `true` | Random delays between issues — keeps you off GitHub's radar |
|
|
107
|
+
| `mode.dryRun` | `false` | Preview only, nothing created |
|
|
108
|
+
| `mode.resumable` | `true` | Saves progress so you can stop and restart safely |
|
|
109
|
+
| `delay.min` | `4` | Minimum minutes between issues |
|
|
110
|
+
| `delay.max` | `8` | Maximum minutes between issues |
|
|
111
|
+
| `labels.autoCreate` | `false` | Auto-create missing labels in GitHub |
|
|
112
|
+
| `labels.colors` | see above | Label name → hex color for auto-creation |
|
|
113
|
+
| `output.logFile` | — | Save a JSON results log to this path |
|
|
114
|
+
| `output.showTable` | `true` | Show a summary table after completion |
|
|
115
|
+
| `notify.webhookUrl` | — | Slack / Discord / Teams webhook URL |
|
|
116
|
+
| `notify.onSuccess` | `true` | Notify when batch completes successfully |
|
|
117
|
+
| `notify.onFailure` | `true` | Notify when any issues fail |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Commands
|
|
122
|
+
|
|
123
|
+
### `cannon init`
|
|
124
|
+
|
|
125
|
+
Creates `cannon.config.json` with defaults. Edit it, then fire.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
cannon init # create config
|
|
129
|
+
cannon init --force # overwrite existing config
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### `cannon auth`
|
|
135
|
+
|
|
136
|
+
Secure GitHub login — no token copying needed.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
cannon auth login # login via GitHub OAuth
|
|
140
|
+
cannon auth status # show who you're logged in as
|
|
141
|
+
cannon auth logout # remove saved credentials
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
How it works: cannon shows you a short code → you enter it at `github.com/login/device` → done. Token is saved to `~/.cannon/credentials.json`, never in your project.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### `cannon validate`
|
|
149
|
+
|
|
150
|
+
Checks everything before you fire — catches problems early.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
cannon validate
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Checks your config is valid JSON · token is present · source file exists · issues can be loaded.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### `cannon fire`
|
|
161
|
+
|
|
162
|
+
Create your issues.
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
cannon fire # run using cannon.config.json
|
|
166
|
+
cannon fire --preview # dry run — nothing created, no delays
|
|
167
|
+
cannon fire --unsafe # no delays (fast but risky)
|
|
168
|
+
cannon fire --delay 2 # fixed 2-minute delay between issues
|
|
169
|
+
cannon fire --fresh # ignore saved progress, start over
|
|
170
|
+
|
|
171
|
+
# override source without editing config
|
|
172
|
+
cannon fire -s csv -f ./issues.csv
|
|
173
|
+
cannon fire -s json -f ./issues.json --preview
|
|
174
|
+
cannon fire -s docx -f ./issues.docx --delay 1
|
|
175
|
+
cannon fire -s pdf -f ./issues.pdf --unsafe
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Flag | What it does |
|
|
179
|
+
| ---------------- | ------------------------------------------------------- |
|
|
180
|
+
| `--preview` | Dry run — shows what would be created, skips all delays |
|
|
181
|
+
| `--unsafe` | No delays at all — fast but GitHub may flag as spam |
|
|
182
|
+
| `--delay <mins>` | Fixed delay in minutes, e.g. `--delay 2` |
|
|
183
|
+
| `--fresh` | Ignore saved progress and start from the beginning |
|
|
184
|
+
| `-s <type>` | Source type override |
|
|
185
|
+
| `-f <path>` | Source file override |
|
|
186
|
+
| `-q <sql>` | SQL query override (database sources) |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Safe Mode vs Unsafe Mode
|
|
191
|
+
|
|
192
|
+
| | Safe (`safeMode: true`) | Unsafe (`safeMode: false`) |
|
|
193
|
+
| ---------------- | -------------------------- | -------------------------- |
|
|
194
|
+
| Delays | Random, 4–8 min by default | None |
|
|
195
|
+
| GitHub spam risk | Low | High |
|
|
196
|
+
| Recommended for | All real runs | Testing only |
|
|
197
|
+
|
|
198
|
+
Safe mode is on by default. For large batches never turn it off.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Issue Sources
|
|
203
|
+
|
|
204
|
+
Every source needs at minimum: `repo` and `title`.
|
|
205
|
+
|
|
206
|
+
| Field | Required | Description |
|
|
207
|
+
| ----------- | -------- | ----------------------------------------- |
|
|
208
|
+
| `repo` | ✅ | `owner/repo` |
|
|
209
|
+
| `title` | ✅ | Issue title |
|
|
210
|
+
| `body` | — | Description |
|
|
211
|
+
| `labels` | — | Comma-separated: `bug,auth` |
|
|
212
|
+
| `milestone` | — | Auto-created if it doesn't exist |
|
|
213
|
+
| `priority` | — | `HIGH` · `MED` · `LOW` (informational) |
|
|
214
|
+
| `track` | — | e.g. `auth`, `ui`, `docs` (informational) |
|
|
215
|
+
|
|
216
|
+
### CSV
|
|
217
|
+
|
|
218
|
+
```csv
|
|
219
|
+
repo,title,body,labels,milestone
|
|
220
|
+
owner/repo,Fix login bug,"Steps to reproduce...",bug,v1.0
|
|
221
|
+
owner/repo,Add dark mode,"User request",enhancement,v1.1
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
"source": { "type": "csv", "file": "./issues.csv" }
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### JSON
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
[
|
|
232
|
+
{
|
|
233
|
+
"repo": "owner/repo",
|
|
234
|
+
"title": "Fix login bug",
|
|
235
|
+
"body": "Steps to reproduce...",
|
|
236
|
+
"labels": "bug,auth",
|
|
237
|
+
"milestone": "v1.0"
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
"source": { "type": "json", "file": "./issues.json" }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### PDF
|
|
247
|
+
|
|
248
|
+
Two layouts are auto-detected.
|
|
249
|
+
|
|
250
|
+
**Block layout:**
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
REPO: owner/repo
|
|
254
|
+
TITLE: Fix login bug
|
|
255
|
+
BODY: Steps to reproduce.
|
|
256
|
+
LABELS: bug, auth
|
|
257
|
+
MILESTONE: v1.0
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Table layout** (pipe-separated):
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
repo | title | body | labels
|
|
264
|
+
owner/repo | Fix login bug | Steps | bug,auth
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
"source": { "type": "pdf", "file": "./issues.pdf" }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### DOCX
|
|
272
|
+
|
|
273
|
+
A table in your Word file. First row = headers.
|
|
274
|
+
|
|
275
|
+
| repo | title | body | labels |
|
|
276
|
+
| ---------- | ------------- | -------- | -------- |
|
|
277
|
+
| owner/repo | Fix login bug | Steps... | bug,auth |
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
"source": { "type": "docx", "file": "./issues.docx" }
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### PostgreSQL
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
# .env
|
|
287
|
+
POSTGRES_URL=postgres://user:password@localhost:5432/mydb
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
"source": {
|
|
292
|
+
"type": "postgres",
|
|
293
|
+
"connectionString": "${POSTGRES_URL}",
|
|
294
|
+
"query": "SELECT repo, title, body, labels, milestone FROM backlog WHERE exported = false"
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### MySQL
|
|
299
|
+
|
|
300
|
+
```json
|
|
301
|
+
"source": {
|
|
302
|
+
"type": "mysql",
|
|
303
|
+
"connectionString": "${MYSQL_URL}",
|
|
304
|
+
"query": "SELECT repo, title, body, labels FROM issues WHERE status = 'pending'"
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### SQLite
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
"source": {
|
|
312
|
+
"type": "sqlite",
|
|
313
|
+
"file": "./backlog.db",
|
|
314
|
+
"query": "SELECT repo, title, body, labels FROM issues"
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Token Security
|
|
321
|
+
|
|
322
|
+
### Minimum required scope
|
|
323
|
+
|
|
324
|
+
- `public_repo` — if all your repos are public
|
|
325
|
+
- `repo` — if any repo is private
|
|
326
|
+
- Fine-grained PAT — grant only `Issues: Read & Write` + `Metadata: Read` on specific repos
|
|
327
|
+
|
|
328
|
+
### How the token is stored
|
|
329
|
+
|
|
330
|
+
`cannon auth login` saves your token to `~/.cannon/credentials.json` with `chmod 600` — only your user can read it. It is never written to your project directory.
|
|
331
|
+
|
|
332
|
+
### Token resolution order
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
1. new IssueCannon({ token: '...' }) ← programmatic
|
|
336
|
+
2. GITHUB_TOKEN env var
|
|
337
|
+
3. .env file → GITHUB_TOKEN=ghp_xxx
|
|
338
|
+
4. cannon auth login → ~/.cannon/credentials.json
|
|
339
|
+
5. cannon.config.json github.token ← last resort, not recommended
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### `.gitignore` — add these
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
.env
|
|
346
|
+
.cannon_state.json
|
|
347
|
+
cannon-log.json
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
`cannon.config.json` is safe to commit as long as `github.token` is blank.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Programmatic Usage
|
|
355
|
+
|
|
356
|
+
```js
|
|
357
|
+
import { IssueCannon } from '@alien-protocol/cannon';
|
|
358
|
+
|
|
359
|
+
const cannon = new IssueCannon({
|
|
360
|
+
safeMode: true,
|
|
361
|
+
dryRun: false,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const { created, failed } = await cannon.fire({
|
|
365
|
+
source: 'csv',
|
|
366
|
+
file: './issues.csv',
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
console.log(`Created: ${created.length} Failed: ${failed.length}`);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Roadmap
|
|
375
|
+
|
|
376
|
+
| Feature | Description |
|
|
377
|
+
| ------------------------- | --------------------------------------------------------------------------------- |
|
|
378
|
+
| `cannon retry` | Re-run only the failed issues from the last batch |
|
|
379
|
+
| Issue templates | Define a `bodyTemplate` in config with `{{variables}}` per issue |
|
|
380
|
+
| Webhook notify | POST a summary to Slack/Discord/Teams on completion _(config ready, coming soon)_ |
|
|
381
|
+
| `cannon serve` | Local web dashboard with live progress and retry button |
|
|
382
|
+
| Fuzzy duplicate detection | Catch near-duplicate titles, not just exact matches |
|
|
383
|
+
| Milestone auto-close | Auto-close milestones once all their issues are created |
|
|
384
|
+
| AI issue enhancement | Improve titles and bodies via Anthropic API before creating |
|
|
385
|
+
| GitHub App auth | Org-wide auth without individual user tokens |
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## Maintainer
|
|
390
|
+
|
|
391
|
+
[ryzen-xp](https://github.com/ryzen-xp)
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
MIT
|