Skip to content

Commit

Permalink
feat: Pull from GitHub instead of npm
Browse files Browse the repository at this point in the history
Fixes: #36

- fetch from ICU’s GitHub release instead of npm (ICU v50+)
- set env FULL_ICU_PREFER_NPM=1 to prefer npm instead
- add .eslint (as well as standard)
- for ICU 67 and following, fetch from icu4c-___-data-_.zip
- otherwise fetch from icu4c-src.zip for little endian
- otherwise, use npm as before
- work around ICU issue with data file name
  https://unicode-org.atlassian.net/browse/ICU-21764
- document the FULL_ICU_PREFER_NPM
- test with FULL_ICU_PREFER_NPM=1
  • Loading branch information
srl295 committed Sep 30, 2021
1 parent 0a1b7a4 commit a57877d
Show file tree
Hide file tree
Showing 18 changed files with 390 additions and 14 deletions.
17 changes: 17 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
env: {
commonjs: true,
es6: true,
node: true
},
extends: 'standard',
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018
},
rules: {
}
}
11 changes: 10 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Lint
on: [push, pull_request]

jobs:
build:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -12,3 +12,12 @@ jobs:
node-version: 16
- run: npm i
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 16
- run: npm i
- run: npm t
4 changes: 2 additions & 2 deletions .github/workflows/test.yml → .github/workflows/test-gh.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Integration Test
name: Integration Test GitHub

on: [push, pull_request]

Expand Down Expand Up @@ -28,4 +28,4 @@ jobs:
- name: Install full-icu
run: docker run --rm -v $(pwd):/usr/src/app -w /usr/src/app ${{ matrix.container }} npm i --no-package-lock --unsafe-perm
- name: Test full-icu
run: docker run --rm -v $(pwd):/usr/src/app -w /usr/src/app ${{ matrix.container }} --icu-data-dir=. ./full-icu-test/test.js
run: docker run --rm -e NODE_ICU_DATA=. -v $(pwd):/usr/src/app -w /usr/src/app ${{ matrix.container }} node ./full-icu-test/test.js
24 changes: 24 additions & 0 deletions .github/workflows/test-npm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Integration Test npm

on: [push, pull_request]

jobs:
build:
strategy:
fail-fast: false
matrix:
container:
- node:12
- node:12-slim
# Will fail on versions that aren't in icu4c-data
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2
with:
repository: nodejs/full-icu-test
path: full-icu-test
- name: Install full-icu
run: docker run --rm -v $(pwd):/usr/src/app -w /usr/src/app ${{ matrix.container }} env FULL_ICU_PREFER_NPM=1 npm i --no-package-lock --unsafe-perm
- name: Test full-icu
run: docker run --rm -e NODE_ICU_DATA=. -v $(pwd):/usr/src/app -w /usr/src/app ${{ matrix.container }} node ./full-icu-test/test.js
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ node_modules
*.dat
npm-debug.log
/yarn.lock
package-lock.json
/.nyc_output
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
node_modules
.svn
.git
npm-debug.log
npm-debug.log
/test
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: node_js
node_js:
- '8'
- '10'
- '11'
- '12'
script:
- npm install
- npm t
cache:
directories:
- node_modules
- ".nvm"
# this is a comment
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# full-icu-npm

Install full ICU data
Install full ICU data from GitHub or npm

### To use

`npm install full-icu`

Note: Set env var `FULL_ICU_PREFER_NPM=true` to prefer using the `icu4c-data` npm module,
otherwise the default is now to load from ICU4C’s GitHub release.

### To install globally

`npm install -g full-icu`
Expand Down
69 changes: 69 additions & 0 deletions install-gh-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (C) 2015-2016 IBM Corporation and Others. All Rights Reserved.

// Install by fetching ICU source tarball
// This will only work for little endian systems, but will work for ancient ICU (back to v50)

const fs = require('fs')
const { URL } = require('url')
const process = require('process')
const myFetch = require('./myFetch')
const yauzl = require('yauzl')

// var isglobal = process.env.npm_config_global === 'true';

