shopify-tunnel-dev 0.2.0 → 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 +8 -6
- package/bin/shopify-tunnel-dev.js +21 -2
- package/package.json +1 -1
- package/src/scaffold-project.js +44 -0
package/README.md
CHANGED
|
@@ -47,18 +47,20 @@ pnpm add -D shopify-tunnel-dev
|
|
|
47
47
|
"scripts": { "dev": "shopify-tunnel-dev" }
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
.cloudflared/
|
|
50
|
+
```bash
|
|
51
|
+
npx shopify-tunnel-dev init
|
|
54
52
|
```
|
|
55
53
|
|
|
54
|
+
`init` creates `.cloudflared/account.json` (empty template, mode 600), adds `.cloudflared/` to
|
|
55
|
+
`.gitignore`, and adds `DEV_HOST=` / `DEV_PORT=` to `.env`. It never overwrites anything, and
|
|
56
|
+
`pnpm dev` runs it automatically in a fresh clone.
|
|
57
|
+
|
|
56
58
|
Give each dev their own app in the Partner Dashboard; the app URL lives on Shopify, not in the tunnel.
|
|
57
59
|
|
|
58
60
|
## Per dev
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
(`chmod 600 .cloudflared/*`)
|
|
62
|
+
Run `pnpm dev` once to scaffold, then fill `.cloudflared/account.json` and add `.cloudflared/cert.pem`
|
|
63
|
+
from the password manager (`chmod 600 .cloudflared/*`). Set in `.env`:
|
|
62
64
|
|
|
63
65
|
```
|
|
64
66
|
DEV_HOST=dev-myapp-alice.example.com
|
|
@@ -3,9 +3,15 @@ import { spawn } from 'node:child_process';
|
|
|
3
3
|
import { resolveCloudflared } from '../src/cloudflared-binary.js';
|
|
4
4
|
import { ConfigError, loadConfig } from '../src/load-config.js';
|
|
5
5
|
import { stopCloudflared } from '../src/run-cloudflared.js';
|
|
6
|
+
import { scaffoldProject } from '../src/scaffold-project.js';
|
|
6
7
|
import { startNamedTunnel, startQuickTunnel } from '../src/start-tunnel.js';
|
|
7
8
|
|
|
8
9
|
const USAGE = `Usage: shopify-tunnel-dev [dev] [options] [-- shopify app dev args]
|
|
10
|
+
shopify-tunnel-dev init
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
dev Start the tunnel and shopify app dev (default)
|
|
14
|
+
init Create .cloudflared/account.json, gitignore it, add DEV_HOST/DEV_PORT to .env
|
|
9
15
|
|
|
10
16
|
Options:
|
|
11
17
|
-q, --quick Random *.trycloudflare.com URL, no Cloudflare credentials needed
|
|
@@ -14,9 +20,13 @@ Options:
|
|
|
14
20
|
|
|
15
21
|
Any other argument (e.g. --reset, --store=foo) is passed to \`shopify app dev\`.`;
|
|
16
22
|
|
|
23
|
+
const NEXT_STEPS =
|
|
24
|
+
'set DEV_HOST and DEV_PORT in .env, fill .cloudflared/account.json and add .cloudflared/cert.pem';
|
|
25
|
+
|
|
17
26
|
function parseArgs(argv) {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
27
|
+
const init = argv[0] === 'init';
|
|
28
|
+
const args = argv[0] === 'dev' || init ? argv.slice(1) : argv;
|
|
29
|
+
const opts = { init, quick: false, force: false, help: false, passthrough: [] };
|
|
20
30
|
for (const arg of args) {
|
|
21
31
|
if (arg === '-q' || arg === '--quick') opts.quick = true;
|
|
22
32
|
else if (arg === '-f' || arg === '--force') opts.force = true;
|
|
@@ -29,6 +39,15 @@ function parseArgs(argv) {
|
|
|
29
39
|
async function main() {
|
|
30
40
|
const opts = parseArgs(process.argv.slice(2));
|
|
31
41
|
if (opts.help) return console.log(USAGE);
|
|
42
|
+
// `init` scaffolds explicitly; a named `dev` run in a fresh clone does the same before complaining,
|
|
43
|
+
// so the config error points at files that already exist.
|
|
44
|
+
if (opts.init || !opts.quick) {
|
|
45
|
+
const changes = scaffoldProject();
|
|
46
|
+
changes.forEach((change) => console.log(`[init] ${change}`));
|
|
47
|
+
if (changes.length) console.log(`[init] next: ${NEXT_STEPS}`);
|
|
48
|
+
else if (opts.init) console.log('[init] nothing to do');
|
|
49
|
+
if (opts.init) return;
|
|
50
|
+
}
|
|
32
51
|
|
|
33
52
|
const cfg = loadConfig({ quick: opts.quick });
|
|
34
53
|
const bin = await resolveCloudflared();
|
package/package.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseEnv } from 'node:util';
|
|
4
|
+
|
|
5
|
+
const ACCOUNT_TEMPLATE = { CF_ACCOUNT_TAG: '', CF_SECRET_KEY: '', CF_HOST_PATTERN: '' };
|
|
6
|
+
|
|
7
|
+
// Idempotent: only creates what is missing, never overwrites. Returns the list of changes made.
|
|
8
|
+
export function scaffoldProject(cwd = process.cwd()) {
|
|
9
|
+
const changes = [];
|
|
10
|
+
const dir = path.join(cwd, '.cloudflared');
|
|
11
|
+
const accountPath = path.join(dir, 'account.json');
|
|
12
|
+
|
|
13
|
+
if (!existsSync(dir)) {
|
|
14
|
+
mkdirSync(dir, { mode: 0o700 });
|
|
15
|
+
changes.push('created .cloudflared/');
|
|
16
|
+
}
|
|
17
|
+
if (!existsSync(accountPath)) {
|
|
18
|
+
writeFileSync(accountPath, `${JSON.stringify(ACCOUNT_TEMPLATE, null, 2)}\n`, { mode: 0o600 });
|
|
19
|
+
changes.push('created .cloudflared/account.json (fill in from your password manager)');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Keep the shared secrets out of git before anyone pastes them in.
|
|
23
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
24
|
+
const gitignore = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf8') : '';
|
|
25
|
+
if (!/^\/?\.cloudflared\/?(\*)?\s*$/m.test(gitignore)) {
|
|
26
|
+
appendLine(gitignorePath, gitignore, '.cloudflared/');
|
|
27
|
+
changes.push('added .cloudflared/ to .gitignore');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const envPath = [path.join(cwd, 'web/.env'), path.join(cwd, '.env')].find((p) => existsSync(p)) ?? path.join(cwd, '.env');
|
|
31
|
+
const envText = existsSync(envPath) ? readFileSync(envPath, 'utf8') : '';
|
|
32
|
+
const env = parseEnv(envText);
|
|
33
|
+
const missingKeys = ['DEV_HOST', 'DEV_PORT'].filter((key) => !(key in env));
|
|
34
|
+
if (missingKeys.length) {
|
|
35
|
+
appendLine(envPath, envText, missingKeys.map((key) => `${key}=`).join('\n'));
|
|
36
|
+
changes.push(`added ${missingKeys.join(', ')} to ${path.relative(cwd, envPath)}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return changes;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function appendLine(file, current, line) {
|
|
43
|
+
appendFileSync(file, `${current && !current.endsWith('\n') ? '\n' : ''}${line}\n`);
|
|
44
|
+
}
|