@gpc-cli/core 0.9.65 → 0.9.67
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/{chunk-4TBNOCVU.js → chunk-4KDWLQB3.js} +85 -37
- package/dist/chunk-4KDWLQB3.js.map +1 -0
- package/dist/index.d.ts +17 -4
- package/dist/index.js +119 -42
- package/dist/index.js.map +1 -1
- package/dist/{releases-XF5MYFQD.js → releases-OAKWPEAC.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-4TBNOCVU.js.map +0 -1
- /package/dist/{releases-XF5MYFQD.js.map → releases-OAKWPEAC.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
diffReleases,
|
|
11
11
|
fetchReleaseNotes,
|
|
12
12
|
findBundle,
|
|
13
|
+
formatSize,
|
|
13
14
|
getReleasesStatus,
|
|
14
15
|
isVersionedNotesDir,
|
|
15
16
|
listBundles,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
validateUploadFile,
|
|
28
29
|
waitForBundle,
|
|
29
30
|
waitForBundleProcessing
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-4KDWLQB3.js";
|
|
31
32
|
|
|
32
33
|
// src/output.ts
|
|
33
34
|
import process2 from "process";
|
|
@@ -404,7 +405,7 @@ var PluginManager = class {
|
|
|
404
405
|
if (!isTrusted && manifest?.permissions) {
|
|
405
406
|
validatePermissions(manifest.permissions);
|
|
406
407
|
}
|
|
407
|
-
const
|
|
408
|
+
const allHooks = createHooks(
|
|
408
409
|
this.beforeHandlers,
|
|
409
410
|
this.afterHandlers,
|
|
410
411
|
this.errorHandlers,
|
|
@@ -412,7 +413,14 @@ var PluginManager = class {
|
|
|
412
413
|
this.afterResponseHandlers,
|
|
413
414
|
this.registeredCommands
|
|
414
415
|
);
|
|
415
|
-
|
|
416
|
+
const hooks = isTrusted || !manifest?.permissions ? allHooks : createRestrictedHooks(allHooks, new Set(manifest.permissions));
|
|
417
|
+
try {
|
|
418
|
+
await plugin.register(hooks);
|
|
419
|
+
} catch (err) {
|
|
420
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
421
|
+
console.warn(`[gpc] Plugin "${plugin.name}" failed to register: ${msg}`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
416
424
|
this.plugins.push({
|
|
417
425
|
name: plugin.name,
|
|
418
426
|
version: plugin.version,
|
|
@@ -508,6 +516,33 @@ function createHooks(beforeHandlers, afterHandlers, errorHandlers, beforeRequest
|
|
|
508
516
|
}
|
|
509
517
|
};
|
|
510
518
|
}
|
|
519
|
+
var HOOK_PERMISSIONS = {
|
|
520
|
+
beforeCommand: "hooks:beforeCommand",
|
|
521
|
+
afterCommand: "hooks:afterCommand",
|
|
522
|
+
onError: "hooks:onError",
|
|
523
|
+
beforeRequest: "hooks:beforeRequest",
|
|
524
|
+
afterResponse: "hooks:afterResponse",
|
|
525
|
+
registerCommands: "commands:register"
|
|
526
|
+
};
|
|
527
|
+
function createRestrictedHooks(inner, permissions) {
|
|
528
|
+
function gate(key) {
|
|
529
|
+
const requiredPerm = HOOK_PERMISSIONS[key];
|
|
530
|
+
if (permissions.has(requiredPerm)) {
|
|
531
|
+
return inner[key];
|
|
532
|
+
}
|
|
533
|
+
return (() => {
|
|
534
|
+
console.warn(`[gpc] Plugin lacks permission "${requiredPerm}" for hook "${key}"`);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
beforeCommand: gate("beforeCommand"),
|
|
539
|
+
afterCommand: gate("afterCommand"),
|
|
540
|
+
onError: gate("onError"),
|
|
541
|
+
beforeRequest: gate("beforeRequest"),
|
|
542
|
+
afterResponse: gate("afterResponse"),
|
|
543
|
+
registerCommands: gate("registerCommands")
|
|
544
|
+
};
|
|
545
|
+
}
|
|
511
546
|
var VALID_PERMISSIONS = /* @__PURE__ */ new Set([
|
|
512
547
|
"read:config",
|
|
513
548
|
"write:config",
|
|
@@ -730,11 +765,6 @@ async function validateImage(filePath, imageType) {
|
|
|
730
765
|
}
|
|
731
766
|
return { valid: errors.length === 0, errors, warnings };
|
|
732
767
|
}
|
|
733
|
-
function formatSize(bytes) {
|
|
734
|
-
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
735
|
-
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
736
|
-
return `${bytes} B`;
|
|
737
|
-
}
|
|
738
768
|
|
|
739
769
|
// src/utils/fastlane.ts
|
|
740
770
|
import { readFile, writeFile, mkdir, readdir, stat as stat2 } from "fs/promises";
|
|
@@ -2804,7 +2834,10 @@ async function getAppStatus(client, reporting, packageName, options = {}) {
|
|
|
2804
2834
|
sections.has("vitals") ? queryVitalWithTrend(reporting, packageName, "slowRenderingRateMetricSet", days) : Promise.resolve(SKIPPED_VITAL),
|
|
2805
2835
|
sections.has("reviews") ? listReviews(client, packageName, { maxResults: 500 }) : Promise.resolve([])
|
|
2806
2836
|
]);
|
|
2837
|
+
const sectionErrors = [];
|
|
2807
2838
|
const rawReleases = releasesResult.status === "fulfilled" ? releasesResult.value : [];
|
|
2839
|
+
if (releasesResult.status === "rejected")
|
|
2840
|
+
sectionErrors.push(`releases: ${String(releasesResult.reason)}`);
|
|
2808
2841
|
const releases = rawReleases.map((r) => ({
|
|
2809
2842
|
track: r.track,
|
|
2810
2843
|
versionCode: r.versionCodes[r.versionCodes.length - 1] ?? "\u2014",
|
|
@@ -2812,10 +2845,20 @@ async function getAppStatus(client, reporting, packageName, options = {}) {
|
|
|
2812
2845
|
userFraction: r.userFraction ?? null
|
|
2813
2846
|
}));
|
|
2814
2847
|
const crashes = crashesResult.status === "fulfilled" ? crashesResult.value : SKIPPED_VITAL;
|
|
2848
|
+
if (crashesResult.status === "rejected")
|
|
2849
|
+
sectionErrors.push(`vitals/crashes: ${String(crashesResult.reason)}`);
|
|
2815
2850
|
const anr = anrResult.status === "fulfilled" ? anrResult.value : SKIPPED_VITAL;
|
|
2851
|
+
if (anrResult.status === "rejected")
|
|
2852
|
+
sectionErrors.push(`vitals/anr: ${String(anrResult.reason)}`);
|
|
2816
2853
|
const slowStart = slowStartResult.status === "fulfilled" ? slowStartResult.value : SKIPPED_VITAL;
|
|
2854
|
+
if (slowStartResult.status === "rejected")
|
|
2855
|
+
sectionErrors.push(`vitals/slowStarts: ${String(slowStartResult.reason)}`);
|
|
2817
2856
|
const slowRender = slowRenderResult.status === "fulfilled" ? slowRenderResult.value : SKIPPED_VITAL;
|
|
2857
|
+
if (slowRenderResult.status === "rejected")
|
|
2858
|
+
sectionErrors.push(`vitals/slowRender: ${String(slowRenderResult.reason)}`);
|
|
2818
2859
|
const rawReviews = reviewsResult.status === "fulfilled" ? reviewsResult.value : [];
|
|
2860
|
+
if (reviewsResult.status === "rejected")
|
|
2861
|
+
sectionErrors.push(`reviews: ${String(reviewsResult.reason)}`);
|
|
2819
2862
|
const reviews = computeReviewSentiment(rawReviews, reviewDays);
|
|
2820
2863
|
return {
|
|
2821
2864
|
packageName,
|
|
@@ -2845,7 +2888,8 @@ async function getAppStatus(client, reporting, packageName, options = {}) {
|
|
|
2845
2888
|
slowRender.trend
|
|
2846
2889
|
)
|
|
2847
2890
|
},
|
|
2848
|
-
reviews
|
|
2891
|
+
reviews,
|
|
2892
|
+
...sectionErrors.length > 0 ? { errors: sectionErrors } : {}
|
|
2849
2893
|
};
|
|
2850
2894
|
}
|
|
2851
2895
|
function vitalIndicator(metric) {
|
|
@@ -3209,7 +3253,12 @@ async function dispatchWebhook(url, event) {
|
|
|
3209
3253
|
signal: AbortSignal.timeout(1e4)
|
|
3210
3254
|
});
|
|
3211
3255
|
if (!response.ok) {
|
|
3212
|
-
throw new
|
|
3256
|
+
throw new GpcError(
|
|
3257
|
+
`Webhook returned ${response.status}`,
|
|
3258
|
+
"WATCH_WEBHOOK_FAILED",
|
|
3259
|
+
5,
|
|
3260
|
+
`The webhook endpoint returned HTTP ${response.status}. Check the URL and server status.`
|
|
3261
|
+
);
|
|
3213
3262
|
}
|
|
3214
3263
|
}
|
|
3215
3264
|
async function handleBreach(event, config, client) {
|
|
@@ -3226,7 +3275,7 @@ async function handleBreach(event, config, client) {
|
|
|
3226
3275
|
}
|
|
3227
3276
|
case "halt": {
|
|
3228
3277
|
try {
|
|
3229
|
-
const { updateRollout: updateRollout2 } = await import("./releases-
|
|
3278
|
+
const { updateRollout: updateRollout2 } = await import("./releases-OAKWPEAC.js");
|
|
3230
3279
|
await updateRollout2(client, config.packageName, config.track, "halt");
|
|
3231
3280
|
halted = true;
|
|
3232
3281
|
} catch {
|
|
@@ -3271,8 +3320,13 @@ async function runWatch(client, reporting, config, callbacks) {
|
|
|
3271
3320
|
round++;
|
|
3272
3321
|
const rollout = await fetchRollout(client, config.packageName, config.track);
|
|
3273
3322
|
const vitals = {};
|
|
3274
|
-
|
|
3275
|
-
|
|
3323
|
+
const metricResults = await Promise.all(
|
|
3324
|
+
config.metrics.map(async (metric) => {
|
|
3325
|
+
const value = await queryWatchMetric(reporting, config.packageName, metric);
|
|
3326
|
+
return { metric, value };
|
|
3327
|
+
})
|
|
3328
|
+
);
|
|
3329
|
+
for (const { metric, value } of metricResults) {
|
|
3276
3330
|
const threshold = config.thresholds[metric];
|
|
3277
3331
|
const result = threshold !== void 0 ? checkThreshold(value, threshold) : { breached: false };
|
|
3278
3332
|
vitals[metric] = {
|
|
@@ -3293,14 +3347,12 @@ async function runWatch(client, reporting, config, callbacks) {
|
|
|
3293
3347
|
if (breaches.length > 0) {
|
|
3294
3348
|
breachCount++;
|
|
3295
3349
|
const stateChanged = await trackBreachState(config.packageName, true);
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
halted = true;
|
|
3301
|
-
}
|
|
3302
|
-
await callbacks.onBreach(event);
|
|
3350
|
+
const didHalt = stateChanged ? await handleBreach(event, config, client) : false;
|
|
3351
|
+
if (didHalt) {
|
|
3352
|
+
event.halted = true;
|
|
3353
|
+
halted = true;
|
|
3303
3354
|
}
|
|
3355
|
+
await callbacks.onBreach(event);
|
|
3304
3356
|
callbacks.onEvent(event);
|
|
3305
3357
|
} else {
|
|
3306
3358
|
await trackBreachState(config.packageName, false);
|
|
@@ -3744,7 +3796,7 @@ async function getUser(client, developerId, email) {
|
|
|
3744
3796
|
}
|
|
3745
3797
|
async function inviteUser(client, developerId, email, permissions, grants) {
|
|
3746
3798
|
const user = { email };
|
|
3747
|
-
if (permissions) user.
|
|
3799
|
+
if (permissions) user.developerAccountPermissions = permissions;
|
|
3748
3800
|
if (grants) user.grants = grants;
|
|
3749
3801
|
return client.create(developerId, user);
|
|
3750
3802
|
}
|
|
@@ -3752,8 +3804,8 @@ async function updateUser(client, developerId, userId, permissions, grants) {
|
|
|
3752
3804
|
const updates = {};
|
|
3753
3805
|
const masks = [];
|
|
3754
3806
|
if (permissions) {
|
|
3755
|
-
updates.
|
|
3756
|
-
masks.push("
|
|
3807
|
+
updates.developerAccountPermissions = permissions;
|
|
3808
|
+
masks.push("developerAccountPermissions");
|
|
3757
3809
|
}
|
|
3758
3810
|
if (grants) {
|
|
3759
3811
|
updates.grants = grants;
|
|
@@ -4802,7 +4854,7 @@ jobs:
|
|
|
4802
4854
|
run: ./gradlew bundleRelease
|
|
4803
4855
|
|
|
4804
4856
|
- name: Install GPC
|
|
4805
|
-
run: npm install -g @gpc-cli/cli
|
|
4857
|
+
run: npm install -g @gpc-cli/cli --ignore-scripts
|
|
4806
4858
|
|
|
4807
4859
|
- name: Preflight compliance check
|
|
4808
4860
|
run: gpc preflight app/build/outputs/bundle/release/app-release.aab --fail-on error
|
|
@@ -4841,14 +4893,14 @@ preflight:
|
|
|
4841
4893
|
stage: preflight
|
|
4842
4894
|
image: node:20
|
|
4843
4895
|
script:
|
|
4844
|
-
- npm install -g @gpc-cli/cli
|
|
4896
|
+
- npm install -g @gpc-cli/cli --ignore-scripts
|
|
4845
4897
|
- gpc preflight app/build/outputs/bundle/release/app-release.aab --fail-on error --json
|
|
4846
4898
|
|
|
4847
4899
|
release:
|
|
4848
4900
|
stage: release
|
|
4849
4901
|
image: node:20
|
|
4850
4902
|
script:
|
|
4851
|
-
- npm install -g @gpc-cli/cli
|
|
4903
|
+
- npm install -g @gpc-cli/cli --ignore-scripts
|
|
4852
4904
|
- gpc releases upload app/build/outputs/bundle/release/app-release.aab --track internal
|
|
4853
4905
|
only:
|
|
4854
4906
|
- tags
|
|
@@ -4864,7 +4916,7 @@ var SEVERITY_ORDER = {
|
|
|
4864
4916
|
};
|
|
4865
4917
|
var DEFAULT_PREFLIGHT_CONFIG = {
|
|
4866
4918
|
failOn: "error",
|
|
4867
|
-
targetSdkMinimum:
|
|
4919
|
+
targetSdkMinimum: 36,
|
|
4868
4920
|
maxDownloadSizeMb: 150,
|
|
4869
4921
|
allowedPermissions: [],
|
|
4870
4922
|
disabledRules: [],
|
|
@@ -5050,7 +5102,7 @@ function extractManifestData(manifest) {
|
|
|
5050
5102
|
const appElements = getChildren(manifest, "application");
|
|
5051
5103
|
const app = appElements[0];
|
|
5052
5104
|
const debuggable = app ? getBoolByName(app.attribute || [], "debuggable", false) : false;
|
|
5053
|
-
const testOnly = getBoolByName(
|
|
5105
|
+
const testOnly = app ? getBoolByName(app.attribute || [], "testOnly", false) : false;
|
|
5054
5106
|
const usesCleartextTraffic = app ? getBoolByName(app.attribute || [], "usesCleartextTraffic", true) : true;
|
|
5055
5107
|
const extractNativeLibs = app ? getBoolByName(app.attribute || [], "extractNativeLibs", true) : true;
|
|
5056
5108
|
const pageSizeCompat = app ? getBoolByName(app.attribute || [], "pageSizeCompat", false) : false;
|
|
@@ -5110,8 +5162,8 @@ function extractComponents(appElement, tagName) {
|
|
|
5110
5162
|
// src/preflight/aab-reader.ts
|
|
5111
5163
|
var AAB_MANIFEST_PATH = "base/manifest/AndroidManifest.xml";
|
|
5112
5164
|
var APK_MANIFEST_PATH = "AndroidManifest.xml";
|
|
5113
|
-
var SO_HEADER_BYTES =
|
|
5114
|
-
var SO_PATH_RE =
|
|
5165
|
+
var SO_HEADER_BYTES = 4096;
|
|
5166
|
+
var SO_PATH_RE = /(?:^|\/)lib\/[^/]+\/[^/]+\.so$/;
|
|
5115
5167
|
function detectManifestPath(filePath) {
|
|
5116
5168
|
return filePath.toLowerCase().endsWith(".apk") ? APK_MANIFEST_PATH : AAB_MANIFEST_PATH;
|
|
5117
5169
|
}
|
|
@@ -6506,16 +6558,35 @@ async function runPreflight(options) {
|
|
|
6506
6558
|
if (options.metadataDir) ctx.metadataDir = options.metadataDir;
|
|
6507
6559
|
if (options.sourceDir) ctx.sourceDir = options.sourceDir;
|
|
6508
6560
|
const requestedNames = options.scanners ? new Set(options.scanners.map((s) => s.toLowerCase())) : null;
|
|
6509
|
-
const applicableScanners =
|
|
6510
|
-
|
|
6561
|
+
const applicableScanners = [];
|
|
6562
|
+
const skippedScanners = [];
|
|
6563
|
+
for (const scanner of ALL_SCANNERS) {
|
|
6564
|
+
if (requestedNames && !requestedNames.has(scanner.name)) continue;
|
|
6565
|
+
let skip = false;
|
|
6511
6566
|
for (const req of scanner.requires) {
|
|
6512
|
-
if (req === "manifest" && !ctx.manifest)
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6567
|
+
if (req === "manifest" && !ctx.manifest) {
|
|
6568
|
+
skip = true;
|
|
6569
|
+
break;
|
|
6570
|
+
}
|
|
6571
|
+
if (req === "zipEntries" && !ctx.zipEntries) {
|
|
6572
|
+
skip = true;
|
|
6573
|
+
break;
|
|
6574
|
+
}
|
|
6575
|
+
if (req === "metadataDir" && !ctx.metadataDir) {
|
|
6576
|
+
skip = true;
|
|
6577
|
+
break;
|
|
6578
|
+
}
|
|
6579
|
+
if (req === "sourceDir" && !ctx.sourceDir) {
|
|
6580
|
+
skip = true;
|
|
6581
|
+
break;
|
|
6582
|
+
}
|
|
6516
6583
|
}
|
|
6517
|
-
|
|
6518
|
-
|
|
6584
|
+
if (skip) {
|
|
6585
|
+
skippedScanners.push(scanner.name);
|
|
6586
|
+
} else {
|
|
6587
|
+
applicableScanners.push(scanner);
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6519
6590
|
const settled = await Promise.allSettled(applicableScanners.map((scanner) => scanner.scan(ctx)));
|
|
6520
6591
|
let findings = [...earlyFindings];
|
|
6521
6592
|
for (let i = 0; i < settled.length; i++) {
|
|
@@ -6551,6 +6622,7 @@ async function runPreflight(options) {
|
|
|
6551
6622
|
const passed = !findings.some((f) => SEVERITY_ORDER[f.severity] >= failThreshold);
|
|
6552
6623
|
return {
|
|
6553
6624
|
scanners: applicableScanners.map((s) => s.name),
|
|
6625
|
+
...skippedScanners.length > 0 ? { skippedScanners } : {},
|
|
6554
6626
|
findings,
|
|
6555
6627
|
summary,
|
|
6556
6628
|
passed,
|
|
@@ -6812,12 +6884,17 @@ async function sendWebhook(config, payload, target) {
|
|
|
6812
6884
|
try {
|
|
6813
6885
|
const targets = target ? [target] : Object.keys(FORMATTERS);
|
|
6814
6886
|
const promises = [];
|
|
6887
|
+
const safePayload = {
|
|
6888
|
+
...payload,
|
|
6889
|
+
error: payload.error ? String(redactSensitive(payload.error)) : void 0,
|
|
6890
|
+
details: payload.details ? redactSensitive(payload.details) : void 0
|
|
6891
|
+
};
|
|
6815
6892
|
for (const t of targets) {
|
|
6816
6893
|
const url = config[t];
|
|
6817
6894
|
if (!url) continue;
|
|
6818
6895
|
const formatter = FORMATTERS[t];
|
|
6819
6896
|
if (!formatter) continue;
|
|
6820
|
-
const body = formatter(
|
|
6897
|
+
const body = formatter(safePayload);
|
|
6821
6898
|
promises.push(sendSingle(url, body).catch(() => {
|
|
6822
6899
|
}));
|
|
6823
6900
|
}
|
|
@@ -7114,15 +7191,15 @@ async function fetchChangelog(options) {
|
|
|
7114
7191
|
signal: controller.signal
|
|
7115
7192
|
});
|
|
7116
7193
|
} catch {
|
|
7117
|
-
throw new
|
|
7194
|
+
throw new GpcError(`Could not fetch changelog.`, "CHANGELOG_FETCH_FAILED", 5, `View online: ${DOCS_CHANGELOG_URL}`);
|
|
7118
7195
|
} finally {
|
|
7119
7196
|
clearTimeout(timer);
|
|
7120
7197
|
}
|
|
7121
7198
|
if (!response.ok) {
|
|
7122
7199
|
if (response.status === 404 && options?.version) {
|
|
7123
|
-
throw new
|
|
7200
|
+
throw new GpcError(`Version ${options.version} not found.`, "CHANGELOG_VERSION_NOT_FOUND", 1, `Run: gpc changelog --limit 10`);
|
|
7124
7201
|
}
|
|
7125
|
-
throw new
|
|
7202
|
+
throw new GpcError(`GitHub API returned ${response.status}.`, "CHANGELOG_FETCH_FAILED", 5, `View online: ${DOCS_CHANGELOG_URL}`);
|
|
7126
7203
|
}
|
|
7127
7204
|
const data = await response.json();
|
|
7128
7205
|
const releases = Array.isArray(data) ? data : [data];
|