module.exports = async function installFromGithub (fullIcu, advice) {
// const icupkg = fullIcu.icupkg
const { icudat, icuend } = fullIcu
if (fs.existsSync(icudat)) {
console.log(` √ ${icudat} (exists)`)
return
}

// var cmdPath = nodePath = process.env.npm_node_execpath;

// var npmPath = process.env.npm_execpath;

// var args;
// https://github.com/unicode-org/icu/releases/download/release-51-3/icu4c-51_3-src.zip
const _baseUrl = process.env.FULL_ICU_BASEURL || 'https://github.com/unicode-org/icu/releases/'
const baseUrl = new URL(_baseUrl)
const versionsAsHyphen = fullIcu.icuver.replace(/\./g, '-')
// ICU v67/v68 use "68.1" and "67.1" in the filename instead of 68_1 and 69_1
// https://unicode-org.atlassian.net/browse/ICU-21764
// Can remove this conditional if the files are updated later.
const versionsAsUnderscore = (fullIcu.icumaj >= 69) ? fullIcu.icuver.replace(/\./g, '_') : fullIcu.icuver
const tag = `release-${versionsAsHyphen}`
const file = `icu4c-${versionsAsUnderscore}-data-bin-${icuend}.zip`
const fullUrl = new URL(`./download/${tag}/${file}`, baseUrl)
console.log(fullUrl.toString())
const [srcZip, tmpd] = await myFetch(fullUrl)

console.log(srcZip, tmpd)
// now, unpack it

console.log(`Looking for ${icudat}`)
return new Promise((resolve, reject) =>
yauzl.open(srcZip, { lazyEntries: true }, (err, zipfile) => {
if (err) return reject(err)
zipfile.readEntry()
zipfile.on('end', () => reject(Error(`Not found in zipfile: ${icudat}`)))
zipfile.on('entry', (entry) => {
if (entry.fileName.endsWith('/')) {
zipfile.readEntry()
} else if (entry.fileName.endsWith(icudat) || entry.fileName.endsWith('/' + icudat)) {
console.log('found ' + entry.fileName)
zipfile.openReadStream(entry, (err, readStream) => {
if (err) return reject(err)
// if entry.file
readStream.on('end', () => zipfile.readEntry())
const pipeOut = fs.createWriteStream(icudat)
readStream.pipe(pipeOut)
console.log(` √ ${icudat} (from ICU binary data tarball)`)
return resolve()
})
} else {
zipfile.readEntry() // get next
}
})
}))
}
71 changes: 71 additions & 0 deletions install-gh.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (C) 2015-2016 IBM Corporation and Others. All Rights Reserved.

// Install by fetching ICU source tarball
// This will only work for little endian systems, but will work for ancient ICU (back to v50)

const fs = require('fs')
const { URL } = require('url')
const process = require('process')
const myFetch = require('./myFetch')
const yauzl = require('yauzl')

// var isglobal = process.env.npm_config_global === 'true';

module.exports = async function installFromGithub (fullIcu, advice) {
const { icudat, icuend } = fullIcu

if (fs.existsSync(icudat)) {
console.log(` √ ${icudat} (exists)`)
return
}

if (icuend !== 'l') {
// Should not hit this, as versions 67 and prior are already in NPM
console.error('Warning: this method probably will fail, because the ICU source tarball only contains little endian data.')
}

// var cmdPath = nodePath = process.env.npm_node_execpath;

// var npmPath = process.env.npm_execpath;

// var args;
// https://github.com/unicode-org/icu/releases/download/release-51-3/icu4c-51_3-src.zip
const _baseUrl = process.env.FULL_ICU_BASEURL || 'https://github.com/unicode-org/icu/releases/'
const baseUrl = new URL(_baseUrl)
const versionsAsHyphen = fullIcu.icuver.replace(/\./g, '-')
const versionsAsUnderscore = fullIcu.icuver.replace(/\./g, '_')
const tag = `release-${versionsAsHyphen}`
const file = `icu4c-${versionsAsUnderscore}-src.zip`
const fullUrl = new URL(`./download/${tag}/${file}`, baseUrl)
console.log(fullUrl.toString())
const [srcZip, tmpd] = await myFetch(fullUrl)

console.log(srcZip, tmpd)
// now, unpack it

console.log(`Looking for ${icudat}`)
return new Promise((resolve, reject) =>
yauzl.open(srcZip, { lazyEntries: true }, (err, zipfile) => {
if (err) return reject(err)
zipfile.readEntry()
zipfile.on('end', () => reject(Error(`Not found in zipfile: ${icudat}`)))
zipfile.on('entry', (entry) => {
if (entry.fileName.endsWith('/')) {
zipfile.readEntry()
} else if (entry.fileName.endsWith('/' + icudat)) {
console.log('found ' + entry.fileName)
zipfile.openReadStream(entry, (err, readStream) => {
if (err) return reject(err)
// if entry.file
readStream.on('end', () => zipfile.readEntry())
const pipeOut = fs.createWriteStream(icudat)
readStream.pipe(pipeOut)
console.log(` √ ${icudat} (from ICU source tarball)`)
return resolve()
})
} else {
zipfile.readEntry() // get next
}
})
}))
}
58 changes: 58 additions & 0 deletions myFetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (C) 2015-2016 IBM Corporation and Others. All Rights Reserved.

