Skip to content

Commit

Permalink
feat: use autogenerated ABI target list (#83)
Browse files Browse the repository at this point in the history
* feat: use autogenerated ABI target list

`yarn update-abi-registry` generates `abi_registry.json`, which is
loaded by the module to determine relatively recent ABI targets, along
with their LTS values and whether they are "future" targets.

* build: auto-update ABI JSON file via scheduled GitHub Action
  • Loading branch information
malept authored Aug 20, 2020
1 parent 75cc2dd commit 3f6f27a
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 19 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/update-abi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Auto-update ABI JSON file
on:
schedule:
- cron: '0 0 * * *'
jobs:
autoupdate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Get npm cache directory
id: npm-cache
run: |
echo "::set-output name=dir::$(npm config get cache)"
- uses: actions/cache@v1
with:
path: ${{ steps.npm-cache.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm install
- name: Update Releases JSON
run: npm run electron-releases
- name: Commit Changes to Releases JSON
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "machine github.com login $GITHUB_ACTOR password $GITHUB_TOKEN" > ~/.netrc
chmod 600 ~/.netrc
git add abi_registry.json
if test -n "$(git status -s)"; then
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
git diff --cached
git commit -m "build: update Electron releases JSON"
git push origin HEAD:$GITHUB_REF
else
echo No update needed
fi
78 changes: 78 additions & 0 deletions abi_registry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
[
{
"runtime": "node",
"target": "11.0.0",
"lts": false,
"future": false,
"abi": "67"
},
{
"runtime": "electron",
"target": "5.0.0",
"lts": false,
"future": false,
"abi": "70"
},
{
"runtime": "node",
"target": "12.0.0",
"lts": [
"2019-10-21",
"2020-10-20"
],
"future": false,
"abi": "68"
},
{
"runtime": "electron",
"target": "6.0.0",
"lts": false,
"future": false,
"abi": "73"
},
{
"runtime": "electron",
"target": "7.0.0",
"lts": false,
"future": false,
"abi": "75"
},
{
"runtime": "electron",
"target": "8.0.0",
"lts": false,
"future": false,
"abi": "76"
},
{
"runtime": "node",
"target": "13.0.0",
"lts": false,
"future": false,
"abi": "74"
},
{
"runtime": "electron",
"target": "9.0.0-beta.1",
"lts": false,
"future": true,
"abi": "80"
},
{
"runtime": "node",
"target": "14.0.0",
"lts": [
"2020-10-20",
"2021-10-19"
],
"future": true,
"abi": "81"
},
{
"runtime": "electron",
"target": "10.0.0-beta.1",
"lts": false,
"future": true,
"abi": "82"
}
]
60 changes: 47 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,54 @@ function getTarget (abi, runtime) {
throw new Error('Could not detect target for abi ' + abi + ' and runtime ' + runtime)
}

function loadGeneratedTargets () {
var registry = require('./abi_registry.json')
var targets = {
supported: [],
additional: [],
future: []
}

registry.forEach(function (item) {
var target = {
runtime: item.runtime,
target: item.target,
abi: item.abi
}
if (item.lts) {
var startDate = new Date(Date.parse(item.lts[0]))
var endDate = new Date(Date.parse(item.lts[1]))
var currentDate = new Date()
target.lts = startDate < currentDate && currentDate < endDate
} else {
target.lts = false
}

if (target.runtime === 'node-webkit') {
targets.additional.push(target)
} else if (item.future) {
targets.future.push(target)
} else {
targets.supported.push(target)
}
})

targets.supported.sort()
targets.additional.sort()
targets.future.sort()

return targets
}

var generatedTargets = loadGeneratedTargets()

var supportedTargets = [
{runtime: 'node', target: '5.0.0', abi: '47', lts: false},
{runtime: 'node', target: '6.0.0', abi: '48', lts: false},
{runtime: 'node', target: '7.0.0', abi: '51', lts: false},
{runtime: 'node', target: '8.0.0', abi: '57', lts: false},
{runtime: 'node', target: '9.0.0', abi: '59', lts: false},
{runtime: 'node', target: '10.0.0', abi: '64', lts: new Date(2018, 10, 1) < new Date() && new Date() < new Date(2020, 4, 31)},
{runtime: 'node', target: '11.0.0', abi: '67', lts: false},
{runtime: 'node', target: '12.0.0', abi: '72', lts: new Date(2019, 9, 21) < new Date() && new Date() < new Date(2020, 9, 31)},
{runtime: 'node', target: '13.0.0', abi: '79', lts: false},
{runtime: 'node', target: '14.0.0', abi: '83', lts: false},
{runtime: 'electron', target: '0.36.0', abi: '47', lts: false},
{runtime: 'electron', target: '1.1.0', abi: '48', lts: false},
{runtime: 'electron', target: '1.3.0', abi: '49', lts: false},
Expand All @@ -70,14 +107,11 @@ var supportedTargets = [
{runtime: 'electron', target: '2.0.0', abi: '57', lts: false},
{runtime: 'electron', target: '3.0.0', abi: '64', lts: false},
{runtime: 'electron', target: '4.0.0', abi: '64', lts: false},
{runtime: 'electron', target: '4.0.4', abi: '69', lts: false},
{runtime: 'electron', target: '5.0.0', abi: '70', lts: false},
{runtime: 'electron', target: '6.0.0', abi: '73', lts: false},
{runtime: 'electron', target: '7.0.0', abi: '75', lts: false},
{runtime: 'electron', target: '8.0.0', abi: '76', lts: false},
{runtime: 'electron', target: '9.0.0', abi: '80', lts: false}
{runtime: 'electron', target: '4.0.4', abi: '69', lts: false}
]

supportedTargets.push.apply(supportedTargets, generatedTargets.supported)

var additionalTargets = [
{runtime: 'node-webkit', target: '0.13.0', abi: '47', lts: false},
{runtime: 'node-webkit', target: '0.15.0', abi: '48', lts: false},
Expand All @@ -86,6 +120,8 @@ var additionalTargets = [
{runtime: 'node-webkit', target: '0.26.5', abi: '59', lts: false}
]

additionalTargets.push.apply(additionalTargets, generatedTargets.additional)

var deprecatedTargets = [
{runtime: 'node', target: '0.2.0', abi: '1', lts: false},
{runtime: 'node', target: '0.9.1', abi: '0x000A', lts: false},
Expand All @@ -104,9 +140,7 @@ var deprecatedTargets = [
{runtime: 'electron', target: '0.33.0', abi: '46', lts: false}
]

var futureTargets = [
{runtime: 'electron', target: '10.0.0-beta.1', abi: '82', lts: false}
]
var futureTargets = generatedTargets.future

var allTargets = deprecatedTargets
.concat(supportedTargets)
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"description": "Get the Node ABI for a given target and runtime, and vice versa.",
"main": "index.js",
"scripts": {
"test": "tape test/index.js",
"semantic-release": "semantic-release",
"travis-deploy-once": "travis-deploy-once"
"test": "tape test/index.js",
"travis-deploy-once": "travis-deploy-once",
"update-abi-registry": "node --unhandled-rejections=strict scripts/update-abi-registry.js"
},
"repository": {
"type": "git",
Expand All @@ -26,6 +27,7 @@
},
"homepage": "https://github.com/lgeiger/node-abi#readme",
"devDependencies": {
"got": "^10.6.0",
"semantic-release": "^15.8.0",
"tape": "^4.6.3",
"travis-deploy-once": "^5.0.1"
Expand Down
93 changes: 93 additions & 0 deletions scripts/update-abi-registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const got = require('got')
const path = require('path')
const semver = require('semver')
const { writeFile } = require('fs').promises

async function getJSONFromCDN (urlPath) {
const response = await got(`https://cdn.jsdelivr.net/gh/${urlPath}`)
return JSON.parse(response.body)
}

async function fetchElectronVersions () {
return (await getJSONFromCDN('electron/releases/lite.json')).map(metadata => metadata.version)
}

async function fetchNodeVersions () {
const schedule = await getJSONFromCDN('nodejs/Release/schedule.json')
const versions = {}

for (const [majorVersion, metadata] of Object.entries(schedule)) {
if (majorVersion.startsWith('v0')) {
continue
}
const version = `${majorVersion.slice(1)}.0.0`
const lts = metadata.hasOwnProperty('lts') ? [metadata.lts, metadata.maintenance] : false
versions[version] = {
runtime: 'node',
target: version,
lts: lts,
future: new Date(Date.parse(metadata.start)) > new Date()
}
}

return versions
}

async function fetchAbiVersions () {
return (await getJSONFromCDN('nodejs/node/doc/abi_version_registry.json')).NODE_MODULE_VERSION
}

async function main () {
const nodeVersions = await fetchNodeVersions()
const abiVersions = await fetchAbiVersions()
const electronVersions = await fetchElectronVersions()

const abiVersionSet = new Set()
const supportedTargets = []
for (const abiVersion of abiVersions) {
if (abiVersion.modules <= 66) {
// Don't try to parse any ABI versions older than 60
break
} else if (abiVersion.runtime === 'electron' && abiVersion.modules < 70) {
// Don't try to parse Electron ABI versions below Electron 5
continue
}

let target
if (abiVersion.runtime === 'node') {
const nodeVersion = `${abiVersion.versions.replace('.0.0-pre', '')}.0.0`
target = nodeVersions[nodeVersion]
if (!target) {
continue
}
} else {
target = {
runtime: abiVersion.runtime === 'nw.js' ? 'node-webkit' : abiVersion.runtime,
target: abiVersion.versions,
lts: false,
future: false
}
if (target.runtime === 'electron') {
target.target = `${target.target}.0.0`
const constraint = /^[0-9]/.test(abiVersion.versions) ? `>= ${abiVersion.versions}` : abiVersion.versions
if (!electronVersions.find(electronVersion => semver.satisfies(electronVersion, constraint))) {
target.target = `${target.target}-beta.1`
target.future = true
}
}
}
target.abi = abiVersion.modules.toString()

const key = [target.runtime, target.target].join('-')
if (abiVersionSet.has(key)) {
continue
}

abiVersionSet.add(key)
supportedTargets.unshift(target)
}

await writeFile(path.resolve(__dirname, '..', 'abi_registry.json'), JSON.stringify(supportedTargets, null, 2))
}

main()
10 changes: 6 additions & 4 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var getTarget = require('../index').getTarget
var getNextTarget = require('../index')._getNextTarget
var allTargets = require('../index').allTargets

test('getNextTarget gets the next unsopported target', function (t) {
test('getNextTarget gets the next unsupported target', function (t) {
var mockTargets = [
{runtime: 'node', target: '7.0.0', abi: '51', lts: false},
{runtime: 'node', target: '8.0.0', abi: '57', lts: false},
Expand Down Expand Up @@ -35,6 +35,7 @@ test('getTarget calculates correct Electron target', function (t) {
t.equal(getTarget('48', 'electron'), '1.1.0')
t.equal(getTarget('49', 'electron'), '1.3.0')
t.equal(getTarget('50', 'electron'), '1.4.0')
t.equal(getTarget('76', 'electron'), '8.0.0')
t.end()
})

Expand All @@ -53,6 +54,7 @@ test('getAbi calculates correct Node ABI', function (t) {
t.equal(getAbi(null), process.versions.modules)
t.throws(function () { getAbi('a.b.c') })
t.throws(function () { getAbi(getNextTarget('node')) })
t.equal(getAbi('12.0.0'), '68')
t.equal(getAbi('7.2.0'), '51')
t.equal(getAbi('7.0.0'), '51')
t.equal(getAbi('6.9.9'), '48')
Expand Down Expand Up @@ -155,8 +157,8 @@ test('allTargets are sorted', function (t) {
return semver.compare(t1.target, t2.target)
}

t.deepEqual(electron, electron.slice().sort(sort))
t.deepEqual(node, node.slice().sort(sort))
t.deepEqual(nodeWebkit, nodeWebkit.slice().sort(sort))
t.deepEqual(electron, electron.slice().sort(sort), 'electron targets are sorted')
t.deepEqual(node, node.slice().sort(sort), 'node targets are sorted')
t.deepEqual(nodeWebkit, nodeWebkit.slice().sort(sort), 'node-webkit targets are sorted')
t.end()
})

0 comments on commit 3f6f27a

Please sign in to comment.