lockdelta 0.1.2 → 0.1.3
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 +11 -5
- package/dist/action.cjs +128 -22
- package/dist/action.cjs.map +1 -1
- package/dist/cli.js +98 -20
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +98 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ No inputs are required on `pull_request` or `push` events. The action reads the
|
|
|
72
72
|
|
|
73
73
|
### Markdown summary
|
|
74
74
|
|
|
75
|
-
When `markdown: 'true'` or `post-comment` is not `'false'`, lockdelta generates a three-section markdown summary. Direct production dependencies are **bold**, dev dependencies are *italic*, and transitive deps are plain. Package names link to their registry (PyPI, npmjs, jsr.io).
|
|
75
|
+
When `markdown: 'true'` or `post-comment` is not `'false'`, lockdelta generates a three-section markdown summary. Direct production dependencies are **bold**, dev dependencies are *italic*, and transitive deps are plain. Package names link to their public registry (PyPI, npmjs, jsr.io). Packages sourced from a private registry are shown without a link; GitHub Packages scoped packages link to their GitHub repository page instead.
|
|
76
76
|
|
|
77
77
|
```yaml
|
|
78
78
|
- name: Diff dependencies
|
|
@@ -234,14 +234,17 @@ console.log(report.summary);
|
|
|
234
234
|
|
|
235
235
|
```ts
|
|
236
236
|
import { registerEcosystem } from 'lockdelta';
|
|
237
|
-
import type { Ecosystem, DirectDeps } from 'lockdelta';
|
|
237
|
+
import type { Ecosystem, DirectDeps, PackageEntry } from 'lockdelta';
|
|
238
238
|
|
|
239
239
|
const rubyEcosystem: Ecosystem = {
|
|
240
240
|
name: 'ruby',
|
|
241
241
|
supportedLockfiles: [{ filename: 'Gemfile.lock', type: 'bundler' }],
|
|
242
242
|
manifestName: 'Gemfile',
|
|
243
243
|
getLockfileType: (filename) => filename === 'Gemfile.lock' ? 'bundler' : undefined,
|
|
244
|
-
parseLockfile: (content, _type)
|
|
244
|
+
parseLockfile: (content, _type): Record<string, PackageEntry> => {
|
|
245
|
+
// parse and return { packageName: { version, registryUrl? } }
|
|
246
|
+
return {};
|
|
247
|
+
},
|
|
245
248
|
parseDirectDeps: (content): DirectDeps => ({ prod: new Set(), dev: new Set() }),
|
|
246
249
|
normalizeName: (name) => name.toLowerCase(),
|
|
247
250
|
};
|
|
@@ -259,8 +262,11 @@ interface PackageChange {
|
|
|
259
262
|
change_type: 'added' | 'removed' | 'updated';
|
|
260
263
|
old_version: string | null;
|
|
261
264
|
new_version: string | null;
|
|
262
|
-
is_direct: boolean;
|
|
263
|
-
is_dev: boolean;
|
|
265
|
+
is_direct: boolean; // declared in the project manifest
|
|
266
|
+
is_dev: boolean; // declared in a dev/optional dependency section
|
|
267
|
+
old_registry_url?: string; // registry origin of the old version (e.g. 'https://npm.pkg.github.com')
|
|
268
|
+
new_registry_url?: string; // registry origin of the new version
|
|
269
|
+
// Both fields present on 'updated' changes: a mismatch signals a potential registry switch
|
|
264
270
|
}
|
|
265
271
|
|
|
266
272
|
interface DiffReport {
|
package/dist/action.cjs
CHANGED
|
@@ -7511,7 +7511,31 @@ function applyFiltersConfig(config, changes) {
|
|
|
7511
7511
|
}
|
|
7512
7512
|
|
|
7513
7513
|
// src/action/markdown.ts
|
|
7514
|
-
|
|
7514
|
+
var PUBLIC_NPM_ORIGINS = /* @__PURE__ */ new Set(["https://registry.npmjs.org", "https://registry.yarnpkg.com"]);
|
|
7515
|
+
var PUBLIC_PYPI_ORIGIN = "https://pypi.org";
|
|
7516
|
+
function packageUrl(ecosystem, name, registryUrl) {
|
|
7517
|
+
if (registryUrl !== void 0) {
|
|
7518
|
+
let origin;
|
|
7519
|
+
try {
|
|
7520
|
+
origin = new URL(registryUrl).origin;
|
|
7521
|
+
} catch {
|
|
7522
|
+
return null;
|
|
7523
|
+
}
|
|
7524
|
+
const isNpmLike = ecosystem === "javascript" || ecosystem === "deno" && !name.startsWith("jsr:");
|
|
7525
|
+
if (isNpmLike) {
|
|
7526
|
+
if (!PUBLIC_NPM_ORIGINS.has(origin)) {
|
|
7527
|
+
if (origin === "https://npm.pkg.github.com" && name.startsWith("@")) {
|
|
7528
|
+
const parts = name.slice(1).split("/");
|
|
7529
|
+
if (parts.length === 2) return `https://github.com/${parts[0]}/${parts[1]}`;
|
|
7530
|
+
}
|
|
7531
|
+
return null;
|
|
7532
|
+
}
|
|
7533
|
+
} else if (ecosystem === "python") {
|
|
7534
|
+
if (!origin.startsWith(PUBLIC_PYPI_ORIGIN)) {
|
|
7535
|
+
return null;
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7515
7539
|
switch (ecosystem) {
|
|
7516
7540
|
case "python":
|
|
7517
7541
|
return `https://pypi.org/project/${name}/`;
|
|
@@ -7525,7 +7549,11 @@ function packageUrl(ecosystem, name) {
|
|
|
7525
7549
|
}
|
|
7526
7550
|
}
|
|
7527
7551
|
function formatName(change, ecosystem) {
|
|
7528
|
-
const url = packageUrl(
|
|
7552
|
+
const url = packageUrl(
|
|
7553
|
+
ecosystem,
|
|
7554
|
+
change.name,
|
|
7555
|
+
change.new_registry_url ?? change.old_registry_url
|
|
7556
|
+
);
|
|
7529
7557
|
const linked = url ? `[${change.name}](${url})` : change.name;
|
|
7530
7558
|
if (change.is_direct && !change.is_dev) return `**${linked}**`;
|
|
7531
7559
|
if (change.is_dev) return `*${linked}*`;
|
|
@@ -7620,7 +7648,7 @@ function parseDenoLock(content) {
|
|
|
7620
7648
|
const { name, version } = splitSpecifier(specifier);
|
|
7621
7649
|
const resultKey = key === "jsr" ? `jsr:${name}` : name;
|
|
7622
7650
|
if (name && version && !result[resultKey]) {
|
|
7623
|
-
result[resultKey] = version;
|
|
7651
|
+
result[resultKey] = { version };
|
|
7624
7652
|
}
|
|
7625
7653
|
}
|
|
7626
7654
|
}
|
|
@@ -7699,7 +7727,7 @@ function parseBunLock(content) {
|
|
|
7699
7727
|
if (typeof nameAtVersion !== "string") continue;
|
|
7700
7728
|
const version = extractVersion(nameAtVersion);
|
|
7701
7729
|
if (!version || version.startsWith("workspace:")) continue;
|
|
7702
|
-
result[name] = version;
|
|
7730
|
+
result[name] = { version };
|
|
7703
7731
|
}
|
|
7704
7732
|
return result;
|
|
7705
7733
|
}
|
|
@@ -7734,7 +7762,10 @@ function parseV2Packages(packages) {
|
|
|
7734
7762
|
const name = key.slice("node_modules/".length);
|
|
7735
7763
|
const pkgVersion = pkg.version;
|
|
7736
7764
|
if (pkgVersion && !result[name]) {
|
|
7737
|
-
|
|
7765
|
+
const entry = { version: pkgVersion };
|
|
7766
|
+
const registryUrl = resolvedToOrigin(pkg.resolved);
|
|
7767
|
+
if (registryUrl !== void 0) entry.registryUrl = registryUrl;
|
|
7768
|
+
result[name] = entry;
|
|
7738
7769
|
}
|
|
7739
7770
|
}
|
|
7740
7771
|
return result;
|
|
@@ -7742,7 +7773,10 @@ function parseV2Packages(packages) {
|
|
|
7742
7773
|
function parseV1Dependencies(deps, result = {}) {
|
|
7743
7774
|
for (const [name, pkg] of Object.entries(deps)) {
|
|
7744
7775
|
if (pkg.version && !result[name]) {
|
|
7745
|
-
|
|
7776
|
+
const entry = { version: pkg.version };
|
|
7777
|
+
const registryUrl = resolvedToOrigin(pkg.resolved);
|
|
7778
|
+
if (registryUrl !== void 0) entry.registryUrl = registryUrl;
|
|
7779
|
+
result[name] = entry;
|
|
7746
7780
|
}
|
|
7747
7781
|
if (pkg.dependencies) {
|
|
7748
7782
|
parseV1Dependencies(pkg.dependencies, result);
|
|
@@ -7750,6 +7784,14 @@ function parseV1Dependencies(deps, result = {}) {
|
|
|
7750
7784
|
}
|
|
7751
7785
|
return result;
|
|
7752
7786
|
}
|
|
7787
|
+
function resolvedToOrigin(resolved) {
|
|
7788
|
+
if (!resolved) return void 0;
|
|
7789
|
+
try {
|
|
7790
|
+
return new URL(resolved).origin;
|
|
7791
|
+
} catch {
|
|
7792
|
+
return void 0;
|
|
7793
|
+
}
|
|
7794
|
+
}
|
|
7753
7795
|
|
|
7754
7796
|
// src/ecosystems/javascript/parsers/pnpm.ts
|
|
7755
7797
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
@@ -7769,7 +7811,7 @@ function parseLockfileVersion(v) {
|
|
|
7769
7811
|
}
|
|
7770
7812
|
function parsePnpmV9(packages) {
|
|
7771
7813
|
const result = {};
|
|
7772
|
-
for (const key of Object.
|
|
7814
|
+
for (const [key, value] of Object.entries(packages)) {
|
|
7773
7815
|
let name;
|
|
7774
7816
|
let version;
|
|
7775
7817
|
if (key.startsWith("@")) {
|
|
@@ -7785,14 +7827,18 @@ function parsePnpmV9(packages) {
|
|
|
7785
7827
|
}
|
|
7786
7828
|
version = stripVersionSuffix(version);
|
|
7787
7829
|
if (name && version && !result[name]) {
|
|
7788
|
-
|
|
7830
|
+
const entry = { version };
|
|
7831
|
+
const pkg = value;
|
|
7832
|
+
const registryUrl = pkg?.resolution?.tarball ? resolvedToOrigin2(pkg.resolution.tarball) : void 0;
|
|
7833
|
+
if (registryUrl !== void 0) entry.registryUrl = registryUrl;
|
|
7834
|
+
result[name] = entry;
|
|
7789
7835
|
}
|
|
7790
7836
|
}
|
|
7791
7837
|
return result;
|
|
7792
7838
|
}
|
|
7793
7839
|
function parsePnpmLegacy(packages) {
|
|
7794
7840
|
const result = {};
|
|
7795
|
-
for (const key of Object.
|
|
7841
|
+
for (const [key, value] of Object.entries(packages)) {
|
|
7796
7842
|
const cleaned = key.startsWith("/") ? key.slice(1) : key;
|
|
7797
7843
|
let name;
|
|
7798
7844
|
let version;
|
|
@@ -7823,7 +7869,11 @@ function parsePnpmLegacy(packages) {
|
|
|
7823
7869
|
}
|
|
7824
7870
|
version = stripVersionSuffix(version);
|
|
7825
7871
|
if (name && version && !result[name]) {
|
|
7826
|
-
|
|
7872
|
+
const entry = { version };
|
|
7873
|
+
const pkg = value;
|
|
7874
|
+
const registryUrl = pkg?.resolution?.tarball ? resolvedToOrigin2(pkg.resolution.tarball) : void 0;
|
|
7875
|
+
if (registryUrl !== void 0) entry.registryUrl = registryUrl;
|
|
7876
|
+
result[name] = entry;
|
|
7827
7877
|
}
|
|
7828
7878
|
}
|
|
7829
7879
|
return result;
|
|
@@ -7831,6 +7881,13 @@ function parsePnpmLegacy(packages) {
|
|
|
7831
7881
|
function stripVersionSuffix(version) {
|
|
7832
7882
|
return version.split("(")[0].split("_")[0].trim();
|
|
7833
7883
|
}
|
|
7884
|
+
function resolvedToOrigin2(url) {
|
|
7885
|
+
try {
|
|
7886
|
+
return new URL(url).origin;
|
|
7887
|
+
} catch {
|
|
7888
|
+
return void 0;
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7834
7891
|
|
|
7835
7892
|
// src/ecosystems/javascript/parsers/yarn.ts
|
|
7836
7893
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
@@ -7861,7 +7918,13 @@ function parseYarnV1(content) {
|
|
|
7861
7918
|
const firstSpecifier = headerLine.split(",")[0].trim().replace(/^"|"$/g, "");
|
|
7862
7919
|
const name = extractNameFromSpecifier(firstSpecifier);
|
|
7863
7920
|
if (name && !packages[name]) {
|
|
7864
|
-
|
|
7921
|
+
const entry = { version: versionMatch[1] };
|
|
7922
|
+
const resolvedMatch = trimmed.match(/^[ \t]+resolved "([^"]+)"/m);
|
|
7923
|
+
if (resolvedMatch) {
|
|
7924
|
+
const registryUrl = resolvedToOrigin3(resolvedMatch[1]);
|
|
7925
|
+
if (registryUrl !== void 0) entry.registryUrl = registryUrl;
|
|
7926
|
+
}
|
|
7927
|
+
packages[name] = entry;
|
|
7865
7928
|
}
|
|
7866
7929
|
}
|
|
7867
7930
|
return packages;
|
|
@@ -7872,13 +7935,16 @@ function parseYarnBerry(content) {
|
|
|
7872
7935
|
for (const [key, value] of Object.entries(data)) {
|
|
7873
7936
|
if (key === "__metadata") continue;
|
|
7874
7937
|
if (typeof value !== "object" || !value) continue;
|
|
7875
|
-
const
|
|
7876
|
-
if (
|
|
7877
|
-
if (!
|
|
7938
|
+
const berryEntry = value;
|
|
7939
|
+
if (berryEntry.linkType === "soft") continue;
|
|
7940
|
+
if (!berryEntry.version) continue;
|
|
7878
7941
|
const cleanKey = key.replace(/^"|"$/g, "");
|
|
7879
7942
|
const name = extractNameFromBerryKey(cleanKey);
|
|
7880
7943
|
if (name && !packages[name]) {
|
|
7881
|
-
|
|
7944
|
+
const entry = { version: berryEntry.version };
|
|
7945
|
+
const registryUrl = extractBerryRegistryOrigin(berryEntry.resolution);
|
|
7946
|
+
if (registryUrl !== void 0) entry.registryUrl = registryUrl;
|
|
7947
|
+
packages[name] = entry;
|
|
7882
7948
|
}
|
|
7883
7949
|
}
|
|
7884
7950
|
return packages;
|
|
@@ -7890,6 +7956,23 @@ function extractNameFromBerryKey(key) {
|
|
|
7890
7956
|
}
|
|
7891
7957
|
return key.split("@")[0];
|
|
7892
7958
|
}
|
|
7959
|
+
function extractBerryRegistryOrigin(resolution) {
|
|
7960
|
+
if (!resolution) return void 0;
|
|
7961
|
+
const atIdx = resolution.startsWith("@") ? resolution.indexOf("@", 1) : resolution.indexOf("@");
|
|
7962
|
+
if (atIdx < 0) return void 0;
|
|
7963
|
+
const spec = resolution.slice(atIdx + 1);
|
|
7964
|
+
if (spec.startsWith("http://") || spec.startsWith("https://")) {
|
|
7965
|
+
return resolvedToOrigin3(spec.split("#")[0]);
|
|
7966
|
+
}
|
|
7967
|
+
return void 0;
|
|
7968
|
+
}
|
|
7969
|
+
function resolvedToOrigin3(url) {
|
|
7970
|
+
try {
|
|
7971
|
+
return new URL(url).origin;
|
|
7972
|
+
} catch {
|
|
7973
|
+
return void 0;
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7893
7976
|
|
|
7894
7977
|
// src/ecosystems/javascript/index.ts
|
|
7895
7978
|
var SUPPORTED_LOCKFILES2 = [
|
|
@@ -8625,7 +8708,15 @@ function parseTomlPackages(content) {
|
|
|
8625
8708
|
const packages = {};
|
|
8626
8709
|
for (const pkg of [...data.package ?? [], ...data.packages ?? []]) {
|
|
8627
8710
|
if (typeof pkg.name === "string" && typeof pkg.version === "string") {
|
|
8628
|
-
|
|
8711
|
+
const entry = { version: pkg.version };
|
|
8712
|
+
const sourceUrl = typeof pkg.source?.registry === "string" ? pkg.source.registry : typeof pkg.source?.url === "string" ? pkg.source.url : void 0;
|
|
8713
|
+
if (sourceUrl !== void 0) {
|
|
8714
|
+
try {
|
|
8715
|
+
entry.registryUrl = new URL(sourceUrl).origin;
|
|
8716
|
+
} catch {
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8719
|
+
packages[pkg.name] = entry;
|
|
8629
8720
|
}
|
|
8630
8721
|
}
|
|
8631
8722
|
return packages;
|
|
@@ -8640,7 +8731,17 @@ function parseTomlPackagesRegex(content) {
|
|
|
8640
8731
|
const nameMatch = block.match(/\nname\s*=\s*"([^"]+)"/);
|
|
8641
8732
|
const versionMatch = block.match(/\nversion\s*=\s*"([^"]+)"/);
|
|
8642
8733
|
if (nameMatch && versionMatch) {
|
|
8643
|
-
|
|
8734
|
+
const entry = { version: versionMatch[1] };
|
|
8735
|
+
const sourceRegistryMatch = block.match(/source\s*=\s*\{[^}]*registry\s*=\s*"([^"]+)"/);
|
|
8736
|
+
const sourceUrlMatch = block.match(/\nurl\s*=\s*"([^"]+)"/);
|
|
8737
|
+
const sourceUrl = sourceRegistryMatch?.[1] ?? sourceUrlMatch?.[1];
|
|
8738
|
+
if (sourceUrl !== void 0) {
|
|
8739
|
+
try {
|
|
8740
|
+
entry.registryUrl = new URL(sourceUrl).origin;
|
|
8741
|
+
} catch {
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
packages[nameMatch[1]] = entry;
|
|
8644
8745
|
}
|
|
8645
8746
|
}
|
|
8646
8747
|
return packages;
|
|
@@ -8790,18 +8891,23 @@ function diffPackages(oldPkgs, newPkgs, directDeps, normalizeName) {
|
|
|
8790
8891
|
for (const name of [...allNames].sort()) {
|
|
8791
8892
|
const inOld = name in oldPkgs;
|
|
8792
8893
|
const inNew = name in newPkgs;
|
|
8793
|
-
if (inOld && inNew && oldPkgs[name] === newPkgs[name]) continue;
|
|
8894
|
+
if (inOld && inNew && oldPkgs[name].version === newPkgs[name].version) continue;
|
|
8794
8895
|
const normalized = normalizeName(name);
|
|
8795
8896
|
const isProd = directDeps.prod.has(normalized);
|
|
8796
8897
|
const isDev = directDeps.dev.has(normalized) && !isProd;
|
|
8797
|
-
|
|
8898
|
+
const change = {
|
|
8798
8899
|
name,
|
|
8799
8900
|
change_type: !inOld ? "added" : !inNew ? "removed" : "updated",
|
|
8800
|
-
old_version: inOld ? oldPkgs[name] : null,
|
|
8801
|
-
new_version: inNew ? newPkgs[name] : null,
|
|
8901
|
+
old_version: inOld ? oldPkgs[name].version : null,
|
|
8902
|
+
new_version: inNew ? newPkgs[name].version : null,
|
|
8802
8903
|
is_direct: isProd || isDev,
|
|
8803
8904
|
is_dev: isDev
|
|
8804
|
-
}
|
|
8905
|
+
};
|
|
8906
|
+
const oldRegistryUrl = inOld ? oldPkgs[name].registryUrl : void 0;
|
|
8907
|
+
const newRegistryUrl = inNew ? newPkgs[name].registryUrl : void 0;
|
|
8908
|
+
if (oldRegistryUrl !== void 0) change.old_registry_url = oldRegistryUrl;
|
|
8909
|
+
if (newRegistryUrl !== void 0) change.new_registry_url = newRegistryUrl;
|
|
8910
|
+
changes.push(change);
|
|
8805
8911
|
}
|
|
8806
8912
|
return changes;
|
|
8807
8913
|
}
|