nodejs-latest-linker
Advanced tools
Comparing version
@@ -5,18 +5,7 @@ #!/usr/bin/env node | ||
const fs = require('fs') | ||
const fs = require('fs/promises') | ||
const path = require('path') | ||
const semver = require('semver') | ||
const map = require('map-async') | ||
const { Linker } = require('./common.js') | ||
const ltsNames = { | ||
4: 'argon', | ||
6: 'boron', | ||
8: 'carbon', | ||
10: 'dubnium', | ||
12: 'erbium', | ||
14: 'fermium', | ||
16: 'gallium', | ||
18: 'hydrogen' | ||
} | ||
/* c8 ignore next 3 */ | ||
if (process.argv.length < 3) { | ||
@@ -27,133 +16,22 @@ throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]') | ||
const dir = path.resolve(process.argv[2]) | ||
const docsDir = process.argv[3] && path.resolve(process.argv[3]) | ||
const docsDir = process.argv[3] && path.resolve(process.argv[3]); | ||
if (!fs.statSync(dir).isDirectory()) { | ||
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]') | ||
} | ||
if (docsDir && !fs.statSync(docsDir).isDirectory()) { | ||
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]') | ||
} | ||
map( | ||
fs.readdirSync(dir).map((d) => path.join(dir, d)), | ||
(d, callback) => fs.stat(d, (_, stat) => callback(null, { d, stat })), | ||
afterMap | ||
) | ||
function afterMap (err, allDirs) { | ||
if (err) { | ||
throw err | ||
(async function main () { | ||
/* c8 ignore next 3 */ | ||
if (!(await fs.stat(dir)).isDirectory()) { | ||
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]') | ||
} | ||
allDirs = allDirs.filter((d) => d.stat && d.stat.isDirectory()) | ||
.map((d) => path.basename(d.d)) | ||
.map((d) => { | ||
try { | ||
return semver.parse(d) | ||
} catch (e) {} | ||
}) | ||
.filter(Boolean) | ||
makeDocsLinks(allDirs.map((v) => v.raw)) | ||
const dirs = allDirs.filter((d) => semver.satisfies(d, '~0.10 || ~0.12 || >= 1.0')) | ||
.map((d) => d.raw) | ||
dirs.sort((d1, d2) => semver.compare(d1, d2)) | ||
link('0.10', dirs) | ||
link(0.12, dirs) | ||
for (let i = 1; ; i++) { | ||
if (!link(i, dirs) && i >= 4) { | ||
break | ||
} | ||
/* c8 ignore next 3 */ | ||
if (docsDir && !(await fs.stat(docsDir)).isDirectory()) { | ||
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]') | ||
} | ||
const max = link(null, dirs) | ||
const tbreg = new RegExp(`(\\w+)-${max}.tar.gz`) | ||
let tarball = fs.readdirSync(path.join(dir, 'latest')) | ||
.filter((f) => tbreg.test(f)) | ||
if (tarball.length !== 1) { | ||
throw new Error('Could not find latest.tar.gz') | ||
const allDirs = (await fs.readdir(dir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name) | ||
const linker = new Linker({ baseDir: dir, docsDir }) | ||
const links = await linker.getLinks(allDirs, fs.readdir) | ||
for (const [dest, src] of links) { | ||
await fs.unlink(dest).catch(() => {}) | ||
await fs.symlink(src, dest) | ||
} | ||
tarball = tarball[0] | ||
const name = tarball.match(tbreg)[1] | ||
const dst = path.join(dir, `${name}-latest.tar.gz`) | ||
try { | ||
fs.unlinkSync(dst) | ||
} catch (e) {} | ||
fs.symlinkSync(path.join(dir, 'latest', tarball), dst) | ||
} | ||
function makeDocsLinks (versions) { | ||
if (!docsDir) { | ||
return | ||
} | ||
versions.forEach((version) => { | ||
const src = path.join(dir, version, 'docs') | ||
const dst = path.join(docsDir, version) | ||
fs.stat(src, (err, stat) => { | ||
if (err || !stat.isDirectory()) { | ||
return | ||
} | ||
fs.unlink(dst, () => { | ||
fs.symlink(src, dst, (err) => { | ||
if (err) { | ||
throw err | ||
} | ||
}) | ||
}) | ||
}) | ||
}) | ||
} | ||
function link (version, dirs) { | ||
const line = version && `${version}.x` | ||
const range = version ? `${Number(version) < 1 ? '~' : '^'}${line}` : '*' | ||
const max = semver.maxSatisfying(dirs, range) | ||
if (!max) { | ||
return false | ||
} | ||
function symlink (name) { | ||
const dst = path.join(dir, name) | ||
const src = path.join(dir, max) | ||
try { | ||
fs.unlinkSync(dst) | ||
} catch (e) {} | ||
fs.symlinkSync(src, dst) | ||
if (!docsDir) { | ||
return | ||
} | ||
const dsrc = path.join(dir, max, 'docs') | ||
const ddst = path.join(docsDir, name) | ||
try { | ||
fs.unlinkSync(ddst) | ||
} catch (e) {} | ||
fs.symlinkSync(dsrc, ddst) | ||
} | ||
if (line) { | ||
symlink(`latest-v${line}`) | ||
if (ltsNames[version]) { | ||
symlink(`latest-${ltsNames[version]}`) | ||
} | ||
} else { | ||
symlink('latest') | ||
} | ||
return max | ||
} | ||
})() |
{ | ||
"name": "nodejs-latest-linker", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "An application to create latest-X symlinks at https://nodejs.org/download/release/ after each new release", | ||
@@ -8,3 +8,3 @@ "main": "latest-linker.js", | ||
"lint": "standard", | ||
"test": "npm run lint && tap test.js" | ||
"test": "npm run lint && tap --allow-incomplete-coverage test.js" | ||
}, | ||
@@ -18,3 +18,2 @@ "repository": { | ||
"dependencies": { | ||
"map-async": "^0.1.1", | ||
"semver": "^7.3.2" | ||
@@ -27,5 +26,5 @@ }, | ||
"devDependencies": { | ||
"tap": "^14.10.8", | ||
"standard": "^14.3.4" | ||
"standard": "^17.1.0", | ||
"tap": "^18.4.0" | ||
} | ||
} |
20
test.js
@@ -7,3 +7,23 @@ 'use strict' | ||
const tap = require('tap') | ||
const { Linker } = require('./common.js') | ||
tap.test('Linker', async t => { | ||
const linker = new Linker({ baseDir: 'base', docsDir: 'docs' }) | ||
const links = await linker.getLinks( | ||
['v19.8.0/', 'v19.8.1/', 'v19.9.0/', 'v20.7.0/', 'v20.8.0/'], | ||
async () => ['docs/', 'win-x64/', 'node-v20.8.0-aix-ppc64.tar.gz', 'node-v20.8.0-arm64.msi', 'node-v20.8.0-headers.tar.gz', 'node-v20.8.0.tar.gz'] | ||
) | ||
t.same(links, new Map([ | ||
['docs/v19.8.0', 'base/v19.8.0/docs'], | ||
['docs/v19.8.1', 'base/v19.8.1/docs'], | ||
['docs/v19.9.0', 'base/v19.9.0/docs'], | ||
['docs/v20.7.0', 'base/v20.7.0/docs'], | ||
['docs/v20.8.0', 'base/v20.8.0/docs'], | ||
['base/latest', 'base/v20.8.0'], | ||
['docs/latest', 'base/v20.8.0/docs'], | ||
['base/node-latest.tar.gz', 'base/latest/node-v20.8.0.tar.gz'] | ||
])) | ||
t.end() | ||
}) | ||
tap.test('basic test', t => { | ||
@@ -10,0 +30,0 @@ const dir = t.testdir({ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
10883
14.28%1
-50%9
12.5%178
14.1%3
200%- Removed
- Removed