pressship 0.2.0 → 0.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/assets/web/app.js +933 -90
- package/assets/web/index.html +15 -6
- package/assets/web/style.css +483 -0
- package/dist/plugin/info.d.ts +1 -0
- package/dist/plugin/info.js +23 -0
- package/dist/plugin/info.js.map +1 -1
- package/dist/web/plugin-check-state.d.ts +1 -0
- package/dist/web/plugin-check-state.js +3 -0
- package/dist/web/plugin-check-state.js.map +1 -1
- package/dist/web/server.d.ts +2 -0
- package/dist/web/server.js +55 -10
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
package/assets/web/app.js
CHANGED
|
@@ -219,6 +219,7 @@ function createInitialStudioRelease() {
|
|
|
219
219
|
newTagError: "",
|
|
220
220
|
customVersionDraft: "",
|
|
221
221
|
customVersionError: "",
|
|
222
|
+
skipReadmeValidation: false,
|
|
222
223
|
bumpInFlight: null,
|
|
223
224
|
bumpSuccess: null,
|
|
224
225
|
bumpError: "",
|
|
@@ -412,6 +413,8 @@ function onStudioResizerKeydown(event) {
|
|
|
412
413
|
|
|
413
414
|
const state = {
|
|
414
415
|
bootstrap: null,
|
|
416
|
+
pluginCheckSummaries: {},
|
|
417
|
+
latestWordPressVersion: "",
|
|
415
418
|
remote: [],
|
|
416
419
|
remoteUsername: "",
|
|
417
420
|
remoteLoading: true,
|
|
@@ -642,6 +645,12 @@ document.addEventListener("input", (event) => {
|
|
|
642
645
|
});
|
|
643
646
|
|
|
644
647
|
document.addEventListener("change", (event) => {
|
|
648
|
+
if (event.target?.id === "studio-skip-readme-validation") {
|
|
649
|
+
state.studio.release.skipReadmeValidation = Boolean(event.target.checked);
|
|
650
|
+
updateStudioSidebar();
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
645
654
|
if (event.target?.id !== "studio-plugin-picker") {
|
|
646
655
|
return;
|
|
647
656
|
}
|
|
@@ -921,6 +930,8 @@ async function boot() {
|
|
|
921
930
|
state.settings = state.bootstrap.settings ?? null;
|
|
922
931
|
state.playgrounds = state.bootstrap.playgrounds ?? [];
|
|
923
932
|
state.aiAssistance.harnesses = state.bootstrap.aiHarnesses ?? [];
|
|
933
|
+
state.pluginCheckSummaries = state.bootstrap.pluginCheckSummaries ?? {};
|
|
934
|
+
state.latestWordPressVersion = state.bootstrap.latestWordPressVersion ?? "";
|
|
924
935
|
const initialRoute = parseLocationRoute();
|
|
925
936
|
primeInitialRouteState(initialRoute);
|
|
926
937
|
renderAccount();
|
|
@@ -1311,8 +1322,9 @@ function renderRemote() {
|
|
|
1311
1322
|
}
|
|
1312
1323
|
|
|
1313
1324
|
function remoteCard(plugin) {
|
|
1314
|
-
const
|
|
1315
|
-
const
|
|
1325
|
+
const localState = remotePluginLocalState(plugin);
|
|
1326
|
+
const inLibrary = localState.entry;
|
|
1327
|
+
const primaryLabel = inLibrary ? "Open in Studio" : "Clone to Local";
|
|
1316
1328
|
const primaryAction = inLibrary
|
|
1317
1329
|
? `data-action="studio" data-scope="local" data-id="${escapeAttr(inLibrary.id)}"`
|
|
1318
1330
|
: `data-action="open-in-library" data-slug="${escapeAttr(plugin.slug)}"`;
|
|
@@ -1321,7 +1333,7 @@ function remoteCard(plugin) {
|
|
|
1321
1333
|
const description = plugin.author ? `By ${plugin.author}` : "WordPress.org plugin";
|
|
1322
1334
|
|
|
1323
1335
|
return `
|
|
1324
|
-
<article class="ps-plugin-card${inLibrary ? " is-in-library" : ""}" data-slug="${escapeAttr(plugin.slug)}">
|
|
1336
|
+
<article class="ps-plugin-card${inLibrary ? " is-in-library" : " is-not-cloned"}" data-slug="${escapeAttr(plugin.slug)}">
|
|
1325
1337
|
<header class="ps-plugin-card-header">
|
|
1326
1338
|
<span class="ps-plugin-card-icon" aria-hidden="true">${escapeHtml(initials)}</span>
|
|
1327
1339
|
<div class="ps-plugin-card-title">
|
|
@@ -1345,6 +1357,13 @@ function remoteCard(plugin) {
|
|
|
1345
1357
|
<dd>${roleBadges}</dd>
|
|
1346
1358
|
</div>
|
|
1347
1359
|
</dl>
|
|
1360
|
+
<div class="ps-plugin-card-status ps-plugin-card-local-state" title="${escapeAttr(localState.title)}">
|
|
1361
|
+
<span class="badge badge-${escapeAttr(localState.tone)}">
|
|
1362
|
+
<span class="dashicons ${escapeAttr(localState.icon)}" aria-hidden="true"></span>
|
|
1363
|
+
${escapeHtml(localState.label)}
|
|
1364
|
+
</span>
|
|
1365
|
+
<small>${escapeHtml(localState.note)}</small>
|
|
1366
|
+
</div>
|
|
1348
1367
|
<footer class="ps-plugin-card-footer">
|
|
1349
1368
|
<button type="button" class="button button-primary ps-plugin-card-primary" ${primaryAction}>
|
|
1350
1369
|
<span class="dashicons ${inLibrary ? "dashicons-editor-code" : "dashicons-download"}" aria-hidden="true"></span>
|
|
@@ -1358,6 +1377,55 @@ function remoteCard(plugin) {
|
|
|
1358
1377
|
`;
|
|
1359
1378
|
}
|
|
1360
1379
|
|
|
1380
|
+
function remotePluginLocalState(plugin) {
|
|
1381
|
+
const matches = state.local.filter((entry) => entry.slug === plugin.slug);
|
|
1382
|
+
const cloned = matches.find((entry) => entry.source === "clone" && entry.exists !== false);
|
|
1383
|
+
const tracked = matches.find((entry) => entry.exists !== false);
|
|
1384
|
+
const missing = matches.find((entry) => entry.exists === false);
|
|
1385
|
+
|
|
1386
|
+
if (cloned) {
|
|
1387
|
+
return {
|
|
1388
|
+
entry: cloned,
|
|
1389
|
+
label: "Cloned locally",
|
|
1390
|
+
note: cloned.path || "SVN checkout is tracked in Studio.",
|
|
1391
|
+
title: cloned.path || "This WordPress.org plugin has a local SVN checkout.",
|
|
1392
|
+
tone: "soft-success",
|
|
1393
|
+
icon: "dashicons-yes-alt"
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (tracked) {
|
|
1398
|
+
return {
|
|
1399
|
+
entry: tracked,
|
|
1400
|
+
label: "Tracked locally",
|
|
1401
|
+
note: tracked.path || "Matching local folder is tracked in Studio.",
|
|
1402
|
+
title: tracked.path || "A matching local plugin folder is tracked in Studio.",
|
|
1403
|
+
tone: "soft-success",
|
|
1404
|
+
icon: "dashicons-admin-site-alt3"
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
if (missing) {
|
|
1409
|
+
return {
|
|
1410
|
+
entry: null,
|
|
1411
|
+
label: "Local path missing",
|
|
1412
|
+
note: "Clone to recreate the local checkout.",
|
|
1413
|
+
title: missing.path || "The previous local folder is no longer available.",
|
|
1414
|
+
tone: "soft-warning",
|
|
1415
|
+
icon: "dashicons-warning"
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
return {
|
|
1420
|
+
entry: null,
|
|
1421
|
+
label: "Not cloned",
|
|
1422
|
+
note: "Clone this WordPress.org plugin to work on it locally.",
|
|
1423
|
+
title: "This WordPress.org plugin is not cloned into the local library.",
|
|
1424
|
+
tone: "soft-warning",
|
|
1425
|
+
icon: "dashicons-download"
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1361
1429
|
function remoteRoleChips(roles = []) {
|
|
1362
1430
|
const safeRoles = Array.isArray(roles) ? roles : [];
|
|
1363
1431
|
if (!safeRoles.length) {
|
|
@@ -1400,6 +1468,9 @@ async function loadLocal() {
|
|
|
1400
1468
|
state.local.map((plugin, index) => [plugin.id, versionStates[index]])
|
|
1401
1469
|
);
|
|
1402
1470
|
renderLocal();
|
|
1471
|
+
if (!state.remoteLoading && state.remote.length) {
|
|
1472
|
+
renderRemote();
|
|
1473
|
+
}
|
|
1403
1474
|
} catch (error) {
|
|
1404
1475
|
state.localError = error.message;
|
|
1405
1476
|
els.local.innerHTML = emptyState({
|
|
@@ -1931,7 +2002,7 @@ function normalizeStudioPlaygroundVersion(value) {
|
|
|
1931
2002
|
|
|
1932
2003
|
async function runStudioCheck() {
|
|
1933
2004
|
if (state.studio.scope !== "local" || !state.studio.id) {
|
|
1934
|
-
notice("
|
|
2005
|
+
notice("Verify is available for local plugins.", "warning");
|
|
1935
2006
|
return;
|
|
1936
2007
|
}
|
|
1937
2008
|
|
|
@@ -1948,13 +2019,22 @@ async function runStudioCheck() {
|
|
|
1948
2019
|
state.studio.checkRanAt = null;
|
|
1949
2020
|
state.studio.terminalOpen = true;
|
|
1950
2021
|
captureStudioEditorValue();
|
|
1951
|
-
appendStudioTerminal(
|
|
2022
|
+
appendStudioTerminal(
|
|
2023
|
+
state.studio.release.skipReadmeValidation
|
|
2024
|
+
? "Running verify with readme validator skipped…"
|
|
2025
|
+
: "Running verify with readme validation and Plugin Check…",
|
|
2026
|
+
"status"
|
|
2027
|
+
);
|
|
1952
2028
|
applyStudioCheckMarkers();
|
|
1953
2029
|
renderStudio();
|
|
1954
2030
|
remountStudioEditorIfNeeded();
|
|
1955
2031
|
updateStudioControls();
|
|
1956
2032
|
|
|
1957
|
-
const job = await createJob({
|
|
2033
|
+
const job = await createJob({
|
|
2034
|
+
type: "check",
|
|
2035
|
+
localId: state.studio.id,
|
|
2036
|
+
skipReadmeValidator: Boolean(state.studio.release.skipReadmeValidation)
|
|
2037
|
+
});
|
|
1958
2038
|
state.studio.checkJobId = job.id;
|
|
1959
2039
|
updateStudioControls();
|
|
1960
2040
|
}
|
|
@@ -2284,9 +2364,9 @@ function renderStudio() {
|
|
|
2284
2364
|
<span class="dashicons dashicons-saved" aria-hidden="true"></span>
|
|
2285
2365
|
<span>Save</span>
|
|
2286
2366
|
</button>
|
|
2287
|
-
<button class="studio-action-button studio-compact-button" type="button" data-action="studio-check" id="studio-check-button" aria-label="Run
|
|
2367
|
+
<button class="studio-action-button studio-compact-button" type="button" data-action="studio-check" id="studio-check-button" aria-label="Run Verify" title="Run Verify" disabled>
|
|
2288
2368
|
<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>
|
|
2289
|
-
<span>
|
|
2369
|
+
<span>Verify</span>
|
|
2290
2370
|
</button>
|
|
2291
2371
|
${renderStudioPackageSizeButton()}
|
|
2292
2372
|
${renderStudioThemeToggle()}
|
|
@@ -2418,7 +2498,7 @@ function renderStudioStatusBar() {
|
|
|
2418
2498
|
const file = state.studio.selectedFile?.path ?? "No file selected";
|
|
2419
2499
|
const check = state.studio.checkSummary
|
|
2420
2500
|
? `${state.studio.checkSummary.error || 0} errors, ${state.studio.checkSummary.warning || 0} warnings`
|
|
2421
|
-
: "
|
|
2501
|
+
: "Verify idle";
|
|
2422
2502
|
const assistant = selectedStudioAiAssistant();
|
|
2423
2503
|
return `
|
|
2424
2504
|
<footer class="studio-statusbar" aria-label="Studio status">
|
|
@@ -3598,10 +3678,10 @@ function renderStudioTerminal() {
|
|
|
3598
3678
|
function renderStudioCheckNotes() {
|
|
3599
3679
|
if (state.studio.checking) {
|
|
3600
3680
|
return `
|
|
3601
|
-
<aside class="studio-check-notes" aria-label="
|
|
3681
|
+
<aside class="studio-check-notes" aria-label="Validation notes">
|
|
3602
3682
|
<div class="studio-check-note studio-check-note-status">
|
|
3603
3683
|
<span class="dashicons dashicons-update" aria-hidden="true"></span>
|
|
3604
|
-
<span>
|
|
3684
|
+
<span>Verify is running…</span>
|
|
3605
3685
|
</div>
|
|
3606
3686
|
</aside>
|
|
3607
3687
|
`;
|
|
@@ -3615,19 +3695,19 @@ function renderStudioCheckNotes() {
|
|
|
3615
3695
|
if (!findings.length) {
|
|
3616
3696
|
const total = state.studio.checkSummary.total ?? 0;
|
|
3617
3697
|
return `
|
|
3618
|
-
<aside class="studio-check-notes" aria-label="
|
|
3698
|
+
<aside class="studio-check-notes" aria-label="Validation notes">
|
|
3619
3699
|
<div class="studio-check-note studio-check-note-${total ? "info" : "success"}">
|
|
3620
3700
|
<span class="dashicons ${total ? "dashicons-info-outline" : "dashicons-yes-alt"}" aria-hidden="true"></span>
|
|
3621
|
-
<span>${escapeHtml(total ? `${formatCheckCounts(state.studio.checkSummary)} in other files.` : "
|
|
3701
|
+
<span>${escapeHtml(total ? `${formatCheckCounts(state.studio.checkSummary)} in other files.` : "Verify reported no findings.")}</span>
|
|
3622
3702
|
</div>
|
|
3623
3703
|
</aside>
|
|
3624
3704
|
`;
|
|
3625
3705
|
}
|
|
3626
3706
|
|
|
3627
3707
|
return `
|
|
3628
|
-
<aside class="studio-check-notes" aria-label="
|
|
3708
|
+
<aside class="studio-check-notes" aria-label="Validation notes">
|
|
3629
3709
|
<header>
|
|
3630
|
-
<strong>
|
|
3710
|
+
<strong>Validation</strong>
|
|
3631
3711
|
<span>${escapeHtml(formatCheckCounts(studioCheckCountsForPath(state.studio.selectedFile?.path)))}</span>
|
|
3632
3712
|
</header>
|
|
3633
3713
|
<div class="studio-check-note-list">
|
|
@@ -3899,7 +3979,7 @@ function studioCliCommandForJob(input) {
|
|
|
3899
3979
|
return studioCliCommand([
|
|
3900
3980
|
"verify",
|
|
3901
3981
|
localPluginCliTarget(input.localId),
|
|
3902
|
-
"--skip-readme-validator",
|
|
3982
|
+
...(input.skipReadmeValidator ? ["--skip-readme-validator"] : []),
|
|
3903
3983
|
...studioCliIgnoreFlags(input.localId)
|
|
3904
3984
|
]);
|
|
3905
3985
|
case "dry-run-publish":
|
|
@@ -3980,6 +4060,18 @@ function applyStudioCheckState(checkState) {
|
|
|
3980
4060
|
state.studio.checkFindings = checkState.findings ?? [];
|
|
3981
4061
|
state.studio.checkSummary = checkState.summary ?? null;
|
|
3982
4062
|
state.studio.checkRanAt = checkState.checkedAt ?? null;
|
|
4063
|
+
|
|
4064
|
+
if (state.studio.id && state.studio.scope === "local" && checkState.summary) {
|
|
4065
|
+
state.pluginCheckSummaries[state.studio.id] = {
|
|
4066
|
+
slug: checkState.slug ?? state.studio.plugin?.slug,
|
|
4067
|
+
name: checkState.name ?? state.studio.plugin?.name,
|
|
4068
|
+
checkedAt: checkState.checkedAt,
|
|
4069
|
+
skipped: checkState.skipped ?? false,
|
|
4070
|
+
available: checkState.available ?? true,
|
|
4071
|
+
summary: checkState.summary
|
|
4072
|
+
};
|
|
4073
|
+
renderDashboard();
|
|
4074
|
+
}
|
|
3983
4075
|
}
|
|
3984
4076
|
|
|
3985
4077
|
function applyStudioIgnoreState(ignoreState) {
|
|
@@ -4633,11 +4725,11 @@ function updateStudioControls() {
|
|
|
4633
4725
|
}
|
|
4634
4726
|
if (studioCheck) {
|
|
4635
4727
|
studioCheck.disabled = !canCheck;
|
|
4636
|
-
studioCheck.setAttribute("aria-label", state.studio.checkSummary ? "Re-run
|
|
4637
|
-
studioCheck.title = state.studio.checkSummary ? "Re-run
|
|
4728
|
+
studioCheck.setAttribute("aria-label", state.studio.checkSummary ? "Re-run Verify" : "Run Verify");
|
|
4729
|
+
studioCheck.title = state.studio.checkSummary ? "Re-run Verify" : "Run Verify";
|
|
4638
4730
|
studioCheck.innerHTML = state.studio.checking
|
|
4639
|
-
? `<span class="dashicons dashicons-update" aria-hidden="true"></span><span>
|
|
4640
|
-
: `<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span><span>${state.studio.checkSummary ? "Re-
|
|
4731
|
+
? `<span class="dashicons dashicons-update" aria-hidden="true"></span><span>Verifying</span>`
|
|
4732
|
+
: `<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span><span>${state.studio.checkSummary ? "Re-verify" : "Verify"}</span>`;
|
|
4641
4733
|
}
|
|
4642
4734
|
if (studioSave) {
|
|
4643
4735
|
studioSave.disabled = !canSaveStudioFile();
|
|
@@ -5269,13 +5361,17 @@ function renderDashboard() {
|
|
|
5269
5361
|
<div id="dashboard-widgets" class="metabox-holder columns-2">
|
|
5270
5362
|
<div id="postbox-container-1" class="postbox-container">
|
|
5271
5363
|
<div class="meta-box-sortables">
|
|
5364
|
+
${renderDashboardOnboardingCard()}
|
|
5272
5365
|
${renderDashboardLocalWidget()}
|
|
5273
|
-
${
|
|
5366
|
+
${renderDashboardReleaseReadinessWidget()}
|
|
5367
|
+
${renderDashboardActivityWidget()}
|
|
5274
5368
|
</div>
|
|
5275
5369
|
</div>
|
|
5276
5370
|
<div id="postbox-container-2" class="postbox-container">
|
|
5277
5371
|
<div class="meta-box-sortables">
|
|
5278
5372
|
${renderDashboardAtGlanceWidget()}
|
|
5373
|
+
${renderDashboardPluginCheckWidget()}
|
|
5374
|
+
${renderDashboardCompatibilityWidget()}
|
|
5279
5375
|
${renderDashboardPlaygroundsCard()}
|
|
5280
5376
|
${renderDashboardAccountCard()}
|
|
5281
5377
|
</div>
|
|
@@ -5363,8 +5459,6 @@ function renderDashboardAtGlanceWidget() {
|
|
|
5363
5459
|
const localLabel = state.local.length === 1 ? "local plugin" : "local plugins";
|
|
5364
5460
|
const remoteLabel = state.remote.length === 1 ? "WordPress.org plugin" : "WordPress.org plugins";
|
|
5365
5461
|
const playgroundLabel = state.playgrounds.length === 1 ? "Playground running" : "Playgrounds running";
|
|
5366
|
-
const account = state.bootstrap?.account?.username;
|
|
5367
|
-
const accountLabel = account ? `Signed in as ${account}` : "WordPress.org not connected";
|
|
5368
5462
|
|
|
5369
5463
|
return renderDashboardPostbox({
|
|
5370
5464
|
id: "dashboard-at-a-glance",
|
|
@@ -5393,14 +5487,51 @@ function renderDashboardAtGlanceWidget() {
|
|
|
5393
5487
|
view: "studio"
|
|
5394
5488
|
})}
|
|
5395
5489
|
</ul>
|
|
5396
|
-
|
|
5397
|
-
<span class="dashicons ${account ? "dashicons-yes-alt" : "dashicons-warning"}" aria-hidden="true"></span>
|
|
5398
|
-
<span>${escapeHtml(accountLabel)}</span>
|
|
5399
|
-
</div>
|
|
5490
|
+
${renderDashboardReleaseStatusLine()}
|
|
5400
5491
|
`
|
|
5401
5492
|
});
|
|
5402
5493
|
}
|
|
5403
5494
|
|
|
5495
|
+
function renderDashboardReleaseStatusLine() {
|
|
5496
|
+
if (state.localLoading && !state.local.length) {
|
|
5497
|
+
return `
|
|
5498
|
+
<div class="ps-dashboard-status">
|
|
5499
|
+
<span class="dashicons dashicons-update" aria-hidden="true"></span>
|
|
5500
|
+
<span>Checking release state…</span>
|
|
5501
|
+
</div>
|
|
5502
|
+
`;
|
|
5503
|
+
}
|
|
5504
|
+
|
|
5505
|
+
const summary = dashboardReleaseSummary();
|
|
5506
|
+
let tone = "info";
|
|
5507
|
+
let icon = "dashicons-info";
|
|
5508
|
+
let text = "No local plugins to release yet.";
|
|
5509
|
+
|
|
5510
|
+
if (summary.total) {
|
|
5511
|
+
if (summary.blocked > 0) {
|
|
5512
|
+
tone = "error";
|
|
5513
|
+
icon = "dashicons-warning";
|
|
5514
|
+
text = `${summary.blocked} blocked from release · ${summary.ready} ready`;
|
|
5515
|
+
} else if (summary.behind > 0) {
|
|
5516
|
+
tone = "warning";
|
|
5517
|
+
icon = "dashicons-update";
|
|
5518
|
+
text = `${summary.behind} behind WordPress.org · ${summary.ready} ready`;
|
|
5519
|
+
} else {
|
|
5520
|
+
tone = "success";
|
|
5521
|
+
icon = "dashicons-yes-alt";
|
|
5522
|
+
text = `All ${summary.ready} plugin${summary.ready === 1 ? "" : "s"} ready to ship`;
|
|
5523
|
+
}
|
|
5524
|
+
}
|
|
5525
|
+
|
|
5526
|
+
return `
|
|
5527
|
+
<button class="ps-dashboard-status ps-dashboard-status-${escapeAttr(tone)}" type="button" data-view-button="release">
|
|
5528
|
+
<span class="dashicons ${escapeAttr(icon)}" aria-hidden="true"></span>
|
|
5529
|
+
<span>${escapeHtml(text)}</span>
|
|
5530
|
+
<span class="dashicons dashicons-arrow-right-alt2 ps-dashboard-status-arrow" aria-hidden="true"></span>
|
|
5531
|
+
</button>
|
|
5532
|
+
`;
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5404
5535
|
function renderDashboardGlanceItem({ icon, value, label, view, loading }) {
|
|
5405
5536
|
return `
|
|
5406
5537
|
<li class="ps-glance-item${loading ? " is-loading" : ""}">
|
|
@@ -5549,11 +5680,94 @@ function renderDashboardPlaygroundsCard() {
|
|
|
5549
5680
|
`;
|
|
5550
5681
|
}
|
|
5551
5682
|
|
|
5683
|
+
function dashboardRemoteSummary() {
|
|
5684
|
+
let cloned = 0;
|
|
5685
|
+
let committer = 0;
|
|
5686
|
+
let contributor = 0;
|
|
5687
|
+
|
|
5688
|
+
for (const plugin of state.remote) {
|
|
5689
|
+
if (remotePluginLocalState(plugin).entry) {
|
|
5690
|
+
cloned += 1;
|
|
5691
|
+
}
|
|
5692
|
+
const roles = Array.isArray(plugin.roles) ? plugin.roles : [];
|
|
5693
|
+
if (roles.includes("committer")) {
|
|
5694
|
+
committer += 1;
|
|
5695
|
+
} else if (roles.includes("contributor")) {
|
|
5696
|
+
contributor += 1;
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
|
|
5700
|
+
const total = state.remote.length;
|
|
5701
|
+
return { total, cloned, notCloned: Math.max(0, total - cloned), committer, contributor };
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5552
5704
|
function renderDashboardAccountCard() {
|
|
5553
|
-
const account = state.bootstrap?.account
|
|
5705
|
+
const account = state.bootstrap?.account;
|
|
5706
|
+
const username = account?.username;
|
|
5707
|
+
const displayName = account?.displayName;
|
|
5708
|
+
const profileUrl = account?.profileUrl;
|
|
5554
5709
|
const loggedIn = Boolean(state.bootstrap?.loggedIn);
|
|
5555
5710
|
const tone = loggedIn ? "success" : "warning";
|
|
5556
5711
|
const label = loggedIn ? "Signed in" : "Not signed in";
|
|
5712
|
+
|
|
5713
|
+
if (!loggedIn) {
|
|
5714
|
+
return renderDashboardPostbox({
|
|
5715
|
+
id: "dashboard-wordpress-org",
|
|
5716
|
+
title: "WordPress.org",
|
|
5717
|
+
icon: "dashicons-admin-users",
|
|
5718
|
+
actions: badge(label, tone),
|
|
5719
|
+
body: `
|
|
5720
|
+
<div class="ps-account-body">
|
|
5721
|
+
<div class="ps-account-row">
|
|
5722
|
+
<span class="ps-account-icon" aria-hidden="true">
|
|
5723
|
+
<span class="dashicons dashicons-admin-users"></span>
|
|
5724
|
+
</span>
|
|
5725
|
+
<span class="ps-account-text">
|
|
5726
|
+
<strong>Not connected</strong>
|
|
5727
|
+
<small>Run <code>pressship login</code> in a terminal to clone, submit, and release.</small>
|
|
5728
|
+
</span>
|
|
5729
|
+
</div>
|
|
5730
|
+
<div class="ps-widget-footer">
|
|
5731
|
+
<button class="button button-ghost button-small" type="button" data-view-button="settings">
|
|
5732
|
+
Settings
|
|
5733
|
+
<span class="dashicons dashicons-arrow-right-alt2" aria-hidden="true"></span>
|
|
5734
|
+
</button>
|
|
5735
|
+
</div>
|
|
5736
|
+
</div>
|
|
5737
|
+
`
|
|
5738
|
+
});
|
|
5739
|
+
}
|
|
5740
|
+
|
|
5741
|
+
const summary = dashboardRemoteSummary();
|
|
5742
|
+
const remoteValue = state.remoteLoading ? "…" : String(summary.total);
|
|
5743
|
+
const clonedValue = state.remoteLoading ? "…" : String(summary.cloned);
|
|
5744
|
+
|
|
5745
|
+
const reach = dashboardReachSummary();
|
|
5746
|
+
const reachBlock = !state.remoteLoading && reach.known
|
|
5747
|
+
? `<div class="ps-account-reach">
|
|
5748
|
+
<span class="ps-account-reach-value">${escapeHtml(formatInstallCount(reach.total))}${reach.plus ? "+" : ""}</span>
|
|
5749
|
+
<span class="ps-account-reach-label">combined active installs${reach.topName ? ` · ${escapeHtml(reach.topName)} leads` : ""}</span>
|
|
5750
|
+
</div>`
|
|
5751
|
+
: "";
|
|
5752
|
+
|
|
5753
|
+
const roleParts = [];
|
|
5754
|
+
if (summary.committer) {
|
|
5755
|
+
roleParts.push(`Committer on ${summary.committer}`);
|
|
5756
|
+
}
|
|
5757
|
+
if (summary.contributor) {
|
|
5758
|
+
roleParts.push(`Contributor on ${summary.contributor}`);
|
|
5759
|
+
}
|
|
5760
|
+
const roleLine = !state.remoteLoading && roleParts.length
|
|
5761
|
+
? `<div class="ps-account-roles"><span class="dashicons dashicons-groups" aria-hidden="true"></span><span>${escapeHtml(roleParts.join(" · "))}</span></div>`
|
|
5762
|
+
: "";
|
|
5763
|
+
|
|
5764
|
+
const profileLink = profileUrl
|
|
5765
|
+
? `<a class="button button-ghost button-small" href="${escapeAttr(profileUrl)}" target="_blank" rel="noopener noreferrer">
|
|
5766
|
+
<span class="dashicons dashicons-external" aria-hidden="true"></span>
|
|
5767
|
+
View profile
|
|
5768
|
+
</a>`
|
|
5769
|
+
: "";
|
|
5770
|
+
|
|
5557
5771
|
return renderDashboardPostbox({
|
|
5558
5772
|
id: "dashboard-wordpress-org",
|
|
5559
5773
|
title: "WordPress.org",
|
|
@@ -5566,11 +5780,28 @@ function renderDashboardAccountCard() {
|
|
|
5566
5780
|
<span class="dashicons dashicons-admin-users"></span>
|
|
5567
5781
|
</span>
|
|
5568
5782
|
<span class="ps-account-text">
|
|
5569
|
-
<strong>${escapeHtml(
|
|
5570
|
-
<small>${escapeHtml(
|
|
5783
|
+
<strong>${escapeHtml(displayName || username || "WordPress.org account")}</strong>
|
|
5784
|
+
<small>${escapeHtml(username ? `@${username}` : "Used for clone, submit, and release.")}</small>
|
|
5571
5785
|
</span>
|
|
5572
5786
|
</div>
|
|
5787
|
+
${reachBlock}
|
|
5788
|
+
<ul class="ps-account-stats">
|
|
5789
|
+
<li>
|
|
5790
|
+
<button class="ps-account-stat" type="button" data-view-button="remote">
|
|
5791
|
+
<strong>${escapeHtml(remoteValue)}</strong>
|
|
5792
|
+
<span>on WordPress.org</span>
|
|
5793
|
+
</button>
|
|
5794
|
+
</li>
|
|
5795
|
+
<li>
|
|
5796
|
+
<button class="ps-account-stat" type="button" data-view-button="local">
|
|
5797
|
+
<strong>${escapeHtml(clonedValue)}</strong>
|
|
5798
|
+
<span>cloned locally</span>
|
|
5799
|
+
</button>
|
|
5800
|
+
</li>
|
|
5801
|
+
</ul>
|
|
5802
|
+
${roleLine}
|
|
5573
5803
|
<div class="ps-widget-footer">
|
|
5804
|
+
${profileLink}
|
|
5574
5805
|
<button class="button button-ghost button-small" type="button" data-view-button="settings">
|
|
5575
5806
|
Settings
|
|
5576
5807
|
<span class="dashicons dashicons-arrow-right-alt2" aria-hidden="true"></span>
|
|
@@ -5581,73 +5812,681 @@ function renderDashboardAccountCard() {
|
|
|
5581
5812
|
});
|
|
5582
5813
|
}
|
|
5583
5814
|
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5815
|
+
const VERSION_STATUS_META = {
|
|
5816
|
+
missing_version: { label: "Missing version", tone: "error", icon: "dashicons-warning", action: "manage-release", actionLabel: "Fix" },
|
|
5817
|
+
header_readme_mismatch: { label: "Version mismatch", tone: "error", icon: "dashicons-randomize", action: "manage-release", actionLabel: "Fix" },
|
|
5818
|
+
duplicate_tag_blocked: { label: "Tag already shipped", tone: "error", icon: "dashicons-tag", action: "manage-release", actionLabel: "Bump" },
|
|
5819
|
+
remote_newer: { label: "Behind WordPress.org", tone: "warning", icon: "dashicons-update", action: "version-state", actionLabel: "Details" },
|
|
5820
|
+
unknown_svn_state: { label: "SVN state unknown", tone: "warning", icon: "dashicons-editor-help", action: "version-state", actionLabel: "Details" },
|
|
5821
|
+
ready: { label: "Ready", tone: "success", icon: "dashicons-yes-alt", action: "manage-release", actionLabel: "Release" }
|
|
5822
|
+
};
|
|
5823
|
+
|
|
5824
|
+
const BLOCKING_STATUS_ORDER = ["missing_version", "header_readme_mismatch", "duplicate_tag_blocked"];
|
|
5825
|
+
|
|
5826
|
+
function versionStatusMeta(status) {
|
|
5827
|
+
return VERSION_STATUS_META[status] ?? { label: labelize(status), tone: "info", icon: "dashicons-info", action: "version-state", actionLabel: "Details" };
|
|
5828
|
+
}
|
|
5829
|
+
|
|
5830
|
+
function primaryVersionIssue(versionState) {
|
|
5831
|
+
const statuses = versionState?.statuses ?? [];
|
|
5832
|
+
const status =
|
|
5833
|
+
BLOCKING_STATUS_ORDER.find((candidate) => statuses.includes(candidate)) ??
|
|
5834
|
+
(statuses.includes("remote_newer") ? "remote_newer" : statuses.find((candidate) => candidate !== "ready"));
|
|
5835
|
+
if (!status) {
|
|
5836
|
+
return null;
|
|
5837
|
+
}
|
|
5838
|
+
const meta = versionStatusMeta(status);
|
|
5839
|
+
const messageIndex = statuses.indexOf(status);
|
|
5840
|
+
const message = versionState.messages?.[messageIndex] ?? versionState.messages?.[0] ?? meta.label;
|
|
5841
|
+
return { status, message, ...meta };
|
|
5842
|
+
}
|
|
5843
|
+
|
|
5844
|
+
function dashboardReleaseSummary() {
|
|
5845
|
+
let ready = 0;
|
|
5846
|
+
let blocked = 0;
|
|
5847
|
+
let behind = 0;
|
|
5848
|
+
let unknown = 0;
|
|
5849
|
+
const attention = [];
|
|
5850
|
+
|
|
5851
|
+
for (const plugin of state.local) {
|
|
5852
|
+
const versionState = state.versionStates.get(plugin.id);
|
|
5853
|
+
if (!versionState || versionState.error || !Array.isArray(versionState.statuses)) {
|
|
5854
|
+
unknown += 1;
|
|
5855
|
+
continue;
|
|
5856
|
+
}
|
|
5857
|
+
|
|
5858
|
+
const isBehind = versionState.statuses.includes("remote_newer");
|
|
5859
|
+
if (versionState.releaseBlocked) {
|
|
5860
|
+
blocked += 1;
|
|
5861
|
+
} else if (isBehind) {
|
|
5862
|
+
behind += 1;
|
|
5863
|
+
} else {
|
|
5864
|
+
ready += 1;
|
|
5865
|
+
}
|
|
5866
|
+
|
|
5867
|
+
if (versionState.releaseBlocked || isBehind) {
|
|
5868
|
+
const issue = primaryVersionIssue(versionState);
|
|
5869
|
+
if (issue) {
|
|
5870
|
+
attention.push({ plugin, versionState, issue });
|
|
5871
|
+
}
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
|
|
5875
|
+
attention.sort((left, right) => issueWeight(right.issue) - issueWeight(left.issue));
|
|
5876
|
+
return { total: state.local.length, ready, blocked, behind, unknown, attention };
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
function issueWeight(issue) {
|
|
5880
|
+
if (issue.tone === "error") return 2;
|
|
5881
|
+
if (issue.tone === "warning") return 1;
|
|
5882
|
+
return 0;
|
|
5883
|
+
}
|
|
5884
|
+
|
|
5885
|
+
function renderDashboardReleaseReadinessWidget() {
|
|
5886
|
+
const loading = state.localLoading && !state.local.length;
|
|
5887
|
+
const summary = dashboardReleaseSummary();
|
|
5590
5888
|
|
|
5591
5889
|
let tone = "info";
|
|
5592
|
-
let label = "
|
|
5593
|
-
if (
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5890
|
+
let label = "—";
|
|
5891
|
+
if (!loading) {
|
|
5892
|
+
if (!summary.total) {
|
|
5893
|
+
tone = "info";
|
|
5894
|
+
label = "No plugins";
|
|
5895
|
+
} else if (summary.blocked > 0) {
|
|
5896
|
+
tone = "error";
|
|
5897
|
+
label = `${summary.blocked} blocked`;
|
|
5898
|
+
} else if (summary.behind > 0) {
|
|
5899
|
+
tone = "warning";
|
|
5900
|
+
label = `${summary.behind} behind`;
|
|
5901
|
+
} else {
|
|
5902
|
+
tone = "success";
|
|
5903
|
+
label = "All ready";
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
|
|
5907
|
+
return renderDashboardPostbox({
|
|
5908
|
+
id: "dashboard-release-readiness",
|
|
5909
|
+
title: "Release readiness",
|
|
5910
|
+
icon: "ps-icon-rocket",
|
|
5911
|
+
className: "ps-dashboard-main",
|
|
5912
|
+
actions: loading ? "" : badge(label, tone),
|
|
5913
|
+
body: renderDashboardReadinessBody(summary, loading)
|
|
5914
|
+
});
|
|
5915
|
+
}
|
|
5916
|
+
|
|
5917
|
+
function renderDashboardReadinessBody(summary, loading) {
|
|
5918
|
+
if (loading) {
|
|
5919
|
+
return `
|
|
5920
|
+
<p class="ps-widget-intro">Checking version and release state for every local plugin…</p>
|
|
5921
|
+
${renderDashboardSkeletonRows(2)}
|
|
5922
|
+
`;
|
|
5923
|
+
}
|
|
5924
|
+
|
|
5925
|
+
if (!summary.total) {
|
|
5926
|
+
return `
|
|
5927
|
+
<div class="ps-empty-card">
|
|
5928
|
+
<span class="dashicons ps-icon-rocket" aria-hidden="true"></span>
|
|
5929
|
+
<strong>Nothing to release yet</strong>
|
|
5930
|
+
<p>Add a local plugin folder, then Pressship flags version mismatches and duplicate tags before you publish.</p>
|
|
5931
|
+
<div class="ps-empty-card-actions">
|
|
5932
|
+
<button class="button button-primary" type="button" data-action="choose-local-folder">
|
|
5933
|
+
<span class="dashicons dashicons-open-folder" aria-hidden="true"></span>
|
|
5934
|
+
Choose Folder
|
|
5935
|
+
</button>
|
|
5936
|
+
</div>
|
|
5937
|
+
</div>
|
|
5938
|
+
`;
|
|
5939
|
+
}
|
|
5940
|
+
|
|
5941
|
+
if (!summary.attention.length) {
|
|
5942
|
+
return `
|
|
5943
|
+
<div class="ps-readiness-clear">
|
|
5944
|
+
<span class="ps-readiness-clear-icon" aria-hidden="true">
|
|
5945
|
+
<span class="dashicons dashicons-yes-alt"></span>
|
|
5614
5946
|
</span>
|
|
5947
|
+
<div class="ps-readiness-clear-text">
|
|
5948
|
+
<strong>Everything looks release-ready</strong>
|
|
5949
|
+
<p>${escapeHtml(`All ${summary.ready} plugin${summary.ready === 1 ? "" : "s"} pass version checks. Open Release Management to ship.`)}</p>
|
|
5950
|
+
</div>
|
|
5951
|
+
<button class="button button-primary button-small" type="button" data-view-button="release">
|
|
5952
|
+
<span class="dashicons ps-icon-rocket" aria-hidden="true"></span>
|
|
5953
|
+
Release Management
|
|
5954
|
+
</button>
|
|
5955
|
+
</div>
|
|
5956
|
+
`;
|
|
5957
|
+
}
|
|
5958
|
+
|
|
5959
|
+
const visible = summary.attention.slice(0, 5);
|
|
5960
|
+
const rows = visible.map(dashboardReadinessRow).join("");
|
|
5961
|
+
const overflow = summary.attention.length > visible.length
|
|
5962
|
+
? `<div class="ps-widget-footer"><button class="button button-ghost button-small" type="button" data-view-button="release">See all ${summary.attention.length} flagged plugins<span class="dashicons dashicons-arrow-right-alt2" aria-hidden="true"></span></button></div>`
|
|
5963
|
+
: "";
|
|
5964
|
+
|
|
5965
|
+
const blockedNote = summary.blocked
|
|
5966
|
+
? `${summary.blocked} blocked from release`
|
|
5967
|
+
: `${summary.behind} behind WordPress.org`;
|
|
5968
|
+
|
|
5969
|
+
return `
|
|
5970
|
+
<p class="ps-widget-intro">${escapeHtml(`${summary.attention.length} plugin${summary.attention.length === 1 ? "" : "s"} need a fix before release — ${blockedNote}.`)}</p>
|
|
5971
|
+
<ul class="ps-attention-list">${rows}</ul>
|
|
5972
|
+
${overflow}
|
|
5973
|
+
`;
|
|
5974
|
+
}
|
|
5975
|
+
|
|
5976
|
+
function dashboardReadinessRow({ plugin, issue }) {
|
|
5977
|
+
const name = plugin.name || plugin.slug || plugin.id;
|
|
5978
|
+
return `
|
|
5979
|
+
<li class="ps-attention-row ps-attention-${escapeAttr(issue.tone)}">
|
|
5980
|
+
<span class="ps-attention-icon" aria-hidden="true">
|
|
5981
|
+
<span class="dashicons ${escapeAttr(issue.icon)}"></span>
|
|
5982
|
+
</span>
|
|
5983
|
+
<button class="ps-attention-main" type="button" data-action="studio" data-scope="local" data-id="${escapeAttr(plugin.id)}" title="Open ${escapeAttr(name)} in Studio">
|
|
5984
|
+
<span class="ps-attention-text">
|
|
5985
|
+
<strong>${escapeHtml(name)}</strong>
|
|
5986
|
+
<small>${escapeHtml(issue.message)}</small>
|
|
5987
|
+
</span>
|
|
5988
|
+
</button>
|
|
5989
|
+
<span class="ps-attention-action">
|
|
5990
|
+
${badge(issue.label, issue.tone)}
|
|
5991
|
+
<button class="button button-small" type="button" data-action="${escapeAttr(issue.action)}" data-id="${escapeAttr(plugin.id)}">${escapeHtml(issue.actionLabel)}</button>
|
|
5992
|
+
</span>
|
|
5993
|
+
</li>
|
|
5994
|
+
`;
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
/* ===================================================================
|
|
5998
|
+
* Dashboard: Recent activity
|
|
5999
|
+
* =================================================================== */
|
|
6000
|
+
|
|
6001
|
+
function renderDashboardActivityWidget() {
|
|
6002
|
+
const jobs = Array.from(state.jobs.values()).sort((left, right) =>
|
|
6003
|
+
String(right.createdAt ?? "").localeCompare(String(left.createdAt ?? ""))
|
|
6004
|
+
);
|
|
6005
|
+
const running = jobs.filter((job) => job.status === "running" || job.status === "queued").length;
|
|
6006
|
+
|
|
6007
|
+
const body = jobs.length
|
|
6008
|
+
? `<ul class="ps-activity-list">${jobs.slice(0, 5).map(dashboardActivityRow).join("")}</ul>`
|
|
6009
|
+
: `
|
|
6010
|
+
<div class="ps-side-empty ps-activity-empty">
|
|
6011
|
+
<span class="dashicons dashicons-clock" aria-hidden="true"></span>
|
|
6012
|
+
<span>No recent activity yet. Clone, check, dry-run, or release a plugin to see it here.</span>
|
|
6013
|
+
</div>
|
|
5615
6014
|
`;
|
|
5616
|
-
})
|
|
5617
|
-
.join("");
|
|
5618
6015
|
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
6016
|
+
return renderDashboardPostbox({
|
|
6017
|
+
id: "dashboard-activity",
|
|
6018
|
+
title: "Recent activity",
|
|
6019
|
+
icon: "dashicons-backup",
|
|
6020
|
+
className: "ps-dashboard-main",
|
|
6021
|
+
actions: running ? `<span class="ps-count-pill">${running} active</span>` : "",
|
|
6022
|
+
body
|
|
6023
|
+
});
|
|
6024
|
+
}
|
|
6025
|
+
|
|
6026
|
+
function dashboardActivityRow(job) {
|
|
6027
|
+
const tone =
|
|
6028
|
+
job.status === "failed"
|
|
6029
|
+
? "error"
|
|
6030
|
+
: job.status === "succeeded"
|
|
6031
|
+
? "success"
|
|
6032
|
+
: job.status === "cancelled"
|
|
6033
|
+
? "warning"
|
|
6034
|
+
: "info";
|
|
6035
|
+
return `
|
|
6036
|
+
<li class="ps-activity-row">
|
|
6037
|
+
<span class="ps-activity-icon ps-activity-${escapeAttr(tone)}" aria-hidden="true">
|
|
6038
|
+
<span class="dashicons ${jobIcon(job.type)}"></span>
|
|
6039
|
+
</span>
|
|
6040
|
+
<span class="ps-activity-text">
|
|
6041
|
+
<strong>${escapeHtml(job.title || jobTypeLabel(job.type))}</strong>
|
|
6042
|
+
<small>${escapeHtml(formatRelativeTime(job.createdAt))}</small>
|
|
6043
|
+
</span>
|
|
6044
|
+
${badge(job.status, tone)}
|
|
6045
|
+
</li>
|
|
6046
|
+
`;
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
function jobTypeLabel(type) {
|
|
6050
|
+
switch (type) {
|
|
6051
|
+
case "clone":
|
|
6052
|
+
return "Clone plugin";
|
|
6053
|
+
case "play":
|
|
6054
|
+
return "Launch Playground";
|
|
6055
|
+
case "check":
|
|
6056
|
+
return "Verify plugin";
|
|
6057
|
+
case "dry-run-publish":
|
|
6058
|
+
return "Dry-run publish";
|
|
6059
|
+
case "confirm-publish":
|
|
6060
|
+
return "Publish";
|
|
6061
|
+
default:
|
|
6062
|
+
return "Task";
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
|
|
6066
|
+
function formatRelativeTime(iso) {
|
|
6067
|
+
if (!iso) {
|
|
6068
|
+
return "";
|
|
6069
|
+
}
|
|
6070
|
+
const then = new Date(iso).getTime();
|
|
6071
|
+
if (!Number.isFinite(then)) {
|
|
6072
|
+
return String(iso);
|
|
6073
|
+
}
|
|
6074
|
+
const diffSeconds = Math.round((Date.now() - then) / 1000);
|
|
6075
|
+
if (diffSeconds < 45) {
|
|
6076
|
+
return "just now";
|
|
6077
|
+
}
|
|
6078
|
+
const minutes = Math.round(diffSeconds / 60);
|
|
6079
|
+
if (minutes < 60) {
|
|
6080
|
+
return `${minutes}m ago`;
|
|
6081
|
+
}
|
|
6082
|
+
const hours = Math.round(minutes / 60);
|
|
6083
|
+
if (hours < 24) {
|
|
6084
|
+
return `${hours}h ago`;
|
|
6085
|
+
}
|
|
6086
|
+
const days = Math.round(hours / 24);
|
|
6087
|
+
if (days < 7) {
|
|
6088
|
+
return `${days}d ago`;
|
|
6089
|
+
}
|
|
6090
|
+
try {
|
|
6091
|
+
return new Date(iso).toLocaleDateString([], { month: "short", day: "numeric" });
|
|
6092
|
+
} catch {
|
|
6093
|
+
return String(iso);
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
|
|
6097
|
+
/* ===================================================================
|
|
6098
|
+
* Dashboard: Validation health
|
|
6099
|
+
* =================================================================== */
|
|
6100
|
+
|
|
6101
|
+
function dashboardPluginCheckSummary() {
|
|
6102
|
+
let checked = 0;
|
|
6103
|
+
let errors = 0;
|
|
6104
|
+
let warnings = 0;
|
|
6105
|
+
let clean = 0;
|
|
6106
|
+
const flagged = [];
|
|
6107
|
+
|
|
6108
|
+
for (const plugin of state.local) {
|
|
6109
|
+
const entry = state.pluginCheckSummaries?.[plugin.id];
|
|
6110
|
+
if (!entry || !entry.summary) {
|
|
6111
|
+
continue;
|
|
6112
|
+
}
|
|
6113
|
+
checked += 1;
|
|
6114
|
+
const summary = entry.summary;
|
|
6115
|
+
if (summary.error > 0) {
|
|
6116
|
+
errors += 1;
|
|
6117
|
+
flagged.push({ plugin, entry, tone: "error" });
|
|
6118
|
+
} else if (summary.warning > 0) {
|
|
6119
|
+
warnings += 1;
|
|
6120
|
+
flagged.push({ plugin, entry, tone: "warning" });
|
|
6121
|
+
} else {
|
|
6122
|
+
clean += 1;
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
|
|
6126
|
+
flagged.sort(
|
|
6127
|
+
(left, right) =>
|
|
6128
|
+
right.entry.summary.error - left.entry.summary.error ||
|
|
6129
|
+
right.entry.summary.warning - left.entry.summary.warning
|
|
6130
|
+
);
|
|
6131
|
+
|
|
6132
|
+
return { checked, errors, warnings, clean, unchecked: state.local.length - checked, flagged };
|
|
6133
|
+
}
|
|
6134
|
+
|
|
6135
|
+
function renderDashboardPluginCheckWidget() {
|
|
6136
|
+
if (!state.local.length) {
|
|
6137
|
+
return "";
|
|
6138
|
+
}
|
|
6139
|
+
|
|
6140
|
+
const summary = dashboardPluginCheckSummary();
|
|
6141
|
+
|
|
6142
|
+
let tone = "info";
|
|
6143
|
+
let label = "Not run";
|
|
6144
|
+
if (summary.checked > 0) {
|
|
6145
|
+
if (summary.errors > 0) {
|
|
6146
|
+
tone = "error";
|
|
6147
|
+
label = `${summary.errors} with errors`;
|
|
6148
|
+
} else if (summary.warnings > 0) {
|
|
6149
|
+
tone = "warning";
|
|
6150
|
+
label = `${summary.warnings} with warnings`;
|
|
6151
|
+
} else {
|
|
6152
|
+
tone = "success";
|
|
6153
|
+
label = "All clean";
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
|
|
6157
|
+
let body;
|
|
6158
|
+
if (summary.checked === 0) {
|
|
6159
|
+
body = `
|
|
6160
|
+
<div class="ps-side-empty">
|
|
6161
|
+
<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>
|
|
6162
|
+
<span>No Verify results yet. Open a plugin in Studio and run Verify.</span>
|
|
6163
|
+
</div>
|
|
6164
|
+
`;
|
|
6165
|
+
} else if (!summary.flagged.length) {
|
|
6166
|
+
body = `
|
|
6167
|
+
<div class="ps-readiness-clear">
|
|
6168
|
+
<span class="ps-readiness-clear-icon" aria-hidden="true">
|
|
6169
|
+
<span class="dashicons dashicons-yes-alt"></span>
|
|
6170
|
+
</span>
|
|
6171
|
+
<div class="ps-readiness-clear-text">
|
|
6172
|
+
<strong>No findings</strong>
|
|
6173
|
+
<p>${escapeHtml(`All ${summary.checked} checked plugin${summary.checked === 1 ? "" : "s"} passed Verify.`)}</p>
|
|
6174
|
+
</div>
|
|
6175
|
+
</div>
|
|
6176
|
+
${dashboardPluginCheckFooter(summary)}
|
|
6177
|
+
`;
|
|
6178
|
+
} else {
|
|
6179
|
+
const rows = summary.flagged.slice(0, 4).map(dashboardPluginCheckRow).join("");
|
|
6180
|
+
body = `
|
|
6181
|
+
<ul class="ps-attention-list">${rows}</ul>
|
|
6182
|
+
${dashboardPluginCheckFooter(summary)}
|
|
6183
|
+
`;
|
|
6184
|
+
}
|
|
5627
6185
|
|
|
5628
6186
|
return renderDashboardPostbox({
|
|
5629
|
-
id: "dashboard-
|
|
5630
|
-
title: "
|
|
5631
|
-
icon: "dashicons-
|
|
6187
|
+
id: "dashboard-plugin-check",
|
|
6188
|
+
title: "Validation health",
|
|
6189
|
+
icon: "dashicons-shield",
|
|
5632
6190
|
actions: badge(label, tone),
|
|
5633
|
-
body
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
6191
|
+
body
|
|
6192
|
+
});
|
|
6193
|
+
}
|
|
6194
|
+
|
|
6195
|
+
function dashboardPluginCheckFooter(summary) {
|
|
6196
|
+
return `
|
|
6197
|
+
<small class="ps-widget-meta">${escapeHtml(
|
|
6198
|
+
`${summary.checked} of ${state.local.length} checked · ${summary.clean} clean${summary.unchecked ? ` · ${summary.unchecked} not run` : ""}`
|
|
6199
|
+
)}</small>
|
|
6200
|
+
`;
|
|
6201
|
+
}
|
|
6202
|
+
|
|
6203
|
+
function dashboardPluginCheckRow({ plugin, entry, tone }) {
|
|
6204
|
+
const name = entry.name || plugin.name || plugin.slug || plugin.id;
|
|
6205
|
+
const counts = [];
|
|
6206
|
+
if (entry.summary.error) {
|
|
6207
|
+
counts.push(`${entry.summary.error} error${entry.summary.error === 1 ? "" : "s"}`);
|
|
6208
|
+
}
|
|
6209
|
+
if (entry.summary.warning) {
|
|
6210
|
+
counts.push(`${entry.summary.warning} warning${entry.summary.warning === 1 ? "" : "s"}`);
|
|
6211
|
+
}
|
|
6212
|
+
const detail = `${counts.join(" · ")}${entry.checkedAt ? ` · ${formatRelativeTime(entry.checkedAt)}` : ""}`;
|
|
6213
|
+
return `
|
|
6214
|
+
<li class="ps-attention-row ps-attention-${escapeAttr(tone)}">
|
|
6215
|
+
<span class="ps-attention-icon" aria-hidden="true">
|
|
6216
|
+
<span class="dashicons ${tone === "error" ? "dashicons-warning" : "dashicons-flag"}"></span>
|
|
6217
|
+
</span>
|
|
6218
|
+
<button class="ps-attention-main" type="button" data-action="studio" data-scope="local" data-id="${escapeAttr(plugin.id)}" title="Open ${escapeAttr(name)} in Studio">
|
|
6219
|
+
<span class="ps-attention-text">
|
|
6220
|
+
<strong>${escapeHtml(name)}</strong>
|
|
6221
|
+
<small>${escapeHtml(detail)}</small>
|
|
6222
|
+
</span>
|
|
6223
|
+
</button>
|
|
6224
|
+
<span class="ps-attention-action">
|
|
6225
|
+
<button class="button button-small" type="button" data-action="studio" data-scope="local" data-id="${escapeAttr(plugin.id)}">Open</button>
|
|
6226
|
+
</span>
|
|
6227
|
+
</li>
|
|
6228
|
+
`;
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6231
|
+
/* ===================================================================
|
|
6232
|
+
* Dashboard: Compatibility watch
|
|
6233
|
+
* =================================================================== */
|
|
6234
|
+
|
|
6235
|
+
function dashboardCompatibilitySummary() {
|
|
6236
|
+
const latest = state.latestWordPressVersion || "";
|
|
6237
|
+
const latestBranch = latest ? versionBranch(latest) : "";
|
|
6238
|
+
let behind = 0;
|
|
6239
|
+
let current = 0;
|
|
6240
|
+
let unknown = 0;
|
|
6241
|
+
const outdated = [];
|
|
6242
|
+
|
|
6243
|
+
for (const plugin of state.remote) {
|
|
6244
|
+
const tested = parseTestedWith(plugin.testedWith);
|
|
6245
|
+
if (!tested) {
|
|
6246
|
+
unknown += 1;
|
|
6247
|
+
continue;
|
|
6248
|
+
}
|
|
6249
|
+
if (latestBranch && compareVersionStrings(versionBranch(tested), latestBranch) < 0) {
|
|
6250
|
+
behind += 1;
|
|
6251
|
+
outdated.push({ plugin, tested });
|
|
6252
|
+
} else {
|
|
6253
|
+
current += 1;
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
|
|
6257
|
+
outdated.sort((left, right) => compareVersionStrings(left.tested, right.tested));
|
|
6258
|
+
return { latest, latestBranch, behind, current, unknown, outdated };
|
|
6259
|
+
}
|
|
6260
|
+
|
|
6261
|
+
function renderDashboardCompatibilityWidget() {
|
|
6262
|
+
if (!state.bootstrap?.loggedIn || (!state.remote.length && !state.remoteLoading)) {
|
|
6263
|
+
return "";
|
|
6264
|
+
}
|
|
6265
|
+
|
|
6266
|
+
const summary = dashboardCompatibilitySummary();
|
|
6267
|
+
const latestLabel = summary.latest ? `WP ${summary.latest}` : "WordPress";
|
|
6268
|
+
|
|
6269
|
+
let tone = "info";
|
|
6270
|
+
let label = summary.latestBranch ? "Up to date" : "Unknown";
|
|
6271
|
+
if (summary.behind > 0) {
|
|
6272
|
+
tone = "warning";
|
|
6273
|
+
label = `${summary.behind} behind`;
|
|
6274
|
+
} else if (summary.latestBranch && summary.current > 0) {
|
|
6275
|
+
tone = "success";
|
|
6276
|
+
label = "Up to date";
|
|
6277
|
+
}
|
|
6278
|
+
|
|
6279
|
+
let body;
|
|
6280
|
+
if (state.remoteLoading) {
|
|
6281
|
+
body = `<div class="ps-side-empty"><span class="dashicons dashicons-update" aria-hidden="true"></span><span>Loading WordPress.org plugins…</span></div>`;
|
|
6282
|
+
} else if (!summary.latestBranch) {
|
|
6283
|
+
body = `<div class="ps-side-empty"><span class="dashicons dashicons-editor-help" aria-hidden="true"></span><span>Could not determine the latest WordPress version right now.</span></div>`;
|
|
6284
|
+
} else if (!summary.outdated.length) {
|
|
6285
|
+
body = `
|
|
6286
|
+
<div class="ps-readiness-clear">
|
|
6287
|
+
<span class="ps-readiness-clear-icon" aria-hidden="true">
|
|
6288
|
+
<span class="dashicons dashicons-yes-alt"></span>
|
|
6289
|
+
</span>
|
|
6290
|
+
<div class="ps-readiness-clear-text">
|
|
6291
|
+
<strong>${escapeHtml(`Tested with ${latestLabel}`)}</strong>
|
|
6292
|
+
<p>${escapeHtml(`All ${summary.current} plugin${summary.current === 1 ? "" : "s"} declare compatibility with the latest WordPress.`)}</p>
|
|
5645
6293
|
</div>
|
|
5646
|
-
|
|
5647
|
-
|
|
6294
|
+
</div>
|
|
6295
|
+
`;
|
|
6296
|
+
} else {
|
|
6297
|
+
const rows = summary.outdated.slice(0, 4).map((item) => dashboardCompatibilityRow(item, summary)).join("");
|
|
6298
|
+
body = `
|
|
6299
|
+
<p class="ps-widget-intro">${escapeHtml(`Latest WordPress is ${summary.latest}. Bump "Tested up to" so users keep installing.`)}</p>
|
|
6300
|
+
<ul class="ps-attention-list">${rows}</ul>
|
|
6301
|
+
`;
|
|
6302
|
+
}
|
|
6303
|
+
|
|
6304
|
+
return renderDashboardPostbox({
|
|
6305
|
+
id: "dashboard-compatibility",
|
|
6306
|
+
title: "Compatibility watch",
|
|
6307
|
+
icon: "dashicons-wordpress",
|
|
6308
|
+
actions: badge(label, tone),
|
|
6309
|
+
body
|
|
5648
6310
|
});
|
|
5649
6311
|
}
|
|
5650
6312
|
|
|
6313
|
+
function dashboardCompatibilityRow({ plugin, tested }, summary) {
|
|
6314
|
+
return `
|
|
6315
|
+
<li class="ps-attention-row ps-attention-warning">
|
|
6316
|
+
<span class="ps-attention-icon" aria-hidden="true">
|
|
6317
|
+
<span class="dashicons dashicons-wordpress"></span>
|
|
6318
|
+
</span>
|
|
6319
|
+
<button class="ps-attention-main" type="button" data-action="details" data-scope="remote" data-id="${escapeAttr(plugin.slug)}" title="View ${escapeAttr(plugin.name || plugin.slug)}">
|
|
6320
|
+
<span class="ps-attention-text">
|
|
6321
|
+
<strong>${escapeHtml(plugin.name || plugin.slug)}</strong>
|
|
6322
|
+
<small>${escapeHtml(`Tested up to ${tested} · WordPress is ${summary.latest}`)}</small>
|
|
6323
|
+
</span>
|
|
6324
|
+
</button>
|
|
6325
|
+
<span class="ps-attention-action">
|
|
6326
|
+
${badge(`${tested}`, "warning")}
|
|
6327
|
+
</span>
|
|
6328
|
+
</li>
|
|
6329
|
+
`;
|
|
6330
|
+
}
|
|
6331
|
+
|
|
6332
|
+
function parseTestedWith(value) {
|
|
6333
|
+
const match = String(value ?? "").match(/(\d+\.\d+(?:\.\d+)?)/);
|
|
6334
|
+
return match ? match[1] : "";
|
|
6335
|
+
}
|
|
6336
|
+
|
|
6337
|
+
function versionBranch(value) {
|
|
6338
|
+
const parts = String(value ?? "").split(".");
|
|
6339
|
+
return `${parts[0] ?? "0"}.${parts[1] ?? "0"}`;
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
function compareVersionStrings(left, right) {
|
|
6343
|
+
const leftParts = String(left).split(/[.-]/).map((part) => Number.parseInt(part, 10) || 0);
|
|
6344
|
+
const rightParts = String(right).split(/[.-]/).map((part) => Number.parseInt(part, 10) || 0);
|
|
6345
|
+
for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) {
|
|
6346
|
+
const diff = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
|
6347
|
+
if (diff !== 0) {
|
|
6348
|
+
return diff > 0 ? 1 : -1;
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6351
|
+
return 0;
|
|
6352
|
+
}
|
|
6353
|
+
|
|
6354
|
+
/* ===================================================================
|
|
6355
|
+
* Dashboard: WordPress.org reach
|
|
6356
|
+
* =================================================================== */
|
|
6357
|
+
|
|
6358
|
+
function dashboardReachSummary() {
|
|
6359
|
+
let total = 0;
|
|
6360
|
+
let known = false;
|
|
6361
|
+
let plus = false;
|
|
6362
|
+
let topName = "";
|
|
6363
|
+
let topCount = -1;
|
|
6364
|
+
|
|
6365
|
+
for (const plugin of state.remote) {
|
|
6366
|
+
const parsed = parseActiveInstalls(plugin.activeInstalls);
|
|
6367
|
+
if (!parsed.known) {
|
|
6368
|
+
continue;
|
|
6369
|
+
}
|
|
6370
|
+
known = true;
|
|
6371
|
+
total += parsed.count;
|
|
6372
|
+
if (parsed.plus) {
|
|
6373
|
+
plus = true;
|
|
6374
|
+
}
|
|
6375
|
+
if (parsed.count > topCount) {
|
|
6376
|
+
topCount = parsed.count;
|
|
6377
|
+
topName = plugin.name || plugin.slug || "";
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6380
|
+
|
|
6381
|
+
return { total, known, plus, topName: total > 0 ? topName : "" };
|
|
6382
|
+
}
|
|
6383
|
+
|
|
6384
|
+
function parseActiveInstalls(value) {
|
|
6385
|
+
const text = String(value ?? "").replace(/,/g, "");
|
|
6386
|
+
const match = text.match(/(\d+)/);
|
|
6387
|
+
if (!match) {
|
|
6388
|
+
return { count: 0, plus: false, known: false };
|
|
6389
|
+
}
|
|
6390
|
+
return { count: Number.parseInt(match[1], 10), plus: /\+/.test(text), known: true };
|
|
6391
|
+
}
|
|
6392
|
+
|
|
6393
|
+
function formatInstallCount(value) {
|
|
6394
|
+
try {
|
|
6395
|
+
return Number(value).toLocaleString();
|
|
6396
|
+
} catch {
|
|
6397
|
+
return String(value);
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
|
|
6401
|
+
/* ===================================================================
|
|
6402
|
+
* Dashboard: Getting-started checklist
|
|
6403
|
+
* =================================================================== */
|
|
6404
|
+
|
|
6405
|
+
function dashboardOnboardingSteps() {
|
|
6406
|
+
const loggedIn = Boolean(state.bootstrap?.loggedIn);
|
|
6407
|
+
const hasLocal = state.local.length > 0;
|
|
6408
|
+
const ranCheck = Object.values(state.pluginCheckSummaries ?? {}).some((entry) => entry?.summary);
|
|
6409
|
+
const released =
|
|
6410
|
+
state.local.some((plugin) => state.versionStates.get(plugin.id)?.remoteVersion) ||
|
|
6411
|
+
Array.from(state.jobs.values()).some(
|
|
6412
|
+
(job) => job.type === "confirm-publish" && job.status === "succeeded"
|
|
6413
|
+
);
|
|
6414
|
+
|
|
6415
|
+
return [
|
|
6416
|
+
{
|
|
6417
|
+
id: "login",
|
|
6418
|
+
done: loggedIn,
|
|
6419
|
+
label: "Connect your WordPress.org account",
|
|
6420
|
+
hint: "Run pressship login in a terminal, then refresh.",
|
|
6421
|
+
actionAttrs: 'data-view-button="settings"',
|
|
6422
|
+
actionLabel: "Settings"
|
|
6423
|
+
},
|
|
6424
|
+
{
|
|
6425
|
+
id: "add",
|
|
6426
|
+
done: hasLocal,
|
|
6427
|
+
label: "Add a local plugin",
|
|
6428
|
+
hint: "Point Pressship at a plugin folder you're working on.",
|
|
6429
|
+
actionAttrs: 'data-action="choose-local-folder"',
|
|
6430
|
+
actionLabel: "Choose Folder"
|
|
6431
|
+
},
|
|
6432
|
+
{
|
|
6433
|
+
id: "check",
|
|
6434
|
+
done: ranCheck,
|
|
6435
|
+
label: "Run Verify on a plugin",
|
|
6436
|
+
hint: "Open a plugin in Studio and run Verify.",
|
|
6437
|
+
actionAttrs: 'data-view-button="local"',
|
|
6438
|
+
actionLabel: "Open Library"
|
|
6439
|
+
},
|
|
6440
|
+
{
|
|
6441
|
+
id: "release",
|
|
6442
|
+
done: released,
|
|
6443
|
+
label: "Prepare or ship a release",
|
|
6444
|
+
hint: "Review version state and walk the publish funnel.",
|
|
6445
|
+
actionAttrs: 'data-view-button="release"',
|
|
6446
|
+
actionLabel: "Release"
|
|
6447
|
+
}
|
|
6448
|
+
];
|
|
6449
|
+
}
|
|
6450
|
+
|
|
6451
|
+
function renderDashboardOnboardingCard() {
|
|
6452
|
+
if (state.localLoading && !state.local.length) {
|
|
6453
|
+
return "";
|
|
6454
|
+
}
|
|
6455
|
+
|
|
6456
|
+
const steps = dashboardOnboardingSteps();
|
|
6457
|
+
const done = steps.filter((step) => step.done).length;
|
|
6458
|
+
if (done === steps.length) {
|
|
6459
|
+
return "";
|
|
6460
|
+
}
|
|
6461
|
+
|
|
6462
|
+
return renderDashboardPostbox({
|
|
6463
|
+
id: "dashboard-getting-started",
|
|
6464
|
+
title: "Getting started",
|
|
6465
|
+
icon: "dashicons-flag",
|
|
6466
|
+
className: "ps-dashboard-main ps-onboarding-card",
|
|
6467
|
+
actions: `<span class="ps-count-pill">${done}/${steps.length}</span>`,
|
|
6468
|
+
body: `<ol class="ps-onboarding-list">${steps.map(dashboardOnboardingRow).join("")}</ol>`
|
|
6469
|
+
});
|
|
6470
|
+
}
|
|
6471
|
+
|
|
6472
|
+
function dashboardOnboardingRow(step) {
|
|
6473
|
+
const action = step.done
|
|
6474
|
+
? ""
|
|
6475
|
+
: `<button class="button button-small" type="button" ${step.actionAttrs}>${escapeHtml(step.actionLabel)}</button>`;
|
|
6476
|
+
return `
|
|
6477
|
+
<li class="ps-onboarding-row${step.done ? " is-done" : ""}">
|
|
6478
|
+
<span class="ps-onboarding-check" aria-hidden="true">
|
|
6479
|
+
<span class="dashicons ${step.done ? "dashicons-yes-alt" : "dashicons-marker"}"></span>
|
|
6480
|
+
</span>
|
|
6481
|
+
<span class="ps-onboarding-label">
|
|
6482
|
+
<strong>${escapeHtml(step.label)}</strong>
|
|
6483
|
+
${step.hint && !step.done ? `<small>${escapeHtml(step.hint)}</small>` : ""}
|
|
6484
|
+
</span>
|
|
6485
|
+
${action}
|
|
6486
|
+
</li>
|
|
6487
|
+
`;
|
|
6488
|
+
}
|
|
6489
|
+
|
|
5651
6490
|
function renderPlaygroundsMenu() {
|
|
5652
6491
|
if (!els.playgroundsSection || !els.playgroundsMenu) {
|
|
5653
6492
|
return;
|
|
@@ -7331,14 +8170,14 @@ function escapeAttr(value) {
|
|
|
7331
8170
|
}
|
|
7332
8171
|
|
|
7333
8172
|
/* ===================================================================
|
|
7334
|
-
*
|
|
8173
|
+
* Clone to Local — clones from WordPress.org SVN or jumps to existing
|
|
7335
8174
|
* =================================================================== */
|
|
7336
8175
|
|
|
7337
8176
|
async function openInLibrary(slug) {
|
|
7338
8177
|
if (!slug) {
|
|
7339
8178
|
return;
|
|
7340
8179
|
}
|
|
7341
|
-
const existing =
|
|
8180
|
+
const existing = remotePluginLocalState({ slug }).entry;
|
|
7342
8181
|
if (existing) {
|
|
7343
8182
|
await openStudio("local", existing.id);
|
|
7344
8183
|
return;
|
|
@@ -7791,13 +8630,13 @@ function renderStudioReleaseTagRow(tag) {
|
|
|
7791
8630
|
function renderStudioReleaseStepValidate(versionState, release) {
|
|
7792
8631
|
const summary = state.studio.checkSummary;
|
|
7793
8632
|
const summaryLine = summary
|
|
7794
|
-
? `
|
|
7795
|
-
: "
|
|
8633
|
+
? `Verify: ${escapeHtml(String(summary.error || 0))} errors, ${escapeHtml(String(summary.warning || 0))} warnings`
|
|
8634
|
+
: "Verify has not been run yet.";
|
|
7796
8635
|
const validationBlocked = release.dryRun?.validationBlocked;
|
|
7797
8636
|
const body = `
|
|
7798
8637
|
<ul class="ps-release-validate-list">
|
|
7799
8638
|
<li>
|
|
7800
|
-
<span><span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>Plugin Check</span>
|
|
8639
|
+
<span><span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>Readme + Plugin Check</span>
|
|
7801
8640
|
<span class="ps-release-validate-state">${escapeHtml(summary ? "Ran" : "Pending")}</span>
|
|
7802
8641
|
<button class="button button-small" type="button" data-action="studio-check">${summary ? "Re-run" : "Run"}</button>
|
|
7803
8642
|
</li>
|
|
@@ -7811,6 +8650,10 @@ function renderStudioReleaseStepValidate(versionState, release) {
|
|
|
7811
8650
|
<span class="ps-release-validate-state">${escapeHtml(release.dryRun?.package?.file ? "Ready" : "Build on dry-run")}</span>
|
|
7812
8651
|
</li>
|
|
7813
8652
|
</ul>
|
|
8653
|
+
<label class="ps-release-skip-readme">
|
|
8654
|
+
<input type="checkbox" id="studio-skip-readme-validation" ${release.skipReadmeValidation ? "checked" : ""} ${state.studio.checking ? "disabled" : ""} />
|
|
8655
|
+
<span>skip readme validation</span>
|
|
8656
|
+
</label>
|
|
7814
8657
|
${validationBlocked
|
|
7815
8658
|
? `<p class="ps-release-step-error">Validation reported blocking findings. Fix them before publishing.</p>`
|
|
7816
8659
|
: ""}
|