diff --git a/.github/workflows/lists.yml b/.github/workflows/lists.yml index da0a2dd1e..892007044 100644 --- a/.github/workflows/lists.yml +++ b/.github/workflows/lists.yml @@ -28,6 +28,12 @@ jobs: run: node --test scripts/lists.test.mjs - name: The committed lists run: node scripts/checkLists.mjs + # What the repository says about itself: the build toolchain reads + # repository.url and dies on a host it cannot parse, and a wrong homepage + # ships to everyone who installs the package. This lives here rather than + # in the build job because it must run even when the build cannot. + - name: The repository's own metadata + run: node scripts/checkRepoMeta.mjs # What phishing.pezkuwichain.io runs before it serves a new master: against a scratch # repository and web root, with these lists. - name: The host's publish checks diff --git a/package.json b/package.json index f3ed8f0d7..daeb871ab 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { "author": "Dijital Kurdistan Tech Inst ", - "bugs": "https://git.pezkuwichain.io/pezkuwichain/phishing/issues", + "bugs": "https://github.com/pezkuwichain/phishing/issues", "engines": { "node": ">=18.14" }, - "homepage": "https://git.pezkuwichain.io/pezkuwichain/phishing#readme", + "homepage": "https://github.com/pezkuwichain/phishing#readme", "license": "Apache-2.0", "packageManager": "yarn@4.6.0", "private": true, "repository": { "type": "git", - "url": "https://git.pezkuwichain.io/pezkuwichain/phishing.git" + "url": "https://github.com/pezkuwichain/phishing.git" }, "sideEffects": false, "type": "module", diff --git a/packages/phishing/package.json b/packages/phishing/package.json index 871dd2cf7..1d66ec5ed 100644 --- a/packages/phishing/package.json +++ b/packages/phishing/package.json @@ -1,17 +1,17 @@ { "author": "Dijital Kurdistan Tech Inst ", - "bugs": "https://git.pezkuwichain.io/pezkuwichain/phishing/issues", + "bugs": "https://github.com/pezkuwichain/phishing/issues", "description": "Simple phishing scripts from a central source", "engines": { "node": ">=18" }, - "homepage": "https://git.pezkuwichain.io/pezkuwichain/phishing/tree/master/packages/phishing#readme", + "homepage": "https://github.com/pezkuwichain/phishing/tree/master/packages/phishing#readme", "license": "Apache-2.0", "name": "@pezkuwi/phishing", "repository": { "directory": "packages/phishing", "type": "git", - "url": "https://git.pezkuwichain.io/pezkuwichain/phishing.git" + "url": "https://github.com/pezkuwichain/phishing.git" }, "sideEffects": [ "./packageDetect.js", diff --git a/scripts/checkRepoMeta.mjs b/scripts/checkRepoMeta.mjs new file mode 100644 index 000000000..984fc721b --- /dev/null +++ b/scripts/checkRepoMeta.mjs @@ -0,0 +1,108 @@ +// Copyright 2020-2026 @pezkuwi/phishing authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +// What this repository says about itself must be true, and must be something the +// build toolchain can read. +// +// On 2026-04-21 a commit pointed repository/homepage/bugs at our Gitea instance +// ("migrate git dependencies to Gitea mirror"). Nothing else moved: origin is +// still GitHub, the published package still says GitHub, and +// git.pezkuwichain.io/pezkuwichain/phishing answers 500 — so every installer of +// @pezkuwi/phishing got dead links for its homepage and its bug tracker. +// +// It also broke `yarn build` from 2026-09-13, when the toolchain started reading +// the field. @pezkuwi/dev does this, and it is ours: +// +// const repoPath = pkg.repository.url +// .split('https://github.com/')[1] +// .split('.git')[0]; +// +// A URL from any other host makes [1] undefined and the build dies with +// "Cannot read properties of undefined (reading 'split')" — a message that names +// neither the field nor the file. +// +// So this check is also the tripwire for the migration that was started and left +// half-done: moving this repository to Gitea for real means fixing @pezkuwi/dev +// first, so that a toolchain built for a state that hosts its own git stops +// assuming github.com. Until that happens, this check turns the metadata red +// here rather than letting it fail as an unreadable stack trace, or ship as a +// dead link. +// +// node scripts/checkRepoMeta.mjs + +import { execFileSync } from 'node:child_process'; +import { existsSync, readdirSync, readFileSync } from 'node:fs'; + +const errors = []; + +function check (name, ok, detail) { + console.log((ok ? 'ok ' : 'FAIL ') + name + (ok ? '' : ` — got: ${JSON.stringify(detail)}`)); + + if (!ok) { + errors.push(name); + } +} + +// Exactly the expression @pezkuwi/dev uses, so this fails where the build fails. +function toolchainCanRead (url) { + try { + const path = String(url).split('https://github.com/')[1].split('.git')[0]; + + return !!path && path.includes('/'); + } catch { + return false; + } +} + +const files = ['package.json']; + +for (const dir of readdirSync('packages', { withFileTypes: true })) { + if (dir.isDirectory() && existsSync(`packages/${dir.name}/package.json`)) { + files.push(`packages/${dir.name}/package.json`); + } +} + +check('every package describes itself', files.length > 1, files); + +const hosts = new Set(); + +for (const file of files) { + const pkg = JSON.parse(readFileSync(file, 'utf8')); + const url = pkg.repository?.url; + + check(`${file}: has a repository url`, !!url, pkg.repository); + check(`${file}: the build toolchain can read that url`, toolchainCanRead(url), url); + check(`${file}: homepage and bugs live on the same host as the repository`, + !!url && !!pkg.homepage && !!pkg.bugs && + new URL(pkg.homepage).host === new URL(url).host && + new URL(String(pkg.bugs)).host === new URL(url).host, + { bugs: pkg.bugs, homepage: pkg.homepage, url }); + + if (url) { + hosts.add(new URL(url).host); + } +} + +check('the whole repository agrees on one host', hosts.size === 1, [...hosts]); + +// And that host is where the code actually is. Without this the metadata can be +// internally consistent and still point somewhere the code has never been. +let origin = ''; + +try { + origin = execFileSync('git', ['remote', 'get-url', 'origin'], { encoding: 'utf8' }).trim(); +} catch { + origin = ''; +} + +if (origin) { + check('the metadata names the remote the code is actually pushed to', + hosts.has(new URL(origin.replace(/^git@([^:]+):/, 'https://$1/')).host), + { hosts: [...hosts], origin } + ); +} else { + console.log('ok (no origin remote here — nothing to compare against)'); +} + +console.log(errors.length ? `FAILED: ${errors.length}` : 'all repository metadata checks passed'); +process.exit(errors.length ? 1 : 0);