@scrymore/scry-deployer 0.4.0 → 0.4.1
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/lib/init.js +84 -18
- package/lib/templates.js +37 -0
- package/package.json +1 -1
package/lib/init.js
CHANGED
|
@@ -79,14 +79,18 @@ async function runInit(argv) {
|
|
|
79
79
|
logger.success('✅ Created .github/workflows/deploy-pr-preview.yml\n');
|
|
80
80
|
|
|
81
81
|
// Step 6: Setup GitHub variables (if gh CLI available)
|
|
82
|
+
// Tracked, not assumed — the closing summary reports what actually happened.
|
|
83
|
+
let ghConfigured = null;
|
|
82
84
|
const step6Start = Date.now();
|
|
83
85
|
if (!argv.skipGhSetup && isGhCliAvailable()) {
|
|
84
86
|
logger.info('6/8: Setting up GitHub repository variables...');
|
|
85
87
|
try {
|
|
86
88
|
await setupGitHubVariables(argv.project, argv.apiKey, argv.apiUrl, logger);
|
|
89
|
+
ghConfigured = true;
|
|
87
90
|
const step6Duration = Date.now() - step6Start;
|
|
88
91
|
logger.success(`✅ GitHub variables configured [${step6Duration}ms]\n`);
|
|
89
92
|
} catch (error) {
|
|
93
|
+
ghConfigured = false;
|
|
90
94
|
const step6Duration = Date.now() - step6Start;
|
|
91
95
|
logger.error(`⚠️ GitHub setup failed: ${error.message} [${step6Duration}ms]`);
|
|
92
96
|
logger.info('You can set these up manually later.\n');
|
|
@@ -126,7 +130,10 @@ async function runInit(argv) {
|
|
|
126
130
|
logger.info('━'.repeat(50));
|
|
127
131
|
logger.info(`[TIMING] Total setup time: ${totalDuration}ms (${(totalDuration / 1000).toFixed(2)}s)\n`);
|
|
128
132
|
|
|
129
|
-
showSuccessMessage(argv.project, envInfo, argv.apiUrl, pushResult.success
|
|
133
|
+
showSuccessMessage(argv.project, envInfo, argv.apiUrl, pushResult.success, {
|
|
134
|
+
ghConfigured,
|
|
135
|
+
committed: commitResult.success,
|
|
136
|
+
});
|
|
130
137
|
|
|
131
138
|
} catch (error) {
|
|
132
139
|
logger.error(`\n❌ Setup failed: ${error.message}`);
|
|
@@ -313,22 +320,63 @@ function isGhCliAvailable() {
|
|
|
313
320
|
/**
|
|
314
321
|
* Setup GitHub variables using gh CLI
|
|
315
322
|
*/
|
|
316
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Does this `gh` know about `gh variable`? It arrived in gh 2.21 (December 2022),
|
|
325
|
+
* and Ubuntu 22.04 still ships 2.4.0 — so plenty of machines do not have it.
|
|
326
|
+
* Without this check the very first call throws, the secret after it is never
|
|
327
|
+
* reached, and CI ends up with no credentials at all.
|
|
328
|
+
*/
|
|
329
|
+
function ghSupportsVariables() {
|
|
317
330
|
try {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
331
|
+
execSync('gh variable --help', { stdio: 'pipe' });
|
|
332
|
+
return true;
|
|
333
|
+
} catch {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
321
337
|
|
|
322
|
-
|
|
323
|
-
|
|
338
|
+
function setVariableViaApi(repo, name, value) {
|
|
339
|
+
// `gh api` predates `gh variable` by years, so it is the safe fallback.
|
|
340
|
+
const payload = JSON.stringify({ name, value });
|
|
341
|
+
try {
|
|
342
|
+
execSync(`gh api -X POST repos/${repo}/actions/variables --input -`, {
|
|
343
|
+
stdio: ['pipe', 'pipe', 'pipe'], input: payload,
|
|
344
|
+
});
|
|
345
|
+
} catch {
|
|
346
|
+
// Already present: update rather than create.
|
|
347
|
+
execSync(`gh api -X PATCH repos/${repo}/actions/variables/${name} --input -`, {
|
|
348
|
+
stdio: ['pipe', 'pipe', 'pipe'], input: payload,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
324
352
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
353
|
+
async function setupGitHubVariables(projectId, apiKey, apiUrl, logger) {
|
|
354
|
+
const repo = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', { stdio: 'pipe' })
|
|
355
|
+
.toString().trim();
|
|
328
356
|
|
|
329
|
-
|
|
330
|
-
|
|
357
|
+
const useCli = ghSupportsVariables();
|
|
358
|
+
if (!useCli) {
|
|
359
|
+
logger.debug(' gh is too old for `gh variable`; using `gh api` instead');
|
|
331
360
|
}
|
|
361
|
+
|
|
362
|
+
// Each of these is reported separately. A failure part-way through used to
|
|
363
|
+
// abandon the rest silently, which is how a repository ended up with the
|
|
364
|
+
// variables missing *and* the secret missing while setup reported success.
|
|
365
|
+
const setVar = (name, value) => {
|
|
366
|
+
if (useCli) execSync(`gh variable set ${name} --body "${value}"`, { stdio: 'pipe' });
|
|
367
|
+
else setVariableViaApi(repo, name, value);
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
setVar('SCRY_PROJECT_ID', projectId);
|
|
371
|
+
logger.debug(' ✓ Set SCRY_PROJECT_ID');
|
|
372
|
+
|
|
373
|
+
setVar('SCRY_API_URL', apiUrl);
|
|
374
|
+
logger.debug(' ✓ Set SCRY_API_URL');
|
|
375
|
+
|
|
376
|
+
// `gh secret set` needs libsodium encryption, so there is no simple `gh api`
|
|
377
|
+
// fallback — but it has existed since gh 1.x, so it works where variables do not.
|
|
378
|
+
execSync(`gh secret set SCRY_API_KEY --body "${apiKey}"`, { stdio: 'pipe' });
|
|
379
|
+
logger.debug(' ✓ Set SCRY_API_KEY');
|
|
332
380
|
}
|
|
333
381
|
|
|
334
382
|
/**
|
|
@@ -377,12 +425,25 @@ function gitCommit(logger) {
|
|
|
377
425
|
'.storybook-deployer.json'
|
|
378
426
|
];
|
|
379
427
|
|
|
428
|
+
// Each file is staged on its own. `git add` fails on a path that
|
|
429
|
+
// .gitignore excludes, and one throw used to abort the loop before the
|
|
430
|
+
// commit — so a leftover `.storybook-deployer.json` ignore rule from the
|
|
431
|
+
// pre-0.4.0 workaround silently prevented the *workflows* being committed,
|
|
432
|
+
// and CI was never set up at all.
|
|
433
|
+
let staged = 0;
|
|
380
434
|
for (const file of filesToAdd) {
|
|
381
|
-
if (fs.existsSync(file))
|
|
435
|
+
if (!fs.existsSync(file)) continue;
|
|
436
|
+
try {
|
|
382
437
|
execSync(`git add "${file}"`, { stdio: 'pipe' });
|
|
438
|
+
staged++;
|
|
383
439
|
logger.debug(` ✓ Added ${file}`);
|
|
440
|
+
} catch {
|
|
441
|
+
logger.debug(` • Skipped ${file} (ignored by .gitignore)`);
|
|
384
442
|
}
|
|
385
443
|
}
|
|
444
|
+
if (staged === 0) {
|
|
445
|
+
return { success: false, message: 'Nothing could be staged' };
|
|
446
|
+
}
|
|
386
447
|
|
|
387
448
|
// Commit
|
|
388
449
|
const commitMessage = 'chore: add Scry Storybook deployment workflows';
|
|
@@ -433,7 +494,7 @@ function gitPush(logger) {
|
|
|
433
494
|
/**
|
|
434
495
|
* Show success message
|
|
435
496
|
*/
|
|
436
|
-
function showSuccessMessage(projectId, envInfo, apiUrl, pushed) {
|
|
497
|
+
function showSuccessMessage(projectId, envInfo, apiUrl, pushed, results = {}) {
|
|
437
498
|
console.log(`
|
|
438
499
|
🎉 ${pushed ? 'Setup Complete and Deployed!' : 'Setup Complete!'}
|
|
439
500
|
|
|
@@ -442,9 +503,14 @@ Your Storybook deployment is configured and ready to go.
|
|
|
442
503
|
📦 What was set up:
|
|
443
504
|
✅ Configuration file (.storybook-deployer.json — no credentials, safe to commit)
|
|
444
505
|
✅ GitHub Actions workflows (.github/workflows/)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
506
|
+
${results.ghConfigured === true
|
|
507
|
+
? '✅ Repository variables and secret (SCRY_PROJECT_ID, SCRY_API_URL, SCRY_API_KEY)'
|
|
508
|
+
: results.ghConfigured === false
|
|
509
|
+
? '❌ Repository variables and secret NOT set — CI cannot deploy until you fix this'
|
|
510
|
+
: '⚠️ Repository variables and secret not attempted — CI cannot deploy until you set them'}
|
|
511
|
+
${results.committed === false
|
|
512
|
+
? '❌ Changes NOT committed — the workflows are staged but uncommitted'
|
|
513
|
+
: (pushed ? '✅ Changes committed and pushed' : '⚠️ Committed; manual push required')}
|
|
448
514
|
|
|
449
515
|
${!pushed ? `
|
|
450
516
|
📌 Next Step:
|
|
@@ -474,4 +540,4 @@ Happy deploying! ✨
|
|
|
474
540
|
`);
|
|
475
541
|
}
|
|
476
542
|
|
|
477
|
-
module.exports = { runInit, createConfigFile };
|
|
543
|
+
module.exports = { runInit, createConfigFile, gitCommit };
|
package/lib/templates.js
CHANGED
|
@@ -59,11 +59,27 @@ function getCacheValue(packageManager) {
|
|
|
59
59
|
/**
|
|
60
60
|
* Generate main deployment workflow
|
|
61
61
|
*/
|
|
62
|
+
/**
|
|
63
|
+
* The command that fetches and runs a package that is not a project dependency.
|
|
64
|
+
*
|
|
65
|
+
* `npx` cannot be assumed. In a pnpm workflow it resolves against the
|
|
66
|
+
* pnpm-managed environment and reports "playwright: not found" — exit 127 —
|
|
67
|
+
* even with --yes. Each package manager has its own equivalent, so use it.
|
|
68
|
+
*/
|
|
69
|
+
function getDlxCommand(packageManager) {
|
|
70
|
+
switch (packageManager) {
|
|
71
|
+
case 'pnpm': return 'pnpm dlx';
|
|
72
|
+
case 'yarn': return 'yarn dlx';
|
|
73
|
+
default: return 'npx --yes';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
function generateMainWorkflow(projectId, apiUrl, packageManager, buildCmd) {
|
|
63
78
|
const pmSetup = getPackageManagerSetup(packageManager);
|
|
64
79
|
const installCmd = getInstallCommand(packageManager);
|
|
65
80
|
const cache = getCacheValue(packageManager);
|
|
66
81
|
const runCmd = packageManager === 'npm' ? `npm run ${buildCmd}` : `${packageManager} run ${buildCmd}`;
|
|
82
|
+
const dlx = getDlxCommand(packageManager);
|
|
67
83
|
|
|
68
84
|
return `# Auto-generated by Scry Storybook Deployer
|
|
69
85
|
# Deploy Storybook to production on push to main branch
|
|
@@ -96,6 +112,16 @@ ${cache}
|
|
|
96
112
|
- name: Build Storybook
|
|
97
113
|
run: ${runCmd}
|
|
98
114
|
|
|
115
|
+
- name: Install Playwright browser
|
|
116
|
+
# Screenshot capture drives --with-analysis, and without a browser every
|
|
117
|
+
# story fails, no metadata archive is produced, and nothing is ever
|
|
118
|
+
# indexed — while the deploy still exits 0 and the workflow goes green.
|
|
119
|
+
# Chromium headless shell only: a few seconds, versus minutes for the
|
|
120
|
+
# full browser set.
|
|
121
|
+
# The version is left floating so it tracks sbcov's own playwright
|
|
122
|
+
# ^1.41.0 optional dependency and installs a matching browser build.
|
|
123
|
+
run: ${dlx} playwright install --with-deps chromium-headless-shell
|
|
124
|
+
|
|
99
125
|
- name: Deploy to Scry
|
|
100
126
|
run: |
|
|
101
127
|
npx @scrymore/scry-deployer \\
|
|
@@ -121,6 +147,7 @@ function generatePRWorkflow(projectId, apiUrl, packageManager, buildCmd) {
|
|
|
121
147
|
const installCmd = getInstallCommand(packageManager);
|
|
122
148
|
const cache = getCacheValue(packageManager);
|
|
123
149
|
const runCmd = packageManager === 'npm' ? `npm run ${buildCmd}` : `${packageManager} run ${buildCmd}`;
|
|
150
|
+
const dlx = getDlxCommand(packageManager);
|
|
124
151
|
|
|
125
152
|
return `# Auto-generated by Scry Storybook Deployer
|
|
126
153
|
# Deploy Storybook preview for pull requests
|
|
@@ -157,6 +184,16 @@ ${cache}
|
|
|
157
184
|
- name: Build Storybook
|
|
158
185
|
run: ${runCmd}
|
|
159
186
|
|
|
187
|
+
- name: Install Playwright browser
|
|
188
|
+
# Screenshot capture drives --with-analysis, and without a browser every
|
|
189
|
+
# story fails, no metadata archive is produced, and nothing is ever
|
|
190
|
+
# indexed — while the deploy still exits 0 and the workflow goes green.
|
|
191
|
+
# Chromium headless shell only: a few seconds, versus minutes for the
|
|
192
|
+
# full browser set.
|
|
193
|
+
# The version is left floating so it tracks sbcov's own playwright
|
|
194
|
+
# ^1.41.0 optional dependency and installs a matching browser build.
|
|
195
|
+
run: ${dlx} playwright install --with-deps chromium-headless-shell
|
|
196
|
+
|
|
160
197
|
- name: Deploy Preview
|
|
161
198
|
id: deploy
|
|
162
199
|
run: |
|