draftpkg 0.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/dist/cli.mjs +53 -0
- package/package.json +21 -0
- package/src/__tests__/setup.test.ts +115 -0
- package/src/cli.ts +39 -0
- package/src/executor.ts +8 -0
- package/src/setup.ts +38 -0
- package/tsconfig.json +10 -0
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/cli.ts
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
// src/setup.ts
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
async function setup(executor) {
|
|
8
|
+
try {
|
|
9
|
+
await executor.run("wrangler whoami");
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"Not logged in to Wrangler. Run `wrangler login` first."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
await executor.run("wrangler kv namespace create METADATA");
|
|
16
|
+
await executor.run("wrangler r2 bucket create draftpkg-tarballs");
|
|
17
|
+
const { stdout } = await executor.run("wrangler deploy");
|
|
18
|
+
const urlMatch = stdout.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
19
|
+
const workerUrl = urlMatch?.[0] ?? "";
|
|
20
|
+
const apiKey = crypto.randomUUID();
|
|
21
|
+
await executor.run(
|
|
22
|
+
`echo "${apiKey}" | wrangler secret put API_KEY`
|
|
23
|
+
);
|
|
24
|
+
return { workerUrl, apiKey };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/cli.ts
|
|
28
|
+
var execAsync = promisify(exec);
|
|
29
|
+
function createExecutor() {
|
|
30
|
+
return {
|
|
31
|
+
async run(command2) {
|
|
32
|
+
return execAsync(command2);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var command = process.argv[2];
|
|
37
|
+
if (command !== "setup") {
|
|
38
|
+
console.error(`Unknown command: ${command ?? "(none)"}`);
|
|
39
|
+
console.error("Usage: draftpkg setup");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
console.log("Setting up Draftpkg...\n");
|
|
43
|
+
setup(createExecutor()).then((result) => {
|
|
44
|
+
console.log("\nDraftpkg is ready!\n");
|
|
45
|
+
console.log(` Worker URL: ${result.workerUrl}`);
|
|
46
|
+
console.log(` API Key: ${result.apiKey}`);
|
|
47
|
+
console.log("\nAdd these as secrets in your GitHub repo:");
|
|
48
|
+
console.log(" DRAFTPKG_WORKER_URL");
|
|
49
|
+
console.log(" DRAFTPKG_API_KEY");
|
|
50
|
+
}).catch((error) => {
|
|
51
|
+
console.error(error.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "draftpkg",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"draftpkg": "dist/cli.mjs"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "esbuild src/cli.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/cli.mjs",
|
|
10
|
+
"check-types": "tsc --noEmit",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@repo/typescript-config": "workspace:*",
|
|
16
|
+
"@types/node": "^25.5.0",
|
|
17
|
+
"esbuild": "^0.27.4",
|
|
18
|
+
"typescript": "^5.9.2",
|
|
19
|
+
"vitest": "^3.2.1"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import type { ExecResult, Executor } from '../executor';
|
|
4
|
+
import { setup } from '../setup';
|
|
5
|
+
|
|
6
|
+
describe('setup', () => {
|
|
7
|
+
let executor: ReturnType<typeof createFakeExecutor>;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
executor = createFakeExecutor({
|
|
11
|
+
'wrangler whoami': 'You are logged in as test@example.com',
|
|
12
|
+
'wrangler kv namespace create METADATA': 'id = "kv-namespace-id-123"',
|
|
13
|
+
'wrangler r2 bucket create draftpkg-tarballs':
|
|
14
|
+
'Created bucket draftpkg-tarballs',
|
|
15
|
+
'wrangler deploy': 'Deployed to https://draftpkg-worker.test.workers.dev',
|
|
16
|
+
'wrangler secret put API_KEY': 'Success',
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('checks wrangler login', async () => {
|
|
21
|
+
await setup(executor);
|
|
22
|
+
|
|
23
|
+
expect(executor.calls[0]!.command).toBe('wrangler whoami');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('creates a KV namespace', async () => {
|
|
27
|
+
await setup(executor);
|
|
28
|
+
|
|
29
|
+
expect(
|
|
30
|
+
executor.calls.some((c) =>
|
|
31
|
+
c.command.includes('kv namespace create METADATA'),
|
|
32
|
+
),
|
|
33
|
+
).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('creates an R2 bucket', async () => {
|
|
37
|
+
await setup(executor);
|
|
38
|
+
|
|
39
|
+
expect(
|
|
40
|
+
executor.calls.some((c) =>
|
|
41
|
+
c.command.includes('r2 bucket create draftpkg-tarballs'),
|
|
42
|
+
),
|
|
43
|
+
).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('deploys the worker', async () => {
|
|
47
|
+
await setup(executor);
|
|
48
|
+
|
|
49
|
+
expect(
|
|
50
|
+
executor.calls.some((c) => c.command.includes('wrangler deploy')),
|
|
51
|
+
).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('sets the API key as a secret', async () => {
|
|
55
|
+
await setup(executor);
|
|
56
|
+
|
|
57
|
+
expect(
|
|
58
|
+
executor.calls.some((c) => c.command.includes('wrangler secret put API_KEY')),
|
|
59
|
+
).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('returns the worker URL and API key', async () => {
|
|
63
|
+
const result = await setup(executor);
|
|
64
|
+
|
|
65
|
+
expect(result.workerUrl).toMatch(/^https:\/\//);
|
|
66
|
+
expect(result.apiKey).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('throws if not logged in', async () => {
|
|
70
|
+
executor = createFakeExecutor({});
|
|
71
|
+
executor.run = async (command) => {
|
|
72
|
+
executor.calls.push({ command });
|
|
73
|
+
if (command === 'wrangler whoami') {
|
|
74
|
+
throw new Error('Not logged in');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { stdout: '', stderr: '' };
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
await expect(setup(executor)).rejects.toThrow('logged in');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('runs steps in the correct order', async () => {
|
|
84
|
+
await setup(executor);
|
|
85
|
+
|
|
86
|
+
const commands = executor.calls.map((c) => c.command);
|
|
87
|
+
const whoamiIdx = commands.findIndex((c) => c.includes('whoami'));
|
|
88
|
+
const kvIdx = commands.findIndex((c) => c.includes('kv namespace create'));
|
|
89
|
+
const r2Idx = commands.findIndex((c) => c.includes('r2 bucket create'));
|
|
90
|
+
const deployIdx = commands.findIndex((c) => c.includes('wrangler deploy'));
|
|
91
|
+
|
|
92
|
+
expect(whoamiIdx).toBeLessThan(kvIdx);
|
|
93
|
+
expect(kvIdx).toBeLessThan(r2Idx);
|
|
94
|
+
expect(r2Idx).toBeLessThan(deployIdx);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
interface RecordedCall {
|
|
99
|
+
command: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function createFakeExecutor(
|
|
103
|
+
responses: Record<string, string> = {},
|
|
104
|
+
): Executor & { calls: RecordedCall[] } {
|
|
105
|
+
const calls: RecordedCall[] = [];
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
calls,
|
|
109
|
+
async run(command: string): Promise<ExecResult> {
|
|
110
|
+
calls.push({ command });
|
|
111
|
+
|
|
112
|
+
return { stdout: responses[command] ?? '', stderr: '' };
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
import type { Executor } from "./executor";
|
|
5
|
+
import { setup } from "./setup";
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
function createExecutor(): Executor {
|
|
10
|
+
return {
|
|
11
|
+
async run(command) {
|
|
12
|
+
return execAsync(command);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const command = process.argv[2];
|
|
18
|
+
|
|
19
|
+
if (command !== "setup") {
|
|
20
|
+
console.error(`Unknown command: ${command ?? "(none)"}`);
|
|
21
|
+
console.error("Usage: draftpkg setup");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log("Setting up Draftpkg...\n");
|
|
26
|
+
|
|
27
|
+
setup(createExecutor())
|
|
28
|
+
.then((result) => {
|
|
29
|
+
console.log("\nDraftpkg is ready!\n");
|
|
30
|
+
console.log(` Worker URL: ${result.workerUrl}`);
|
|
31
|
+
console.log(` API Key: ${result.apiKey}`);
|
|
32
|
+
console.log("\nAdd these as secrets in your GitHub repo:");
|
|
33
|
+
console.log(" DRAFTPKG_WORKER_URL");
|
|
34
|
+
console.log(" DRAFTPKG_API_KEY");
|
|
35
|
+
})
|
|
36
|
+
.catch((error) => {
|
|
37
|
+
console.error(error.message);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
package/src/executor.ts
ADDED
package/src/setup.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { Executor } from "./executor";
|
|
4
|
+
|
|
5
|
+
export interface SetupResult {
|
|
6
|
+
workerUrl: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function setup(executor: Executor): Promise<SetupResult> {
|
|
11
|
+
// 1. Check login
|
|
12
|
+
try {
|
|
13
|
+
await executor.run("wrangler whoami");
|
|
14
|
+
} catch {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"Not logged in to Wrangler. Run `wrangler login` first.",
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 2. Create KV namespace
|
|
21
|
+
await executor.run("wrangler kv namespace create METADATA");
|
|
22
|
+
|
|
23
|
+
// 3. Create R2 bucket
|
|
24
|
+
await executor.run("wrangler r2 bucket create draftpkg-tarballs");
|
|
25
|
+
|
|
26
|
+
// 4. Deploy worker
|
|
27
|
+
const { stdout } = await executor.run("wrangler deploy");
|
|
28
|
+
const urlMatch = stdout.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
29
|
+
const workerUrl = urlMatch?.[0] ?? "";
|
|
30
|
+
|
|
31
|
+
// 5. Generate and set API key
|
|
32
|
+
const apiKey = crypto.randomUUID();
|
|
33
|
+
await executor.run(
|
|
34
|
+
`echo "${apiKey}" | wrangler secret put API_KEY`,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return { workerUrl, apiKey };
|
|
38
|
+
}
|