const os = require('os')
const path = require('path')
const fs = require('fs')

function getFetcher (u) {
if (u.protocol === 'https:') return require('https')
if (u.protocol === 'http:') return require('http')
return null
}

/**
* @param {URL} fullUrl url to fetch
* @returns {Promse<String[]>} filename, tmpdir
*/
function myFetch (fullUrl) {
return new Promise((resolve, reject) => {
const fetcher = getFetcher(fullUrl)
console.log('Fetch:', fullUrl.toString())
if (!fetcher) {
return reject(Error(`Unknown URL protocol ${fullUrl.protocol} in ${fullUrl.toString()}`))
}

fetcher.get(fullUrl, res => {
const length = res.headers['content-length']
if (res.statusCode === 302 && res.headers.location) {
return resolve(myFetch(new URL(res.headers.location)))
} else if (res.statusCode !== 200) {
return reject(Error(`Bad status code ${res.statusCode}`))
}
const tmpd = fs.mkdtempSync(os.tmpdir())
const tmpf = path.join(tmpd, 'icu-download.zip')
let gotSoFar = 0
console.dir(tmpd)

res.on('data', data => {
gotSoFar += data.length
fs.appendFileSync(tmpf, data)
// console.dir(res.headers);
process.stdout.write(`${gotSoFar}/${length}\r`)
// console.log(`chunk: ${data.length}`);
})
res.on('end', () => {
resolve([tmpf, tmpd])
console.log(`${gotSoFar}/${length}\n`)
})
res.on('error', error => {
fs.unlinkSync(tmpf)
fs.rmdirSync(tmpd)
console.error(error)
return reject(error)
})
})
})
}

module.exports = myFetch
10 changes: 6 additions & 4 deletions node-full-icu.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env node
const { spawn } = require('child_process')
const data = require('./full-icu')
const env = data.icu_small ? {
...process.env,
NODE_ICU_DATA: data.datPath()
} : process.env
const env = data.icu_small
? {
...process.env,
NODE_ICU_DATA: data.datPath()
}
: process.env

spawn('/usr/bin/env', ['node', ...process.argv.slice(2)], { env, stdio: 'inherit' })
17 changes: 14 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
"version": "1.3.5-0",
"description": "install 'full-icu' data for your current node",
"scripts": {
"lint": "standard",
"postinstall": "node postinstall.js"
"lint": "standard && eslint *.js test/*.js",
"postinstall": "node postinstall.js",
"test": "tap test/*.js"
},
"keywords": [
"icu4c"
Expand All @@ -23,7 +24,17 @@
"bugs": {
"url": "https://github.com/unicode-org/full-icu-npm/issues"
},
"dependencies": {
"yauzl": "^2.10.0"
},
"devDependencies": {
"standard": "^16.0.3"
"eslint": "^7.7.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-header": "^3.0.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"standard": "^16.0.3",
"tap": "^15.0.10"
}
}
Loading

0 comments on commit a57877d

Please sign in to comment.