├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── asset.js ├── bin.js ├── download.js ├── error.js ├── help.txt ├── index.js ├── log.js ├── package.json ├── proxy.js ├── rc.js ├── test ├── .gitignore ├── asset-test.js ├── download-test.js ├── invalid.tar.gz ├── log-test.js ├── proxy-test.js ├── rc-test.js ├── releases.json ├── skip-test.js ├── util-test.js └── util │ └── clean-env.js └── util.js /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: ['*'] 5 | permissions: 6 | contents: write 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Create GitHub release 15 | uses: docker://antonyurchenko/git-release:v4 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push, pull_request] 3 | permissions: 4 | contents: read 5 | jobs: 6 | test: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest, windows-latest] 10 | node: [10, 12, 14, 16] 11 | arch: [x64] 12 | include: 13 | - os: windows-latest 14 | node: 14 15 | arch: x86 16 | runs-on: ${{ matrix.os }} 17 | name: ${{ matrix.os }} / Node ${{ matrix.node }} ${{ matrix.arch }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | - name: Use node ${{ matrix.node }} 22 | uses: actions/setup-node@v2 23 | with: 24 | node-version: ${{ matrix.node }} 25 | architecture: ${{ matrix.arch }} 26 | - name: Install 27 | run: npm install 28 | - name: Test 29 | run: npm test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | package-lock.json 4 | build 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test 4 | .github 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [7.1.3] - 2025-01-22 4 | 5 | ### Fixed 6 | 7 | - Bump napi-build-utils from 1 to 2 ([#204](https://github.com/prebuild/prebuild-install/issues/204)) ([`1bf4a15`](https://github.com/prebuild/prebuild-install/commit/1bf4a15)) (Bailey Pearson) 8 | 9 | ## [7.1.2] - 2024-02-29 10 | 11 | ### Fixed 12 | 13 | - Support environments where MD5 is prohibited ([#191](https://github.com/prebuild/prebuild-install/issues/191)) ([`9140468`](https://github.com/prebuild/prebuild-install/commit/9140468)) (Tomasz Szuba) 14 | 15 | ## [7.1.1] - 2022-06-07 16 | 17 | ### Fixed 18 | 19 | - Replace use of npmlog dependency with console.error ([#182](https://github.com/prebuild/prebuild-install/issues/182)) ([`4e2284c`](https://github.com/prebuild/prebuild-install/commit/4e2284c)) (Lovell Fuller) 20 | - Ensure script output can be captured by tests ([#181](https://github.com/prebuild/prebuild-install/issues/181)) ([`d1853cb`](https://github.com/prebuild/prebuild-install/commit/d1853cb)) (Lovell Fuller) 21 | 22 | ## [7.1.0] - 2022-04-20 23 | 24 | ### Changed 25 | 26 | - Allow setting libc to glibc on non-glibc platform ([#176](https://github.com/prebuild/prebuild-install/issues/176)) ([`f729abb`](https://github.com/prebuild/prebuild-install/commit/f729abb)) (Joona Heinikoski) 27 | 28 | ## [7.0.1] - 2022-01-28 29 | 30 | ### Changed 31 | 32 | - Upgrade to the latest version of `detect-libc` ([#166](https://github.com/prebuild/prebuild-install/issues/166)) ([`f71c6b9`](https://github.com/prebuild/prebuild-install/commit/f71c6b9)) (Lovell Fuller) 33 | 34 | ## [7.0.0] - 2021-11-12 35 | 36 | ### Changed 37 | 38 | - **Breaking:** bump `node-abi` so that Electron 14+ gets correct ABI ([#161](https://github.com/prebuild/prebuild-install/issues/161)) ([`477f347`](https://github.com/prebuild/prebuild-install/commit/477f347)) (csett86). Drops support of Node.js < 10. 39 | - Bump `simple-get` ([`7468c14`](https://github.com/prebuild/prebuild-install/commit/7468c14)) (Vincent Weevers). 40 | 41 | ## [6.1.4] - 2021-08-11 42 | 43 | ### Fixed 44 | 45 | - Move auth token to header instead of query param ([#160](https://github.com/prebuild/prebuild-install/issues/160)) ([`b3fad76`](https://github.com/prebuild/prebuild-install/commit/b3fad76)) (nicolai-nordic) 46 | - Remove `_` prefix as it isn't allowed by npm config ([#153](https://github.com/prebuild/prebuild-install/issues/153)) ([`a964e5b`](https://github.com/prebuild/prebuild-install/commit/a964e5b)) (Tom Boothman) 47 | - Make `rc.path` absolute ([#158](https://github.com/prebuild/prebuild-install/issues/158)) ([`57bcc06`](https://github.com/prebuild/prebuild-install/commit/57bcc06)) (George Waters). 48 | 49 | ## [6.1.3] - 2021-06-03 50 | 51 | ### Changed 52 | 53 | - Inline no longer maintained `noop-logger` ([#155](https://github.com/prebuild/prebuild-install/issues/155)) ([`e08d75a`](https://github.com/prebuild/prebuild-install/commit/e08d75a)) (Alexandru Dima) 54 | - Point users towards `prebuildify` in README ([#150](https://github.com/prebuild/prebuild-install/issues/150)) ([`5ee1a2f`](https://github.com/prebuild/prebuild-install/commit/5ee1a2f)) (Vincent Weevers) 55 | 56 | ## [6.1.2] - 2021-04-24 57 | 58 | ### Fixed 59 | 60 | - Support URL-safe strings in scoped packages ([#148](https://github.com/prebuild/prebuild-install/issues/148)) ([`db36c7a`](https://github.com/prebuild/prebuild-install/commit/db36c7a)) (Marco) 61 | 62 | ## [6.1.1] - 2021-04-04 63 | 64 | ### Fixed 65 | 66 | - Support `force` & `buildFromSource` options in yarn ([#140](https://github.com/prebuild/prebuild-install/issues/140)) ([`8cb1ced`](https://github.com/prebuild/prebuild-install/commit/8cb1ced)) (João Moreno) 67 | - Bump `node-abi` to prevent dedupe (closes [#135](https://github.com/prebuild/prebuild-install/issues/135)) ([`2950fb2`](https://github.com/prebuild/prebuild-install/commit/2950fb2)) (Vincent Weevers) 68 | 69 | ## [6.1.0] - 2021-04-03 70 | 71 | ### Added 72 | 73 | - Restore local prebuilds feature ([#137](https://github.com/prebuild/prebuild-install/issues/137)) ([`dc4e5ea`](https://github.com/prebuild/prebuild-install/commit/dc4e5ea)) (Wes Roberts). Previously removed in [#81](https://github.com/prebuild/prebuild-install/issues/81) / [`a069253`](https://github.com/prebuild/prebuild-install/commit/a06925378d38ca821bfa93aa4c1fdedc253b2420). 74 | 75 | ## [6.0.1] - 2021-02-14 76 | 77 | ### Fixed 78 | 79 | - Fixes empty `--tag-prefix` ([#143](https://github.com/prebuild/prebuild-install/issues/143)) ([**@mathiask88**](https://github.com/mathiask88)) 80 | 81 | ## [6.0.0] - 2020-10-23 82 | 83 | ### Changed 84 | 85 | - **Breaking:** don't skip downloads in standalone mode ([`b6f3b36`](https://github.com/prebuild/prebuild-install/commit/b6f3b36)) ([**@vweevers**](https://github.com/vweevers)) 86 | 87 | ### Added 88 | 89 | - Document cross platform options ([`e5c9a5a`](https://github.com/prebuild/prebuild-install/commit/e5c9a5a)) ([**@fishbone1**](https://github.com/fishbone1)) 90 | 91 | ### Removed 92 | 93 | - **Breaking:** remove `--compile` and `--prebuild` options ([`94f2492`](https://github.com/prebuild/prebuild-install/commit/94f2492)) ([**@vweevers**](https://github.com/vweevers)) 94 | 95 | ### Fixed 96 | 97 | - Support npm 7 ([`8acccac`](https://github.com/prebuild/prebuild-install/commit/8acccac), [`08eaf6d`](https://github.com/prebuild/prebuild-install/commit/08eaf6d), [`22175b8`](https://github.com/prebuild/prebuild-install/commit/22175b8)) ([**@vweevers**](https://github.com/vweevers)) 98 | 99 | ## [5.3.6] - 2020-10-20 100 | 101 | ### Changed 102 | 103 | - Replace `mkdirp` dependency with `mkdirp-classic` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 104 | 105 | [7.1.3]: https://github.com/prebuild/prebuild-install/releases/tag/v7.1.3 106 | 107 | [7.1.2]: https://github.com/prebuild/prebuild-install/releases/tag/v7.1.2 108 | 109 | [7.1.1]: https://github.com/prebuild/prebuild-install/releases/tag/v7.1.1 110 | 111 | [7.1.0]: https://github.com/prebuild/prebuild-install/releases/tag/v7.1.0 112 | 113 | [7.0.1]: https://github.com/prebuild/prebuild-install/releases/tag/v7.0.1 114 | 115 | [7.0.0]: https://github.com/prebuild/prebuild-install/releases/tag/v7.0.0 116 | 117 | [6.1.4]: https://github.com/prebuild/prebuild-install/releases/tag/v6.1.4 118 | 119 | [6.1.3]: https://github.com/prebuild/prebuild-install/releases/tag/v6.1.3 120 | 121 | [6.1.2]: https://github.com/prebuild/prebuild-install/releases/tag/v6.1.2 122 | 123 | [6.1.1]: https://github.com/prebuild/prebuild-install/releases/tag/v6.1.1 124 | 125 | [6.1.0]: https://github.com/prebuild/prebuild-install/releases/tag/v6.1.0 126 | 127 | [6.0.1]: https://github.com/prebuild/prebuild-install/releases/tag/v6.0.1 128 | 129 | [6.0.0]: https://github.com/prebuild/prebuild-install/releases/tag/v6.0.0 130 | 131 | [5.3.6]: https://github.com/prebuild/prebuild-install/releases/tag/v5.3.6 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to prebuild 2 | 3 | - no commits direct to master 4 | - all commits as pull requests (one or several per PR) 5 | - each commit solves one identifiable problem 6 | - never merge one's own PRs, another contributor does this 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mathias Buus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prebuild-install 2 | 3 | > **A command line tool to easily install prebuilt binaries for multiple versions of Node.js & Electron on a specific platform.** 4 | > By default it downloads prebuilt binaries from a GitHub release. 5 | 6 | [![npm](https://img.shields.io/npm/v/prebuild-install.svg)](https://www.npmjs.com/package/prebuild-install) 7 | ![Node version](https://img.shields.io/node/v/prebuild-install.svg) 8 | [![Test](https://img.shields.io/github/actions/workflow/status/prebuild/prebuild-install/test.yml?label=test)](https://github.com/prebuild/prebuild-install/actions/workflows/test.yml) 9 | [![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) 10 | [![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) 11 | 12 | ## Note 13 | 14 | **Instead of [`prebuild`](https://github.com/prebuild/prebuild) paired with [`prebuild-install`](https://github.com/prebuild/prebuild-install), we recommend [`prebuildify`](https://github.com/prebuild/prebuildify) paired with [`node-gyp-build`](https://github.com/prebuild/node-gyp-build).** 15 | 16 | With `prebuildify`, all prebuilt binaries are shipped inside the package that is published to npm, which means there's no need for a separate download step like you find in `prebuild`. The irony of this approach is that it is faster to download all prebuilt binaries for every platform when they are bundled than it is to download a single prebuilt binary as an install script. 17 | 18 | Upsides: 19 | 20 | 1. No extra download step, making it more reliable and faster to install. 21 | 2. Supports changing runtime versions locally and using the same install between Node.js and Electron. Reinstalling or rebuilding is not necessary, as all prebuilt binaries are in the npm tarball and the correct one is simply picked on runtime. 22 | 3. The `node-gyp-build` runtime dependency is dependency-free and will remain so out of principle, because introducing dependencies would negate the shorter install time. 23 | 4. Prebuilt binaries work even if npm install scripts are disabled. 24 | 5. The npm package checksum covers prebuilt binaries too. 25 | 26 | Downsides: 27 | 28 | 1. The installed npm package is larger on disk. Using [Node-API](https://nodejs.org/api/n-api.html) alleviates this because Node-API binaries are runtime-agnostic and forward-compatible. 29 | 2. Publishing is mildly more complicated, because `npm publish` must be done after compiling and fetching prebuilt binaries (typically in CI). 30 | 31 | ## Usage 32 | 33 | Use [`prebuild`](https://github.com/prebuild/prebuild) to create and upload prebuilt binaries. Then change your package.json install script to: 34 | 35 | ```json 36 | { 37 | "scripts": { 38 | "install": "prebuild-install || node-gyp rebuild" 39 | } 40 | } 41 | ``` 42 | 43 | When a consumer then installs your package with npm thus triggering the above install script, `prebuild-install` will download a suitable prebuilt binary, or exit with a non-zero exit code if there is none, which triggers `node-gyp rebuild` in order to build from source. 44 | 45 | Options (see below) can be passed to `prebuild-install` like so: 46 | 47 | ```json 48 | { 49 | "scripts": { 50 | "install": "prebuild-install -r napi || node-gyp rebuild" 51 | } 52 | } 53 | ``` 54 | 55 | ### Help 56 | 57 | ``` 58 | prebuild-install [options] 59 | 60 | --download -d [url] (download prebuilds, no url means github) 61 | --target -t version (version to install for) 62 | --runtime -r runtime (Node runtime [node, napi or electron] to build or install for, default is node) 63 | --path -p path (make a prebuild-install here) 64 | --token -T gh-token (github token for private repos) 65 | --arch arch (target CPU architecture, see Node OS module docs, default is current arch) 66 | --platform platform (target platform, see Node OS module docs, default is current platform) 67 | --tag-prefix (github tag prefix, default is "v") 68 | --build-from-source (skip prebuild download) 69 | --verbose (log verbosely) 70 | --libc (use provided libc rather than system default) 71 | --debug (set Debug or Release configuration) 72 | --version (print prebuild-install version and exit) 73 | ``` 74 | 75 | When `prebuild-install` is run via an `npm` script, options `--build-from-source`, `--debug`, `--download`, `--target`, `--runtime`, `--arch` `--platform` and `--libc` may be passed through via arguments given to the `npm` command. 76 | 77 | Alternatively you can set environment variables `npm_config_build_from_source=true`, `npm_config_platform`, `npm_config_arch`, `npm_config_target` `npm_config_runtime` and `npm_config_libc`. 78 | 79 | ### Libc 80 | 81 | On non-glibc Linux platforms, the Libc name is appended to platform name. For example, musl-based environments are called `linuxmusl`. If `--libc=glibc` is passed as option, glibc is discarded and platform is called as just `linux`. This can be used for example to build cross-platform packages on Alpine Linux. 82 | 83 | ### Private Repositories 84 | 85 | `prebuild-install` supports downloading prebuilds from private GitHub repositories using the `-T `: 86 | 87 | ``` 88 | $ prebuild-install -T 89 | ``` 90 | 91 | If you don't want to use the token on cli you can put it in `~/.prebuild-installrc`: 92 | 93 | ``` 94 | token= 95 | ``` 96 | 97 | Alternatively you can specify it in the `prebuild-install_token` environment variable. 98 | 99 | Note that using a GitHub token uses the API to resolve the correct release meaning that you are subject to the ([GitHub Rate Limit](https://developer.github.com/v3/rate_limit/)). 100 | 101 | ### Create GitHub Token 102 | 103 | To create a token: 104 | 105 | - Go to [this page](https://github.com/settings/tokens) 106 | - Click the `Generate new token` button 107 | - Give the token a name and click the `Generate token` button, see below 108 | 109 | ![prebuild-token](https://cloud.githubusercontent.com/assets/13285808/20844584/d0b85268-b8c0-11e6-8b08-2b19522165a9.png) 110 | 111 | The default scopes should be fine. 112 | 113 | ### Custom binaries 114 | 115 | The end user can override binary download location through environment variables in their .npmrc file. 116 | The variable needs to meet the mask `% your package name %_binary_host` or `% your package name %_binary_host_mirror`. For example: 117 | 118 | ``` 119 | leveldown_binary_host=http://overriden-host.com/overriden-path 120 | ``` 121 | 122 | Note that the package version subpath and file name will still be appended. 123 | So if you are installing `leveldown@1.2.3` the resulting url will be: 124 | 125 | ``` 126 | http://overriden-host.com/overriden-path/v1.2.3/leveldown-v1.2.3-node-v57-win32-x64.tar.gz 127 | ``` 128 | 129 | #### Local prebuilds 130 | 131 | If you want to use prebuilds from your local filesystem, you can use the `% your package name %_local_prebuilds` .npmrc variable to set a path to the folder containing prebuilds. For example: 132 | 133 | ``` 134 | leveldown_local_prebuilds=/path/to/prebuilds 135 | ``` 136 | 137 | This option will look directly in that folder for bundles created with `prebuild`, for example: 138 | 139 | ``` 140 | /path/to/prebuilds/leveldown-v1.2.3-node-v57-win32-x64.tar.gz 141 | ``` 142 | 143 | Non-absolute paths resolve relative to the directory of the package invoking prebuild-install, e.g. for nested dependencies. 144 | 145 | ### Cache 146 | 147 | All prebuilt binaries are cached to minimize traffic. So first `prebuild-install` picks binaries from the cache and if no binary could be found, it will be downloaded. Depending on the environment, the cache folder is determined in the following order: 148 | 149 | - `${npm_config_cache}/_prebuilds` 150 | - `${APP_DATA}/npm-cache/_prebuilds` 151 | - `${HOME}/.npm/_prebuilds` 152 | 153 | ## Install 154 | 155 | With [npm](https://npmjs.org) do: 156 | 157 | ``` 158 | npm install prebuild-install 159 | ``` 160 | 161 | ## License 162 | 163 | [MIT](./LICENSE) 164 | -------------------------------------------------------------------------------- /asset.js: -------------------------------------------------------------------------------- 1 | const get = require('simple-get') 2 | const util = require('./util') 3 | const proxy = require('./proxy') 4 | 5 | function findAssetId (opts, cb) { 6 | const downloadUrl = util.getDownloadUrl(opts) 7 | const apiUrl = util.getApiUrl(opts) 8 | const log = opts.log || util.noopLogger 9 | 10 | log.http('request', 'GET ' + apiUrl) 11 | const reqOpts = proxy({ 12 | url: apiUrl, 13 | json: true, 14 | headers: { 15 | 'User-Agent': 'simple-get', 16 | Authorization: 'token ' + opts.token 17 | } 18 | }, opts) 19 | 20 | const req = get.concat(reqOpts, function (err, res, data) { 21 | if (err) return cb(err) 22 | log.http(res.statusCode, apiUrl) 23 | if (res.statusCode !== 200) return cb(err) 24 | 25 | // Find asset id in release 26 | for (const release of data) { 27 | if (release.tag_name === opts['tag-prefix'] + opts.pkg.version) { 28 | for (const asset of release.assets) { 29 | if (asset.browser_download_url === downloadUrl) { 30 | return cb(null, asset.id) 31 | } 32 | } 33 | } 34 | } 35 | 36 | cb(new Error('Could not find GitHub release for version')) 37 | }) 38 | 39 | req.setTimeout(30 * 1000, function () { 40 | req.abort() 41 | }) 42 | } 43 | 44 | module.exports = findAssetId 45 | -------------------------------------------------------------------------------- /bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path') 4 | const fs = require('fs') 5 | const napi = require('napi-build-utils') 6 | 7 | const pkg = require(path.resolve('package.json')) 8 | const rc = require('./rc')(pkg) 9 | const log = require('./log')(rc, process.env) 10 | const download = require('./download') 11 | const asset = require('./asset') 12 | const util = require('./util') 13 | 14 | const prebuildClientVersion = require('./package.json').version 15 | if (rc.version) { 16 | console.log(prebuildClientVersion) 17 | process.exit(0) 18 | } 19 | 20 | if (rc.path) process.chdir(rc.path) 21 | 22 | if (rc.runtime === 'electron' && rc.target[0] === '4' && rc.abi === '64') { 23 | log.error(`Electron version ${rc.target} found - skipping prebuild-install work due to known ABI issue`) 24 | log.error('More information about this issue can be found at https://github.com/lgeiger/node-abi/issues/54') 25 | process.exit(1) 26 | } 27 | 28 | if (!fs.existsSync('package.json')) { 29 | log.error('setup', 'No package.json found. Aborting...') 30 | process.exit(1) 31 | } 32 | 33 | if (rc.help) { 34 | console.error(fs.readFileSync(path.join(__dirname, 'help.txt'), 'utf-8')) 35 | process.exit(0) 36 | } 37 | 38 | log.info('begin', 'Prebuild-install version', prebuildClientVersion) 39 | 40 | const opts = Object.assign({}, rc, { pkg: pkg, log: log }) 41 | 42 | if (napi.isNapiRuntime(rc.runtime)) napi.logUnsupportedVersion(rc.target, log) 43 | 44 | const origin = util.packageOrigin(process.env, pkg) 45 | 46 | if (opts.force) { 47 | log.warn('install', 'prebuilt binaries enforced with --force!') 48 | log.warn('install', 'prebuilt binaries may be out of date!') 49 | } else if (origin && origin.length > 4 && origin.substr(0, 4) === 'git+') { 50 | log.info('install', 'installing from git repository, skipping download.') 51 | process.exit(1) 52 | } else if (opts.buildFromSource) { 53 | log.info('install', '--build-from-source specified, not attempting download.') 54 | process.exit(1) 55 | } 56 | 57 | const startDownload = function (downloadUrl) { 58 | download(downloadUrl, opts, function (err) { 59 | if (err) { 60 | log.warn('install', err.message) 61 | return process.exit(1) 62 | } 63 | log.info('install', 'Successfully installed prebuilt binary!') 64 | }) 65 | } 66 | 67 | if (opts.token) { 68 | asset(opts, function (err, assetId) { 69 | if (err) { 70 | log.warn('install', err.message) 71 | return process.exit(1) 72 | } 73 | 74 | startDownload(util.getAssetUrl(opts, assetId)) 75 | }) 76 | } else { 77 | startDownload(util.getDownloadUrl(opts)) 78 | } 79 | -------------------------------------------------------------------------------- /download.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const get = require('simple-get') 4 | const pump = require('pump') 5 | const tfs = require('tar-fs') 6 | const zlib = require('zlib') 7 | const util = require('./util') 8 | const error = require('./error') 9 | const proxy = require('./proxy') 10 | const mkdirp = require('mkdirp-classic') 11 | 12 | function downloadPrebuild (downloadUrl, opts, cb) { 13 | let cachedPrebuild = util.cachedPrebuild(downloadUrl) 14 | const localPrebuild = util.localPrebuild(downloadUrl, opts) 15 | const tempFile = util.tempFile(cachedPrebuild) 16 | const log = opts.log || util.noopLogger 17 | 18 | if (opts.nolocal) return download() 19 | 20 | log.info('looking for local prebuild @', localPrebuild) 21 | fs.access(localPrebuild, fs.R_OK | fs.W_OK, function (err) { 22 | if (err && err.code === 'ENOENT') { 23 | return download() 24 | } 25 | 26 | log.info('found local prebuild') 27 | cachedPrebuild = localPrebuild 28 | unpack() 29 | }) 30 | 31 | function download () { 32 | ensureNpmCacheDir(function (err) { 33 | if (err) return onerror(err) 34 | 35 | log.info('looking for cached prebuild @', cachedPrebuild) 36 | fs.access(cachedPrebuild, fs.R_OK | fs.W_OK, function (err) { 37 | if (!(err && err.code === 'ENOENT')) { 38 | log.info('found cached prebuild') 39 | return unpack() 40 | } 41 | 42 | log.http('request', 'GET ' + downloadUrl) 43 | const reqOpts = proxy({ url: downloadUrl }, opts) 44 | 45 | if (opts.token) { 46 | reqOpts.headers = { 47 | 'User-Agent': 'simple-get', 48 | Accept: 'application/octet-stream', 49 | Authorization: 'token ' + opts.token 50 | } 51 | } 52 | 53 | const req = get(reqOpts, function (err, res) { 54 | if (err) return onerror(err) 55 | log.http(res.statusCode, downloadUrl) 56 | if (res.statusCode !== 200) return onerror() 57 | mkdirp(util.prebuildCache(), function () { 58 | log.info('downloading to @', tempFile) 59 | pump(res, fs.createWriteStream(tempFile), function (err) { 60 | if (err) return onerror(err) 61 | fs.rename(tempFile, cachedPrebuild, function (err) { 62 | if (err) return cb(err) 63 | log.info('renaming to @', cachedPrebuild) 64 | unpack() 65 | }) 66 | }) 67 | }) 68 | }) 69 | 70 | req.setTimeout(30 * 1000, function () { 71 | req.abort() 72 | }) 73 | }) 74 | 75 | function onerror (err) { 76 | fs.unlink(tempFile, function () { 77 | cb(err || error.noPrebuilts(opts)) 78 | }) 79 | } 80 | }) 81 | } 82 | 83 | function unpack () { 84 | let binaryName 85 | 86 | const updateName = opts.updateName || function (entry) { 87 | if (/\.node$/i.test(entry.name)) binaryName = entry.name 88 | } 89 | 90 | log.info('unpacking @', cachedPrebuild) 91 | 92 | const options = { 93 | readable: true, 94 | writable: true, 95 | hardlinkAsFilesFallback: true 96 | } 97 | const extract = tfs.extract(opts.path, options).on('entry', updateName) 98 | 99 | pump(fs.createReadStream(cachedPrebuild), zlib.createGunzip(), extract, 100 | function (err) { 101 | if (err) return cb(err) 102 | 103 | let resolved 104 | if (binaryName) { 105 | try { 106 | resolved = path.resolve(opts.path || '.', binaryName) 107 | } catch (err) { 108 | return cb(err) 109 | } 110 | log.info('unpack', 'resolved to ' + resolved) 111 | 112 | if (opts.runtime === 'node' && opts.platform === process.platform && opts.abi === process.versions.modules && opts.arch === process.arch) { 113 | try { 114 | require(resolved) 115 | } catch (err) { 116 | return cb(err) 117 | } 118 | log.info('unpack', 'required ' + resolved + ' successfully') 119 | } 120 | } 121 | 122 | cb(null, resolved) 123 | }) 124 | } 125 | 126 | function ensureNpmCacheDir (cb) { 127 | const cacheFolder = util.npmCache() 128 | fs.access(cacheFolder, fs.R_OK | fs.W_OK, function (err) { 129 | if (err && err.code === 'ENOENT') { 130 | return makeNpmCacheDir() 131 | } 132 | cb(err) 133 | }) 134 | 135 | function makeNpmCacheDir () { 136 | log.info('npm cache directory missing, creating it...') 137 | mkdirp(cacheFolder, cb) 138 | } 139 | } 140 | } 141 | 142 | module.exports = downloadPrebuild 143 | -------------------------------------------------------------------------------- /error.js: -------------------------------------------------------------------------------- 1 | exports.noPrebuilts = function (opts) { 2 | return new Error([ 3 | 'No prebuilt binaries found', 4 | '(target=' + opts.target, 5 | 'runtime=' + opts.runtime, 6 | 'arch=' + opts.arch, 7 | 'libc=' + opts.libc, 8 | 'platform=' + opts.platform + ')' 9 | ].join(' ')) 10 | } 11 | 12 | exports.invalidArchive = function () { 13 | return new Error('Missing .node file in archive') 14 | } 15 | -------------------------------------------------------------------------------- /help.txt: -------------------------------------------------------------------------------- 1 | prebuild-install [options] 2 | 3 | --download -d [url] (download prebuilds, no url means github) 4 | --target -t version (version to install for) 5 | --runtime -r runtime (Node runtime [node or electron] to build or install for, default is node) 6 | --path -p path (make a prebuild-install here) 7 | --token -T gh-token (github token for private repos) 8 | --arch arch (target CPU architecture, see Node OS module docs, default is current arch) 9 | --platform platform (target platform, see Node OS module docs, default is current platform) 10 | --tag-prefix (github tag prefix, default is "v") 11 | --force (always use prebuilt binaries when available) 12 | --build-from-source (skip prebuild download) 13 | --verbose (log verbosely) 14 | --libc (use provided libc rather than system default) 15 | --debug (set Debug or Release configuration) 16 | --version (print prebuild-install version and exit) 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | exports.download = require('./download') 2 | -------------------------------------------------------------------------------- /log.js: -------------------------------------------------------------------------------- 1 | const levels = { 2 | silent: 0, 3 | error: 1, 4 | warn: 2, 5 | notice: 3, 6 | http: 4, 7 | timing: 5, 8 | info: 6, 9 | verbose: 7, 10 | silly: 8 11 | } 12 | 13 | module.exports = function (rc, env) { 14 | const level = rc.verbose 15 | ? 'verbose' 16 | : env.npm_config_loglevel || 'notice' 17 | 18 | const logAtLevel = function (messageLevel) { 19 | return function (...args) { 20 | if (levels[messageLevel] <= levels[level]) { 21 | console.error(`prebuild-install ${messageLevel} ${args.join(' ')}`) 22 | } 23 | } 24 | } 25 | 26 | return { 27 | error: logAtLevel('error'), 28 | warn: logAtLevel('warn'), 29 | http: logAtLevel('http'), 30 | info: logAtLevel('info'), 31 | level 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prebuild-install", 3 | "version": "7.1.3", 4 | "description": "A command line tool to easily install prebuilt binaries for multiple version of node/iojs on a specific platform", 5 | "scripts": { 6 | "test": "standard && hallmark && tape test/*-test.js", 7 | "hallmark": "hallmark --fix" 8 | }, 9 | "keywords": [ 10 | "prebuilt", 11 | "binaries", 12 | "native", 13 | "addon", 14 | "module", 15 | "c", 16 | "c++", 17 | "bindings", 18 | "devops", 19 | "napi" 20 | ], 21 | "dependencies": { 22 | "detect-libc": "^2.0.0", 23 | "expand-template": "^2.0.3", 24 | "github-from-package": "0.0.0", 25 | "minimist": "^1.2.3", 26 | "mkdirp-classic": "^0.5.3", 27 | "napi-build-utils": "^2.0.0", 28 | "node-abi": "^3.3.0", 29 | "pump": "^3.0.0", 30 | "rc": "^1.2.7", 31 | "simple-get": "^4.0.0", 32 | "tar-fs": "^2.0.0", 33 | "tunnel-agent": "^0.6.0" 34 | }, 35 | "devDependencies": { 36 | "a-native-module": "^1.0.0", 37 | "hallmark": "^4.0.0", 38 | "nock": "^10.0.6", 39 | "rimraf": "^2.5.2", 40 | "standard": "^16.0.4", 41 | "tape": "^5.3.1", 42 | "tempy": "0.2.1" 43 | }, 44 | "bin": "./bin.js", 45 | "repository": { 46 | "type": "git", 47 | "url": "https://github.com/prebuild/prebuild-install.git" 48 | }, 49 | "author": "Mathias Buus (@mafintosh)", 50 | "contributors": [ 51 | "Julian Gruber (https://github.com/juliangruber)", 52 | "Brett Lawson (https://github.com/brett19)", 53 | "Pieter Hintjens (https://github.com/hintjens)", 54 | "Lars-Magnus Skog (https://github.com/ralphtheninja)", 55 | "Jesús Leganés Combarro (https://github.com/piranna)", 56 | "Mathias Küsel (https://github.com/mathiask88)", 57 | "Lukas Geiger (https://github.com/lgeiger)" 58 | ], 59 | "license": "MIT", 60 | "bugs": { 61 | "url": "https://github.com/prebuild/prebuild-install/issues" 62 | }, 63 | "homepage": "https://github.com/prebuild/prebuild-install", 64 | "engines": { 65 | "node": ">=10" 66 | } 67 | } -------------------------------------------------------------------------------- /proxy.js: -------------------------------------------------------------------------------- 1 | const url = require('url') 2 | const tunnel = require('tunnel-agent') 3 | const util = require('./util') 4 | 5 | function applyProxy (reqOpts, opts) { 6 | const log = opts.log || util.noopLogger 7 | 8 | const proxy = opts['https-proxy'] || opts.proxy 9 | 10 | if (proxy) { 11 | // eslint-disable-next-line node/no-deprecated-api 12 | const parsedDownloadUrl = url.parse(reqOpts.url) 13 | // eslint-disable-next-line node/no-deprecated-api 14 | const parsedProxy = url.parse(proxy) 15 | const uriProtocol = (parsedDownloadUrl.protocol === 'https:' ? 'https' : 'http') 16 | const proxyProtocol = (parsedProxy.protocol === 'https:' ? 'Https' : 'Http') 17 | const tunnelFnName = [uriProtocol, proxyProtocol].join('Over') 18 | reqOpts.agent = tunnel[tunnelFnName]({ 19 | proxy: { 20 | host: parsedProxy.hostname, 21 | port: +parsedProxy.port, 22 | proxyAuth: parsedProxy.auth 23 | } 24 | }) 25 | log.http('request', 'Proxy setup detected (Host: ' + 26 | parsedProxy.hostname + ', Port: ' + 27 | parsedProxy.port + ', Authentication: ' + 28 | (parsedProxy.auth ? 'Yes' : 'No') + ')' + 29 | ' Tunneling with ' + tunnelFnName) 30 | } 31 | 32 | return reqOpts 33 | } 34 | 35 | module.exports = applyProxy 36 | -------------------------------------------------------------------------------- /rc.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const minimist = require('minimist') 3 | const getAbi = require('node-abi').getAbi 4 | const detectLibc = require('detect-libc') 5 | const napi = require('napi-build-utils') 6 | 7 | const env = process.env 8 | 9 | const libc = env.LIBC || process.env.npm_config_libc || 10 | (detectLibc.isNonGlibcLinuxSync() && detectLibc.familySync()) || '' 11 | 12 | // Get the configuration 13 | module.exports = function (pkg) { 14 | const pkgConf = pkg.config || {} 15 | const buildFromSource = env.npm_config_build_from_source 16 | 17 | const rc = require('rc')('prebuild-install', { 18 | target: pkgConf.target || env.npm_config_target || process.versions.node, 19 | runtime: pkgConf.runtime || env.npm_config_runtime || 'node', 20 | arch: pkgConf.arch || env.npm_config_arch || process.arch, 21 | libc: libc, 22 | platform: env.npm_config_platform || process.platform, 23 | debug: env.npm_config_debug === 'true', 24 | force: false, 25 | verbose: env.npm_config_verbose === 'true', 26 | buildFromSource: buildFromSource === pkg.name || buildFromSource === 'true', 27 | path: '.', 28 | proxy: env.npm_config_proxy || env.http_proxy || env.HTTP_PROXY, 29 | 'https-proxy': env.npm_config_https_proxy || env.https_proxy || env.HTTPS_PROXY, 30 | 'local-address': env.npm_config_local_address, 31 | 'local-prebuilds': 'prebuilds', 32 | 'tag-prefix': 'v', 33 | download: env.npm_config_download 34 | }, minimist(process.argv, { 35 | alias: { 36 | target: 't', 37 | runtime: 'r', 38 | help: 'h', 39 | arch: 'a', 40 | path: 'p', 41 | version: 'v', 42 | download: 'd', 43 | buildFromSource: 'build-from-source', 44 | token: 'T' 45 | } 46 | })) 47 | 48 | rc.path = path.resolve(rc.path === true ? '.' : rc.path || '.') 49 | 50 | if (napi.isNapiRuntime(rc.runtime) && rc.target === process.versions.node) { 51 | rc.target = napi.getBestNapiBuildVersion() 52 | } 53 | 54 | rc.abi = napi.isNapiRuntime(rc.runtime) ? rc.target : getAbi(rc.target, rc.runtime) 55 | 56 | rc.libc = rc.platform !== 'linux' || rc.libc === detectLibc.GLIBC ? '' : rc.libc 57 | 58 | return rc 59 | } 60 | 61 | // Print the configuration values when executed standalone for testing purposses 62 | if (!module.parent) { 63 | console.log(JSON.stringify(module.exports({}), null, 2)) 64 | } 65 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | prebuilds/ 3 | invalid.node 4 | -------------------------------------------------------------------------------- /test/asset-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const fs = require('fs') 3 | const rm = require('rimraf') 4 | const path = require('path') 5 | const https = require('https') 6 | const download = require('../download') 7 | const util = require('../util') 8 | const asset = require('../asset') 9 | const nock = require('nock') 10 | const releases = require('./releases.json') 11 | 12 | const build = path.join(__dirname, 'build') 13 | const unpacked = path.join(build, 'Release/leveldown.node') 14 | 15 | // Release assets call 16 | nock('https://api.github.com:443', { 17 | encodedQueryParams: true, 18 | reqheaders: { 19 | 'User-Agent': 'simple-get', 20 | Authorization: 'token TOKEN' 21 | } 22 | }) 23 | .persist() 24 | .get('/repos/ralphtheninja/a-native-module/releases') 25 | .reply(200, releases) 26 | 27 | // Binary download 28 | nock('https://api.github.com:443', { 29 | encodedQueryParams: true, 30 | reqheaders: { 31 | 'User-Agent': 'simple-get' 32 | } 33 | }) 34 | .persist() 35 | .get(function (uri) { 36 | return /\/repos\/ralphtheninja\/a-native-module\/releases\/assets\/\d*/g.test(uri) 37 | }) 38 | .reply(302, undefined, { 39 | Location: function (req, res, body) { 40 | const assetId = req.path 41 | .replace('/repos/ralphtheninja/a-native-module/releases/assets/', '') 42 | 43 | for (const release of releases) { 44 | for (const asset of release.assets) { 45 | if (asset.id.toString() === assetId) { 46 | return asset.browser_download_url 47 | } 48 | } 49 | } 50 | } 51 | }) 52 | 53 | test('downloading from GitHub with token', function (t) { 54 | t.plan(11) 55 | rm.sync(build) 56 | rm.sync(util.prebuildCache()) 57 | 58 | const opts = getOpts() 59 | asset(opts, function (err, assetId) { 60 | t.error(err, 'no error') 61 | 62 | const downloadUrl = util.getAssetUrl(opts, assetId) 63 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 64 | let tempFile 65 | 66 | let writeStreamCount = 0 67 | const _createWriteStream = fs.createWriteStream 68 | fs.createWriteStream = function (path) { 69 | if (writeStreamCount++ === 0) { 70 | tempFile = path 71 | t.ok(/\.tmp$/i.test(path), 'this is the temporary file') 72 | } else { 73 | t.ok(/\.node$/i.test(path), 'this is the unpacked file') 74 | } 75 | return _createWriteStream(path) 76 | } 77 | 78 | const _createReadStream = fs.createReadStream 79 | fs.createReadStream = function (path) { 80 | t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild') 81 | return _createReadStream(path) 82 | } 83 | 84 | const _request = https.request 85 | https.request = function (req) { 86 | https.request = _request 87 | t.equal('https://' + req.hostname + req.path, downloadUrl, 'correct url') 88 | return _request.apply(https, arguments) 89 | } 90 | 91 | t.equal(fs.existsSync(build), false, 'no build folder') 92 | 93 | download(downloadUrl, opts, function (err) { 94 | t.error(err, 'no error') 95 | t.equal(fs.existsSync(util.prebuildCache()), true, 'prebuildCache created') 96 | t.equal(fs.existsSync(cachedPrebuild), true, 'prebuild was cached') 97 | t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist') 98 | t.equal(fs.existsSync(tempFile), false, 'temp file should be gone') 99 | fs.createWriteStream = _createWriteStream 100 | fs.createReadStream = _createReadStream 101 | }) 102 | }) 103 | }) 104 | 105 | test('non existing version should fail asset request', function (t) { 106 | t.plan(3) 107 | rm.sync(build) 108 | rm.sync(util.prebuildCache()) 109 | 110 | const opts = getOpts() 111 | opts.pkg = Object.assign({}, opts.pkg, { version: '0' }) 112 | asset(opts, function (err, assetId) { 113 | t.ok(err, 'should error') 114 | t.equal(assetId, undefined) 115 | 116 | const downloadUrl = util.getAssetUrl(opts, assetId) 117 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 118 | 119 | t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached') 120 | }) 121 | }) 122 | 123 | function getOpts () { 124 | return { 125 | pkg: require('a-native-module/package'), 126 | runtime: 'node', 127 | abi: 64, 128 | platform: process.platform, 129 | arch: process.arch, 130 | path: __dirname, 131 | token: 'TOKEN', 132 | 'tag-prefix': 'v' 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /test/download-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const fs = require('fs') 3 | const rm = require('rimraf') 4 | const path = require('path') 5 | const http = require('http') 6 | const https = require('https') 7 | const download = require('../download') 8 | const util = require('../util') 9 | const error = require('../error') 10 | 11 | const build = path.join(__dirname, 'build') 12 | const unpacked = path.join(build, 'Release/leveldown.node') 13 | 14 | test('downloading from GitHub, not cached', function (t) { 15 | t.plan(10) 16 | rm.sync(build) 17 | rm.sync(util.prebuildCache()) 18 | 19 | const opts = getOpts() 20 | const downloadUrl = util.getDownloadUrl(opts) 21 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 22 | let tempFile 23 | 24 | let writeStreamCount = 0 25 | const _createWriteStream = fs.createWriteStream 26 | fs.createWriteStream = function (path) { 27 | if (writeStreamCount++ === 0) { 28 | tempFile = path 29 | t.ok(/\.tmp$/i.test(path), 'this is the temporary file') 30 | } else { 31 | t.ok(/\.node$/i.test(path), 'this is the unpacked file') 32 | } 33 | return _createWriteStream(path) 34 | } 35 | 36 | const _createReadStream = fs.createReadStream 37 | fs.createReadStream = function (path) { 38 | t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild') 39 | return _createReadStream(path) 40 | } 41 | 42 | const _request = https.request 43 | https.request = function (opts) { 44 | https.request = _request 45 | t.equal('https://' + opts.hostname + opts.path, downloadUrl, 'correct url') 46 | return _request.apply(https, arguments) 47 | } 48 | 49 | t.equal(fs.existsSync(build), false, 'no build folder') 50 | 51 | download(downloadUrl, opts, function (err) { 52 | t.error(err, 'no error') 53 | t.equal(fs.existsSync(util.prebuildCache()), true, 'prebuildCache created') 54 | t.equal(fs.existsSync(cachedPrebuild), true, 'prebuild was cached') 55 | t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist') 56 | t.equal(fs.existsSync(tempFile), false, 'temp file should be gone') 57 | fs.createWriteStream = _createWriteStream 58 | fs.createReadStream = _createReadStream 59 | }) 60 | }) 61 | 62 | test('cached prebuild', function (t) { 63 | t.plan(5) 64 | rm.sync(build) 65 | 66 | const opts = getOpts() 67 | const downloadUrl = util.getDownloadUrl(opts) 68 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 69 | 70 | const _createWriteStream = fs.createWriteStream 71 | fs.createWriteStream = function (path) { 72 | t.ok(/\.node$/i.test(path), 'this is the unpacked file') 73 | return _createWriteStream(path) 74 | } 75 | 76 | const _createReadStream = fs.createReadStream 77 | fs.createReadStream = function (path) { 78 | t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild') 79 | return _createReadStream(path) 80 | } 81 | 82 | t.equal(fs.existsSync(build), false, 'no build folder') 83 | 84 | download(downloadUrl, opts, function (err) { 85 | t.error(err, 'no error') 86 | t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist') 87 | fs.createReadStream = _createReadStream 88 | fs.createWriteStream = _createWriteStream 89 | }) 90 | }) 91 | 92 | test('local prebuild', function (t) { 93 | t.plan(6) 94 | rm.sync(build) 95 | 96 | const opts = getOpts() 97 | const downloadUrl = util.getDownloadUrl(opts) 98 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 99 | const localPrebuild = util.localPrebuild(downloadUrl, opts) 100 | 101 | t.ok(fs.existsSync(cachedPrebuild), 'cached prebuild exists') 102 | 103 | // fs.copyFileSync() not available before Node 8.5 104 | fs.writeFileSync(localPrebuild, fs.readFileSync(cachedPrebuild)) 105 | 106 | const _createWriteStream = fs.createWriteStream 107 | fs.createWriteStream = function (path) { 108 | t.ok(/\.node$/i.test(path), 'this is the unpacked file') 109 | return _createWriteStream(path) 110 | } 111 | 112 | const _createReadStream = fs.createReadStream 113 | fs.createReadStream = function (path) { 114 | t.equal(path, localPrebuild, 'createReadStream called for localPrebuild') 115 | return _createReadStream(path) 116 | } 117 | 118 | t.equal(fs.existsSync(build), false, 'no build folder') 119 | 120 | download(downloadUrl, opts, function (err) { 121 | t.error(err, 'no error') 122 | t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist') 123 | fs.createReadStream = _createReadStream 124 | fs.createWriteStream = _createWriteStream 125 | rm.sync(localPrebuild) 126 | }) 127 | }) 128 | 129 | test('non existing host should fail with no dangling temp file', function (t) { 130 | t.plan(3) 131 | 132 | const opts = getOpts() 133 | opts.pkg.binary = { 134 | host: 'https://foo.bar.baz' 135 | } 136 | 137 | const downloadUrl = util.getDownloadUrl(opts) 138 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 139 | 140 | const _createWriteStream = fs.createWriteStream 141 | fs.createWriteStream = function (path) { 142 | t.ok(false, 'no temporary file should be written') 143 | return _createWriteStream(path) 144 | } 145 | 146 | t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached') 147 | 148 | download(downloadUrl, opts, function (err) { 149 | t.ok(err, 'should error') 150 | t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached') 151 | fs.createWriteStream = _createWriteStream 152 | }) 153 | }) 154 | 155 | test('existing host but invalid url should fail', function (t) { 156 | t.plan(3) 157 | 158 | const opts = getOpts() 159 | opts.pkg.binary = { 160 | host: 'http://localhost:8888', 161 | remote_path: 'prebuilds', 162 | package_name: 'woohooo-{abi}' 163 | } 164 | 165 | const downloadUrl = util.getDownloadUrl(opts) 166 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 167 | 168 | const server = http.createServer(function (req, res) { 169 | t.equal(req.url, '/prebuilds/woohooo-' + opts.abi, 'correct url') 170 | res.statusCode = 404 171 | res.end() 172 | }).listen(8888, function () { 173 | download(downloadUrl, opts, function (err) { 174 | t.same(err, error.noPrebuilts(opts)) 175 | t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached') 176 | t.end() 177 | server.unref() 178 | }) 179 | }) 180 | }) 181 | 182 | test('error during download should fail with no dangling temp file', function (t) { 183 | t.plan(7) 184 | 185 | const downloadError = new Error('something went wrong during download') 186 | 187 | const opts = getOpts() 188 | opts.pkg.binary = { 189 | host: 'http://localhost:8889', 190 | remote_path: 'prebuilds', 191 | package_name: 'woohooo-{abi}' 192 | } 193 | 194 | const downloadUrl = util.getDownloadUrl(opts) 195 | const cachedPrebuild = util.cachedPrebuild(downloadUrl) 196 | let tempFile 197 | 198 | const _createWriteStream = fs.createWriteStream 199 | fs.createWriteStream = function (path) { 200 | tempFile = path 201 | t.ok(/\.tmp$/i.test(path), 'this is the temporary file') 202 | return _createWriteStream(path) 203 | } 204 | 205 | const _request = http.request 206 | http.request = function (opts) { 207 | http.request = _request 208 | t.equal('http://' + opts.hostname + ':' + opts.port + opts.path, downloadUrl, 'correct url') 209 | const wrapped = arguments[1] 210 | arguments[1] = function (res) { 211 | t.equal(res.statusCode, 200, 'correct statusCode') 212 | // simulates error during download 213 | setTimeout(function () { res.emit('error', downloadError) }, 10) 214 | wrapped(res) 215 | } 216 | return _request.apply(http, arguments) 217 | } 218 | 219 | const server = http.createServer(function (req, res) { 220 | t.equal(req.url, '/prebuilds/woohooo-' + opts.abi, 'correct url') 221 | res.statusCode = 200 222 | res.write('yep') // simulates hanging request 223 | }).listen(8889, function () { 224 | download(downloadUrl, opts, function (err) { 225 | t.equal(err.message, downloadError.message, 'correct error') 226 | t.equal(fs.existsSync(tempFile), false, 'no dangling temp file') 227 | t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached') 228 | t.end() 229 | fs.createWriteStream = _createWriteStream 230 | server.unref() 231 | }) 232 | }) 233 | }) 234 | 235 | test('should fail if abi is system abi with invalid binary', function (t) { 236 | const opts = getOpts() 237 | opts.abi = process.versions.modules 238 | opts.pkg.binary = { host: 'http://localhost:8890' } 239 | 240 | const server = http.createServer(function (req, res) { 241 | res.statusCode = 200 242 | const archive = path.join(__dirname, 'invalid.tar.gz') 243 | fs.createReadStream(archive).pipe(res) 244 | }).listen(8890, function () { 245 | download(util.getDownloadUrl(opts), opts, function (err) { 246 | server.unref() 247 | if (err && typeof err.message === 'string') { 248 | t.pass('require failed because of invalid abi') 249 | } else { 250 | t.fail('should have caused a require() error') 251 | } 252 | t.end() 253 | }) 254 | }) 255 | }) 256 | 257 | function getOpts () { 258 | return { 259 | pkg: require('a-native-module/package'), 260 | runtime: 'node', 261 | abi: 64, 262 | platform: process.platform, 263 | arch: process.arch, 264 | path: __dirname, 265 | 'tag-prefix': 'v', 266 | 'local-prebuilds': __dirname 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /test/invalid.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prebuild/prebuild-install/7635ed93422ac88a2c262b5818202dd9fd942784/test/invalid.tar.gz -------------------------------------------------------------------------------- /test/log-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const Logger = require('../log') 3 | 4 | test('log.level defaults to notice if npm_config_loglevel is not set', function (t) { 5 | const log = Logger({}, {}) 6 | t.is(log.level, 'notice') 7 | t.end() 8 | }) 9 | 10 | test('log.level respects npm_config_loglevel', function (t) { 11 | const log = Logger({}, { npm_config_loglevel: 'info' }) 12 | t.is(log.level, 'info') 13 | t.end() 14 | }) 15 | 16 | test('log.level is verbose if rc.verbose', function (t) { 17 | const log = Logger({ verbose: true }, {}) 18 | t.is(log.level, 'verbose') 19 | t.end() 20 | }) 21 | -------------------------------------------------------------------------------- /test/proxy-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const proxy = require('../proxy') 3 | 4 | test('downloading using proxy', function (t) { 5 | t.plan(8) 6 | 7 | const opts = { 8 | proxy: 'https://user:pass@hostname.com:8080' 9 | } 10 | 11 | const reqOpts = { 12 | url: 'https://api.github.com/repos/ralphtheninja/a-native-module/releases', 13 | json: true, 14 | headers: { 15 | 'User-Agent': 'simple-get', 16 | Authorization: 'token TOKEN' 17 | } 18 | } 19 | 20 | const request = proxy(reqOpts, opts) 21 | 22 | t.equal(request.url, reqOpts.url, 'Request url remains the same') 23 | t.equal(request.json, reqOpts.json, 'Request json remains the same') 24 | t.equal(request.headers['User-Agent'], reqOpts.headers['User-Agent'], 'Request user agent remains the same') 25 | t.equal(request.Authorization, reqOpts.Authorization, 'Request auth remains the same') 26 | 27 | t.equal(request.agent.proxyOptions.host, 'hostname.com', 'Proxy hostname is set') 28 | t.equal(request.agent.proxyOptions.port, 8080, 'Proxy port is set') 29 | t.equal(request.agent.proxyOptions.proxyAuth, 'user:pass', 'Proxy auth is set') 30 | t.equal(request.agent.defaultPort, 443, 'Proxy default port is set') 31 | }) 32 | 33 | test('downloading without using proxy', function (t) { 34 | t.plan(1) 35 | 36 | const reqOpts = { 37 | url: 'https://api.github.com/repos/ralphtheninja/a-native-module/releases', 38 | json: true, 39 | headers: { 40 | 'User-Agent': 'simple-get', 41 | Authorization: 'token TOKEN' 42 | } 43 | } 44 | 45 | const request = proxy(reqOpts, {}) 46 | t.equal(request.agent, undefined, 'Proxy is not set') 47 | }) 48 | -------------------------------------------------------------------------------- /test/rc-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const path = require('path') 3 | const exec = require('child_process').exec 4 | const fs = require('fs') 5 | const tempy = require('tempy') // Locked to 0.2.1 for node 6 support 6 | const cleanEnv = require('./util/clean-env') 7 | 8 | test('custom config and aliases', function (t) { 9 | const args = [ 10 | '--arch ARCH', 11 | '--platform PLATFORM', 12 | '--download https://foo.bar', 13 | '--debug', 14 | '--version', 15 | '--help', 16 | '--path ../some/other/path', 17 | '--target 1.4.10', 18 | '--runtime electron', 19 | '--token TOKEN' 20 | ] 21 | runRc(t, args.join(' '), {}, function (rc, tmp) { 22 | t.equal(rc.arch, 'ARCH', 'correct arch') 23 | t.equal(rc.arch, rc.a, 'arch alias') 24 | t.equal(rc.platform, 'PLATFORM', 'correct platform') 25 | t.equal(rc.download, 'https://foo.bar', 'download is set') 26 | t.equal(rc.download, rc.d, 'download alias') 27 | t.equal(rc.debug, true, 'debug is set') 28 | t.equal(rc.version, true, 'version is set') 29 | t.equal(rc.version, rc.v, 'version alias') 30 | t.equal(rc.help, true, 'help is set') 31 | t.equal(rc.help, rc.h, 'help alias') 32 | t.equal(rc.path, path.resolve(tmp, '../some/other/path'), 'correct path') 33 | t.equal(rc.target, '1.4.10', 'correct target') 34 | t.equal(rc.target, rc.t, 'target alias') 35 | t.equal(rc.runtime, 'electron', 'correct runtime') 36 | t.equal(rc.runtime, rc.r, 'runtime alias') 37 | t.equal(rc.abi, '50', 'correct ABI') 38 | t.equal(rc.token, 'TOKEN', 'correct token') 39 | t.equal(rc['tag-prefix'], 'v', 'correct default tag prefix') 40 | t.end() 41 | }) 42 | }) 43 | 44 | // TODO: merge into above test 45 | test('npm args are passed on from npm environment into rc', function (t) { 46 | const args = [ 47 | '--build-from-source', 48 | '--download', 49 | 'https://foo.bar', 50 | '--debug', 51 | '--verbose' 52 | ].join(' ') 53 | 54 | runRc(t, args, {}, function (rc) { 55 | t.equal(rc.buildFromSource, true, 'buildFromSource should be true') 56 | t.equal(rc.debug, true, 'debug should be true') 57 | t.equal(rc.verbose, true, 'verbose should be true') 58 | t.equal(rc.download, 'https://foo.bar', 'download is set') 59 | t.end() 60 | }) 61 | }) 62 | 63 | test('npm_config_* are passed on from environment into rc', function (t) { 64 | const env = { 65 | // Note that these are validated by npm 66 | npm_config_proxy: 'http://localhost/', 67 | npm_config_https_proxy: 'https://localhost/', 68 | npm_config_local_address: '127.0.0.1', 69 | npm_config_target: '1.4.0', 70 | npm_config_runtime: 'electron', 71 | npm_config_platform: 'linux', 72 | npm_config_build_from_source: 'true', 73 | npm_config_libc: 'testlibc' 74 | } 75 | runRc(t, '', env, function (rc) { 76 | t.equal(rc.proxy, 'http://localhost/', 'proxy is set') 77 | t.equal(rc['https-proxy'], 'https://localhost/', 'https-proxy is set') 78 | t.equal(rc['local-address'], '127.0.0.1', 'local-address is set') 79 | t.equal(rc.target, '1.4.0', 'target is set') 80 | t.equal(rc.runtime, 'electron', 'runtime is set') 81 | t.equal(rc.platform, 'linux', 'platform is set') 82 | t.equal(rc.buildFromSource, true, 'build-from-source is set') 83 | t.equal(rc.libc, 'testlibc', 'libc is set') 84 | t.end() 85 | }) 86 | }) 87 | 88 | test('can pass in external package config to rc', function (t) { 89 | const pkg = { 90 | config: { 91 | target: '1.0.0', 92 | runtime: 'electron', 93 | arch: 'woohoo-arch' 94 | } 95 | } 96 | const rc = require('../rc')(pkg) 97 | t.equal(rc.target, '1.0.0', 'correct target') 98 | t.equal(rc.runtime, 'electron', 'correct runtime') 99 | t.equal(rc.arch, 'woohoo-arch', 'correct arch') 100 | t.end() 101 | }) 102 | 103 | test('use default ABI', function (t) { 104 | runRc(t, '', {}, function (rc) { 105 | t.equal(rc.abi, process.versions.modules, 'correct default ABI') 106 | t.end() 107 | }) 108 | }) 109 | 110 | test('using --tag-prefix will set the tag prefix', function (t) { 111 | const args = ['--tag-prefix @scoped/package@'] 112 | runRc(t, args.join(' '), {}, function (rc) { 113 | t.equal(rc['tag-prefix'], '@scoped/package@', 'tag prefix should be set') 114 | t.end() 115 | }) 116 | }) 117 | 118 | test('libc works on linux platform', function (t) { 119 | const args = [ 120 | '--libc musl --platform linux' 121 | ] 122 | runRc(t, args.join(' '), {}, function (rc, tmp) { 123 | t.equal(rc.libc, 'musl', 'libc family') 124 | t.equal(rc.platform, 'linux', 'platform') 125 | t.end() 126 | }) 127 | }) 128 | 129 | test('libc glibc is passed as empty', function (t) { 130 | const args = [ 131 | '--libc glibc --platform linux' 132 | ] 133 | runRc(t, args.join(' '), {}, function (rc, tmp) { 134 | t.equal(rc.libc, '', 'libc family') 135 | t.equal(rc.platform, 'linux', 'platform') 136 | t.end() 137 | }) 138 | }) 139 | 140 | test('libc is discarded on non-linux platform', function (t) { 141 | const args = [ 142 | '--libc musl --platform windows' 143 | ] 144 | runRc(t, args.join(' '), {}, function (rc, tmp) { 145 | t.equal(rc.libc, '', 'libc family') 146 | t.equal(rc.platform, 'windows', 'platform') 147 | t.end() 148 | }) 149 | }) 150 | 151 | function runRc (t, args, env, cb) { 152 | const pkg = { 153 | name: 'test', 154 | private: true, 155 | scripts: { 156 | install: 'node ' + path.resolve(__dirname, '..', 'rc.js') + ' ' + args 157 | } 158 | } 159 | 160 | const tmp = tempy.directory() 161 | const json = JSON.stringify(pkg) 162 | 163 | fs.writeFile(path.join(tmp, 'package.json'), json, function (err) { 164 | if (err) throw err 165 | 166 | const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm' 167 | const cmd = npm + ' run install' 168 | 169 | env = Object.assign(cleanEnv(process.env), env) 170 | 171 | exec(cmd, { env, cwd: tmp }, function (err, stdout, stderr) { 172 | t.error(err, 'no error') 173 | t.equal(stderr.trim(), '', 'no stderr') 174 | 175 | let result 176 | 177 | try { 178 | result = JSON.parse(stdout.slice(stdout.indexOf('{'))) 179 | } catch (e) { 180 | return t.fail(e) 181 | } 182 | 183 | cb(result, tmp) 184 | }) 185 | }) 186 | } 187 | -------------------------------------------------------------------------------- /test/releases.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/1612622", 4 | "assets_url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/1612622/assets", 5 | "upload_url": "https://uploads.github.com/repos/ralphtheninja/a-native-module/releases/1612622/assets{?name,label}", 6 | "html_url": "https://github.com/ralphtheninja/a-native-module/releases/tag/v1.0.0", 7 | "id": 1612622, 8 | "tag_name": "v1.0.0", 9 | "target_commitish": "master", 10 | "name": "", 11 | "draft": false, 12 | "author": { 13 | "login": "ralphtheninja", 14 | "id": 308049, 15 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 16 | "gravatar_id": "", 17 | "url": "https://api.github.com/users/ralphtheninja", 18 | "html_url": "https://github.com/ralphtheninja", 19 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 20 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 21 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 22 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 23 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 24 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 25 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 26 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 27 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 28 | "type": "User", 29 | "site_admin": false 30 | }, 31 | "prerelease": false, 32 | "created_at": "2015-07-31T13:06:36Z", 33 | "published_at": "2015-07-31T13:14:48Z", 34 | "assets": [ 35 | { 36 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758435", 37 | "id": 758435, 38 | "name": "a-native-module-v1.0.0-node-v11-darwin-x64.tar.gz", 39 | "label": null, 40 | "uploader": { 41 | "login": "ralphtheninja", 42 | "id": 308049, 43 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 44 | "gravatar_id": "", 45 | "url": "https://api.github.com/users/ralphtheninja", 46 | "html_url": "https://github.com/ralphtheninja", 47 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 48 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 49 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 50 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 51 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 52 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 53 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 54 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 55 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 56 | "type": "User", 57 | "site_admin": false 58 | }, 59 | "content_type": "application/x-targz", 60 | "state": "uploaded", 61 | "size": 151227, 62 | "download_count": 2, 63 | "created_at": "2015-07-31T13:21:32Z", 64 | "updated_at": "2015-07-31T13:21:34Z", 65 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v11-darwin-x64.tar.gz" 66 | }, 67 | { 68 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758433", 69 | "id": 758433, 70 | "name": "a-native-module-v1.0.0-node-v11-linux-ia32.tar.gz", 71 | "label": null, 72 | "uploader": { 73 | "login": "ralphtheninja", 74 | "id": 308049, 75 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 76 | "gravatar_id": "", 77 | "url": "https://api.github.com/users/ralphtheninja", 78 | "html_url": "https://github.com/ralphtheninja", 79 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 80 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 81 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 82 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 83 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 84 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 85 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 86 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 87 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 88 | "type": "User", 89 | "site_admin": false 90 | }, 91 | "content_type": "application/x-targz", 92 | "state": "uploaded", 93 | "size": 171362, 94 | "download_count": 1, 95 | "created_at": "2015-07-31T13:21:32Z", 96 | "updated_at": "2015-07-31T13:21:34Z", 97 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v11-linux-ia32.tar.gz" 98 | }, 99 | { 100 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758434", 101 | "id": 758434, 102 | "name": "a-native-module-v1.0.0-node-v11-linux-x64.tar.gz", 103 | "label": null, 104 | "uploader": { 105 | "login": "ralphtheninja", 106 | "id": 308049, 107 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 108 | "gravatar_id": "", 109 | "url": "https://api.github.com/users/ralphtheninja", 110 | "html_url": "https://github.com/ralphtheninja", 111 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 112 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 113 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 114 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 115 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 116 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 117 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 118 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 119 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 120 | "type": "User", 121 | "site_admin": false 122 | }, 123 | "content_type": "application/x-targz", 124 | "state": "uploaded", 125 | "size": 195028, 126 | "download_count": 855, 127 | "created_at": "2015-07-31T13:21:32Z", 128 | "updated_at": "2015-07-31T13:21:34Z", 129 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v11-linux-x64.tar.gz" 130 | }, 131 | { 132 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758437", 133 | "id": 758437, 134 | "name": "a-native-module-v1.0.0-node-v11-win32-ia32.tar.gz", 135 | "label": null, 136 | "uploader": { 137 | "login": "ralphtheninja", 138 | "id": 308049, 139 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 140 | "gravatar_id": "", 141 | "url": "https://api.github.com/users/ralphtheninja", 142 | "html_url": "https://github.com/ralphtheninja", 143 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 144 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 145 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 146 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 147 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 148 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 149 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 150 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 151 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 152 | "type": "User", 153 | "site_admin": false 154 | }, 155 | "content_type": "application/x-targz", 156 | "state": "uploaded", 157 | "size": 162739, 158 | "download_count": 0, 159 | "created_at": "2015-07-31T13:21:32Z", 160 | "updated_at": "2015-07-31T13:21:34Z", 161 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v11-win32-ia32.tar.gz" 162 | }, 163 | { 164 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758438", 165 | "id": 758438, 166 | "name": "a-native-module-v1.0.0-node-v11-win32-x64.tar.gz", 167 | "label": null, 168 | "uploader": { 169 | "login": "ralphtheninja", 170 | "id": 308049, 171 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 172 | "gravatar_id": "", 173 | "url": "https://api.github.com/users/ralphtheninja", 174 | "html_url": "https://github.com/ralphtheninja", 175 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 176 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 177 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 178 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 179 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 180 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 181 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 182 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 183 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 184 | "type": "User", 185 | "site_admin": false 186 | }, 187 | "content_type": "application/x-targz", 188 | "state": "uploaded", 189 | "size": 182340, 190 | "download_count": 0, 191 | "created_at": "2015-07-31T13:21:32Z", 192 | "updated_at": "2015-07-31T13:21:35Z", 193 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v11-win32-x64.tar.gz" 194 | }, 195 | { 196 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758436", 197 | "id": 758436, 198 | "name": "a-native-module-v1.0.0-node-v14-darwin-x64.tar.gz", 199 | "label": null, 200 | "uploader": { 201 | "login": "ralphtheninja", 202 | "id": 308049, 203 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 204 | "gravatar_id": "", 205 | "url": "https://api.github.com/users/ralphtheninja", 206 | "html_url": "https://github.com/ralphtheninja", 207 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 208 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 209 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 210 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 211 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 212 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 213 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 214 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 215 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 216 | "type": "User", 217 | "site_admin": false 218 | }, 219 | "content_type": "application/x-targz", 220 | "state": "uploaded", 221 | "size": 151875, 222 | "download_count": 107, 223 | "created_at": "2015-07-31T13:21:32Z", 224 | "updated_at": "2015-07-31T13:21:35Z", 225 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v14-darwin-x64.tar.gz" 226 | }, 227 | { 228 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758442", 229 | "id": 758442, 230 | "name": "a-native-module-v1.0.0-node-v14-linux-ia32.tar.gz", 231 | "label": null, 232 | "uploader": { 233 | "login": "ralphtheninja", 234 | "id": 308049, 235 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 236 | "gravatar_id": "", 237 | "url": "https://api.github.com/users/ralphtheninja", 238 | "html_url": "https://github.com/ralphtheninja", 239 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 240 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 241 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 242 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 243 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 244 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 245 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 246 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 247 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 248 | "type": "User", 249 | "site_admin": false 250 | }, 251 | "content_type": "application/x-targz", 252 | "state": "uploaded", 253 | "size": 185874, 254 | "download_count": 0, 255 | "created_at": "2015-07-31T13:21:32Z", 256 | "updated_at": "2015-07-31T13:21:37Z", 257 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v14-linux-ia32.tar.gz" 258 | }, 259 | { 260 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758439", 261 | "id": 758439, 262 | "name": "a-native-module-v1.0.0-node-v14-linux-x64.tar.gz", 263 | "label": null, 264 | "uploader": { 265 | "login": "ralphtheninja", 266 | "id": 308049, 267 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 268 | "gravatar_id": "", 269 | "url": "https://api.github.com/users/ralphtheninja", 270 | "html_url": "https://github.com/ralphtheninja", 271 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 272 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 273 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 274 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 275 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 276 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 277 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 278 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 279 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 280 | "type": "User", 281 | "site_admin": false 282 | }, 283 | "content_type": "application/x-targz", 284 | "state": "uploaded", 285 | "size": 205644, 286 | "download_count": 872, 287 | "created_at": "2015-07-31T13:21:32Z", 288 | "updated_at": "2015-07-31T13:21:35Z", 289 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v14-linux-x64.tar.gz" 290 | }, 291 | { 292 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758440", 293 | "id": 758440, 294 | "name": "a-native-module-v1.0.0-node-v14-win32-ia32.tar.gz", 295 | "label": null, 296 | "uploader": { 297 | "login": "ralphtheninja", 298 | "id": 308049, 299 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 300 | "gravatar_id": "", 301 | "url": "https://api.github.com/users/ralphtheninja", 302 | "html_url": "https://github.com/ralphtheninja", 303 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 304 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 305 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 306 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 307 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 308 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 309 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 310 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 311 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 312 | "type": "User", 313 | "site_admin": false 314 | }, 315 | "content_type": "application/x-targz", 316 | "state": "uploaded", 317 | "size": 162612, 318 | "download_count": 0, 319 | "created_at": "2015-07-31T13:21:32Z", 320 | "updated_at": "2015-07-31T13:21:35Z", 321 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v14-win32-ia32.tar.gz" 322 | }, 323 | { 324 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758441", 325 | "id": 758441, 326 | "name": "a-native-module-v1.0.0-node-v14-win32-x64.tar.gz", 327 | "label": null, 328 | "uploader": { 329 | "login": "ralphtheninja", 330 | "id": 308049, 331 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 332 | "gravatar_id": "", 333 | "url": "https://api.github.com/users/ralphtheninja", 334 | "html_url": "https://github.com/ralphtheninja", 335 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 336 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 337 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 338 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 339 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 340 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 341 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 342 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 343 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 344 | "type": "User", 345 | "site_admin": false 346 | }, 347 | "content_type": "application/x-targz", 348 | "state": "uploaded", 349 | "size": 183868, 350 | "download_count": 0, 351 | "created_at": "2015-07-31T13:21:32Z", 352 | "updated_at": "2015-07-31T13:21:36Z", 353 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v14-win32-x64.tar.gz" 354 | }, 355 | { 356 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758443", 357 | "id": 758443, 358 | "name": "a-native-module-v1.0.0-node-v42-darwin-x64.tar.gz", 359 | "label": null, 360 | "uploader": { 361 | "login": "ralphtheninja", 362 | "id": 308049, 363 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 364 | "gravatar_id": "", 365 | "url": "https://api.github.com/users/ralphtheninja", 366 | "html_url": "https://github.com/ralphtheninja", 367 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 368 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 369 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 370 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 371 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 372 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 373 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 374 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 375 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 376 | "type": "User", 377 | "site_admin": false 378 | }, 379 | "content_type": "application/x-targz", 380 | "state": "uploaded", 381 | "size": 152284, 382 | "download_count": 0, 383 | "created_at": "2015-07-31T13:21:32Z", 384 | "updated_at": "2015-07-31T13:21:36Z", 385 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v42-darwin-x64.tar.gz" 386 | }, 387 | { 388 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758444", 389 | "id": 758444, 390 | "name": "a-native-module-v1.0.0-node-v42-linux-ia32.tar.gz", 391 | "label": null, 392 | "uploader": { 393 | "login": "ralphtheninja", 394 | "id": 308049, 395 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 396 | "gravatar_id": "", 397 | "url": "https://api.github.com/users/ralphtheninja", 398 | "html_url": "https://github.com/ralphtheninja", 399 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 400 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 401 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 402 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 403 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 404 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 405 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 406 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 407 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 408 | "type": "User", 409 | "site_admin": false 410 | }, 411 | "content_type": "application/x-targz", 412 | "state": "uploaded", 413 | "size": 186123, 414 | "download_count": 0, 415 | "created_at": "2015-07-31T13:21:32Z", 416 | "updated_at": "2015-07-31T13:21:37Z", 417 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v42-linux-ia32.tar.gz" 418 | }, 419 | { 420 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758445", 421 | "id": 758445, 422 | "name": "a-native-module-v1.0.0-node-v42-linux-x64.tar.gz", 423 | "label": null, 424 | "uploader": { 425 | "login": "ralphtheninja", 426 | "id": 308049, 427 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 428 | "gravatar_id": "", 429 | "url": "https://api.github.com/users/ralphtheninja", 430 | "html_url": "https://github.com/ralphtheninja", 431 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 432 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 433 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 434 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 435 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 436 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 437 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 438 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 439 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 440 | "type": "User", 441 | "site_admin": false 442 | }, 443 | "content_type": "application/x-targz", 444 | "state": "uploaded", 445 | "size": 205765, 446 | "download_count": 456, 447 | "created_at": "2015-07-31T13:21:32Z", 448 | "updated_at": "2015-07-31T13:21:37Z", 449 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v42-linux-x64.tar.gz" 450 | }, 451 | { 452 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758449", 453 | "id": 758449, 454 | "name": "a-native-module-v1.0.0-node-v42-win32-ia32.tar.gz", 455 | "label": null, 456 | "uploader": { 457 | "login": "ralphtheninja", 458 | "id": 308049, 459 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 460 | "gravatar_id": "", 461 | "url": "https://api.github.com/users/ralphtheninja", 462 | "html_url": "https://github.com/ralphtheninja", 463 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 464 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 465 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 466 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 467 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 468 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 469 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 470 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 471 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 472 | "type": "User", 473 | "site_admin": false 474 | }, 475 | "content_type": "application/x-targz", 476 | "state": "uploaded", 477 | "size": 196774, 478 | "download_count": 0, 479 | "created_at": "2015-07-31T13:21:32Z", 480 | "updated_at": "2015-07-31T13:21:37Z", 481 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v42-win32-ia32.tar.gz" 482 | }, 483 | { 484 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758446", 485 | "id": 758446, 486 | "name": "a-native-module-v1.0.0-node-v42-win32-x64.tar.gz", 487 | "label": null, 488 | "uploader": { 489 | "login": "ralphtheninja", 490 | "id": 308049, 491 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 492 | "gravatar_id": "", 493 | "url": "https://api.github.com/users/ralphtheninja", 494 | "html_url": "https://github.com/ralphtheninja", 495 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 496 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 497 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 498 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 499 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 500 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 501 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 502 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 503 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 504 | "type": "User", 505 | "site_admin": false 506 | }, 507 | "content_type": "application/x-targz", 508 | "state": "uploaded", 509 | "size": 212538, 510 | "download_count": 0, 511 | "created_at": "2015-07-31T13:21:32Z", 512 | "updated_at": "2015-07-31T13:21:37Z", 513 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v42-win32-x64.tar.gz" 514 | }, 515 | { 516 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758447", 517 | "id": 758447, 518 | "name": "a-native-module-v1.0.0-node-v43-darwin-x64.tar.gz", 519 | "label": null, 520 | "uploader": { 521 | "login": "ralphtheninja", 522 | "id": 308049, 523 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 524 | "gravatar_id": "", 525 | "url": "https://api.github.com/users/ralphtheninja", 526 | "html_url": "https://github.com/ralphtheninja", 527 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 528 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 529 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 530 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 531 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 532 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 533 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 534 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 535 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 536 | "type": "User", 537 | "site_admin": false 538 | }, 539 | "content_type": "application/x-targz", 540 | "state": "uploaded", 541 | "size": 152281, 542 | "download_count": 0, 543 | "created_at": "2015-07-31T13:21:32Z", 544 | "updated_at": "2015-07-31T13:21:37Z", 545 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v43-darwin-x64.tar.gz" 546 | }, 547 | { 548 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758448", 549 | "id": 758448, 550 | "name": "a-native-module-v1.0.0-node-v43-linux-ia32.tar.gz", 551 | "label": null, 552 | "uploader": { 553 | "login": "ralphtheninja", 554 | "id": 308049, 555 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 556 | "gravatar_id": "", 557 | "url": "https://api.github.com/users/ralphtheninja", 558 | "html_url": "https://github.com/ralphtheninja", 559 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 560 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 561 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 562 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 563 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 564 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 565 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 566 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 567 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 568 | "type": "User", 569 | "site_admin": false 570 | }, 571 | "content_type": "application/x-targz", 572 | "state": "uploaded", 573 | "size": 186123, 574 | "download_count": 0, 575 | "created_at": "2015-07-31T13:21:32Z", 576 | "updated_at": "2015-07-31T13:21:37Z", 577 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v43-linux-ia32.tar.gz" 578 | }, 579 | { 580 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758450", 581 | "id": 758450, 582 | "name": "a-native-module-v1.0.0-node-v43-linux-x64.tar.gz", 583 | "label": null, 584 | "uploader": { 585 | "login": "ralphtheninja", 586 | "id": 308049, 587 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 588 | "gravatar_id": "", 589 | "url": "https://api.github.com/users/ralphtheninja", 590 | "html_url": "https://github.com/ralphtheninja", 591 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 592 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 593 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 594 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 595 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 596 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 597 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 598 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 599 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 600 | "type": "User", 601 | "site_admin": false 602 | }, 603 | "content_type": "application/x-targz", 604 | "state": "uploaded", 605 | "size": 205762, 606 | "download_count": 456, 607 | "created_at": "2015-07-31T13:21:33Z", 608 | "updated_at": "2015-07-31T13:21:37Z", 609 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v43-linux-x64.tar.gz" 610 | }, 611 | { 612 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758454", 613 | "id": 758454, 614 | "name": "a-native-module-v1.0.0-node-v43-win32-ia32.tar.gz", 615 | "label": null, 616 | "uploader": { 617 | "login": "ralphtheninja", 618 | "id": 308049, 619 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 620 | "gravatar_id": "", 621 | "url": "https://api.github.com/users/ralphtheninja", 622 | "html_url": "https://github.com/ralphtheninja", 623 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 624 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 625 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 626 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 627 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 628 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 629 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 630 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 631 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 632 | "type": "User", 633 | "site_admin": false 634 | }, 635 | "content_type": "application/x-targz", 636 | "state": "uploaded", 637 | "size": 196776, 638 | "download_count": 0, 639 | "created_at": "2015-07-31T13:21:33Z", 640 | "updated_at": "2015-07-31T13:21:38Z", 641 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v43-win32-ia32.tar.gz" 642 | }, 643 | { 644 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758451", 645 | "id": 758451, 646 | "name": "a-native-module-v1.0.0-node-v43-win32-x64.tar.gz", 647 | "label": null, 648 | "uploader": { 649 | "login": "ralphtheninja", 650 | "id": 308049, 651 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 652 | "gravatar_id": "", 653 | "url": "https://api.github.com/users/ralphtheninja", 654 | "html_url": "https://github.com/ralphtheninja", 655 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 656 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 657 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 658 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 659 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 660 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 661 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 662 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 663 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 664 | "type": "User", 665 | "site_admin": false 666 | }, 667 | "content_type": "application/x-targz", 668 | "state": "uploaded", 669 | "size": 212534, 670 | "download_count": 0, 671 | "created_at": "2015-07-31T13:21:33Z", 672 | "updated_at": "2015-07-31T13:21:38Z", 673 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v43-win32-x64.tar.gz" 674 | }, 675 | { 676 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758453", 677 | "id": 758453, 678 | "name": "a-native-module-v1.0.0-node-v44-darwin-x64.tar.gz", 679 | "label": null, 680 | "uploader": { 681 | "login": "ralphtheninja", 682 | "id": 308049, 683 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 684 | "gravatar_id": "", 685 | "url": "https://api.github.com/users/ralphtheninja", 686 | "html_url": "https://github.com/ralphtheninja", 687 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 688 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 689 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 690 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 691 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 692 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 693 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 694 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 695 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 696 | "type": "User", 697 | "site_admin": false 698 | }, 699 | "content_type": "application/x-targz", 700 | "state": "uploaded", 701 | "size": 152259, 702 | "download_count": 21, 703 | "created_at": "2015-07-31T13:21:33Z", 704 | "updated_at": "2015-07-31T13:21:38Z", 705 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v44-darwin-x64.tar.gz" 706 | }, 707 | { 708 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758452", 709 | "id": 758452, 710 | "name": "a-native-module-v1.0.0-node-v44-linux-ia32.tar.gz", 711 | "label": null, 712 | "uploader": { 713 | "login": "ralphtheninja", 714 | "id": 308049, 715 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 716 | "gravatar_id": "", 717 | "url": "https://api.github.com/users/ralphtheninja", 718 | "html_url": "https://github.com/ralphtheninja", 719 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 720 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 721 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 722 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 723 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 724 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 725 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 726 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 727 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 728 | "type": "User", 729 | "site_admin": false 730 | }, 731 | "content_type": "application/x-targz", 732 | "state": "uploaded", 733 | "size": 186026, 734 | "download_count": 0, 735 | "created_at": "2015-07-31T13:21:33Z", 736 | "updated_at": "2015-07-31T13:21:38Z", 737 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v44-linux-ia32.tar.gz" 738 | }, 739 | { 740 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758455", 741 | "id": 758455, 742 | "name": "a-native-module-v1.0.0-node-v44-linux-x64.tar.gz", 743 | "label": null, 744 | "uploader": { 745 | "login": "ralphtheninja", 746 | "id": 308049, 747 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 748 | "gravatar_id": "", 749 | "url": "https://api.github.com/users/ralphtheninja", 750 | "html_url": "https://github.com/ralphtheninja", 751 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 752 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 753 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 754 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 755 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 756 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 757 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 758 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 759 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 760 | "type": "User", 761 | "site_admin": false 762 | }, 763 | "content_type": "application/x-targz", 764 | "state": "uploaded", 765 | "size": 205671, 766 | "download_count": 650, 767 | "created_at": "2015-07-31T13:21:33Z", 768 | "updated_at": "2015-07-31T13:21:38Z", 769 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v44-linux-x64.tar.gz" 770 | }, 771 | { 772 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/758456", 773 | "id": 758456, 774 | "name": "a-native-module-v1.0.0-node-v44-win32-ia32.tar.gz", 775 | "label": null, 776 | "uploader": { 777 | "login": "ralphtheninja", 778 | "id": 308049, 779 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 780 | "gravatar_id": "", 781 | "url": "https://api.github.com/users/ralphtheninja", 782 | "html_url": "https://github.com/ralphtheninja", 783 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 784 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 785 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 786 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 787 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 788 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 789 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 790 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 791 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 792 | "type": "User", 793 | "site_admin": false 794 | }, 795 | "content_type": "application/x-targz", 796 | "state": "uploaded", 797 | "size": 196720, 798 | "download_count": 0, 799 | "created_at": "2015-07-31T13:21:33Z", 800 | "updated_at": "2015-07-31T13:21:39Z", 801 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v44-win32-ia32.tar.gz" 802 | }, 803 | { 804 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904883", 805 | "id": 2904883, 806 | "name": "a-native-module-v1.0.0-node-v44-win32-x64.tar.gz", 807 | "label": null, 808 | "uploader": { 809 | "login": "mathiask88", 810 | "id": 4368785, 811 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 812 | "gravatar_id": "", 813 | "url": "https://api.github.com/users/mathiask88", 814 | "html_url": "https://github.com/mathiask88", 815 | "followers_url": "https://api.github.com/users/mathiask88/followers", 816 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 817 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 818 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 819 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 820 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 821 | "repos_url": "https://api.github.com/users/mathiask88/repos", 822 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 823 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 824 | "type": "User", 825 | "site_admin": false 826 | }, 827 | "content_type": "application/x-gzip", 828 | "state": "uploaded", 829 | "size": 221267, 830 | "download_count": 0, 831 | "created_at": "2016-12-30T12:48:13Z", 832 | "updated_at": "2016-12-30T12:48:14Z", 833 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v44-win32-x64.tar.gz" 834 | }, 835 | { 836 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/797799", 837 | "id": 797799, 838 | "name": "a-native-module-v1.0.0-node-v45-darwin-x64.tar.gz", 839 | "label": null, 840 | "uploader": { 841 | "login": "ralphtheninja", 842 | "id": 308049, 843 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 844 | "gravatar_id": "", 845 | "url": "https://api.github.com/users/ralphtheninja", 846 | "html_url": "https://github.com/ralphtheninja", 847 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 848 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 849 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 850 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 851 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 852 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 853 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 854 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 855 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 856 | "type": "User", 857 | "site_admin": false 858 | }, 859 | "content_type": "application/gzip", 860 | "state": "uploaded", 861 | "size": 156195, 862 | "download_count": 5, 863 | "created_at": "2015-08-18T10:42:12Z", 864 | "updated_at": "2015-08-18T10:42:24Z", 865 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v45-darwin-x64.tar.gz" 866 | }, 867 | { 868 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/797803", 869 | "id": 797803, 870 | "name": "a-native-module-v1.0.0-node-v45-linux-ia32.tar.gz", 871 | "label": null, 872 | "uploader": { 873 | "login": "ralphtheninja", 874 | "id": 308049, 875 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 876 | "gravatar_id": "", 877 | "url": "https://api.github.com/users/ralphtheninja", 878 | "html_url": "https://github.com/ralphtheninja", 879 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 880 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 881 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 882 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 883 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 884 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 885 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 886 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 887 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 888 | "type": "User", 889 | "site_admin": false 890 | }, 891 | "content_type": "application/gzip", 892 | "state": "uploaded", 893 | "size": 219701, 894 | "download_count": 0, 895 | "created_at": "2015-08-18T10:42:15Z", 896 | "updated_at": "2015-08-18T10:42:49Z", 897 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v45-linux-ia32.tar.gz" 898 | }, 899 | { 900 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/797801", 901 | "id": 797801, 902 | "name": "a-native-module-v1.0.0-node-v45-linux-x64.tar.gz", 903 | "label": null, 904 | "uploader": { 905 | "login": "ralphtheninja", 906 | "id": 308049, 907 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 908 | "gravatar_id": "", 909 | "url": "https://api.github.com/users/ralphtheninja", 910 | "html_url": "https://github.com/ralphtheninja", 911 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 912 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 913 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 914 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 915 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 916 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 917 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 918 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 919 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 920 | "type": "User", 921 | "site_admin": false 922 | }, 923 | "content_type": "application/gzip", 924 | "state": "uploaded", 925 | "size": 222512, 926 | "download_count": 572, 927 | "created_at": "2015-08-18T10:42:13Z", 928 | "updated_at": "2015-08-18T10:42:44Z", 929 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v45-linux-x64.tar.gz" 930 | }, 931 | { 932 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/797802", 933 | "id": 797802, 934 | "name": "a-native-module-v1.0.0-node-v45-win32-ia32.tar.gz", 935 | "label": null, 936 | "uploader": { 937 | "login": "ralphtheninja", 938 | "id": 308049, 939 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 940 | "gravatar_id": "", 941 | "url": "https://api.github.com/users/ralphtheninja", 942 | "html_url": "https://github.com/ralphtheninja", 943 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 944 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 945 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 946 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 947 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 948 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 949 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 950 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 951 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 952 | "type": "User", 953 | "site_admin": false 954 | }, 955 | "content_type": "application/gzip", 956 | "state": "uploaded", 957 | "size": 200973, 958 | "download_count": 0, 959 | "created_at": "2015-08-18T10:42:14Z", 960 | "updated_at": "2015-08-18T10:42:45Z", 961 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v45-win32-ia32.tar.gz" 962 | }, 963 | { 964 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904884", 965 | "id": 2904884, 966 | "name": "a-native-module-v1.0.0-node-v45-win32-x64.tar.gz", 967 | "label": null, 968 | "uploader": { 969 | "login": "mathiask88", 970 | "id": 4368785, 971 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 972 | "gravatar_id": "", 973 | "url": "https://api.github.com/users/mathiask88", 974 | "html_url": "https://github.com/mathiask88", 975 | "followers_url": "https://api.github.com/users/mathiask88/followers", 976 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 977 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 978 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 979 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 980 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 981 | "repos_url": "https://api.github.com/users/mathiask88/repos", 982 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 983 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 984 | "type": "User", 985 | "site_admin": false 986 | }, 987 | "content_type": "application/x-gzip", 988 | "state": "uploaded", 989 | "size": 222292, 990 | "download_count": 0, 991 | "created_at": "2016-12-30T12:48:13Z", 992 | "updated_at": "2016-12-30T12:48:15Z", 993 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v45-win32-x64.tar.gz" 994 | }, 995 | { 996 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/887034", 997 | "id": 887034, 998 | "name": "a-native-module-v1.0.0-node-v46-darwin-x64.tar.gz", 999 | "label": null, 1000 | "uploader": { 1001 | "login": "ralphtheninja", 1002 | "id": 308049, 1003 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1004 | "gravatar_id": "", 1005 | "url": "https://api.github.com/users/ralphtheninja", 1006 | "html_url": "https://github.com/ralphtheninja", 1007 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1008 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1009 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1010 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1011 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1012 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1013 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1014 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1015 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1016 | "type": "User", 1017 | "site_admin": false 1018 | }, 1019 | "content_type": "application/gzip", 1020 | "state": "uploaded", 1021 | "size": 155692, 1022 | "download_count": 91, 1023 | "created_at": "2015-09-23T12:31:56Z", 1024 | "updated_at": "2015-09-23T12:31:58Z", 1025 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-darwin-x64.tar.gz" 1026 | }, 1027 | { 1028 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/887036", 1029 | "id": 887036, 1030 | "name": "a-native-module-v1.0.0-node-v46-linux-arm.tar.gz", 1031 | "label": null, 1032 | "uploader": { 1033 | "login": "ralphtheninja", 1034 | "id": 308049, 1035 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1036 | "gravatar_id": "", 1037 | "url": "https://api.github.com/users/ralphtheninja", 1038 | "html_url": "https://github.com/ralphtheninja", 1039 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1040 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1041 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1042 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1043 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1044 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1045 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1046 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1047 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1048 | "type": "User", 1049 | "site_admin": false 1050 | }, 1051 | "content_type": "application/gzip", 1052 | "state": "uploaded", 1053 | "size": 189892, 1054 | "download_count": 0, 1055 | "created_at": "2015-09-23T12:31:56Z", 1056 | "updated_at": "2015-09-23T12:32:00Z", 1057 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-linux-arm.tar.gz" 1058 | }, 1059 | { 1060 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/887038", 1061 | "id": 887038, 1062 | "name": "a-native-module-v1.0.0-node-v46-linux-ia32.tar.gz", 1063 | "label": null, 1064 | "uploader": { 1065 | "login": "ralphtheninja", 1066 | "id": 308049, 1067 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1068 | "gravatar_id": "", 1069 | "url": "https://api.github.com/users/ralphtheninja", 1070 | "html_url": "https://github.com/ralphtheninja", 1071 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1072 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1073 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1074 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1075 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1076 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1077 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1078 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1079 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1080 | "type": "User", 1081 | "site_admin": false 1082 | }, 1083 | "content_type": "application/gzip", 1084 | "state": "uploaded", 1085 | "size": 220579, 1086 | "download_count": 4, 1087 | "created_at": "2015-09-23T12:31:56Z", 1088 | "updated_at": "2015-09-23T12:31:59Z", 1089 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-linux-ia32.tar.gz" 1090 | }, 1091 | { 1092 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/855425", 1093 | "id": 855425, 1094 | "name": "a-native-module-v1.0.0-node-v46-linux-x64.tar.gz", 1095 | "label": null, 1096 | "uploader": { 1097 | "login": "ralphtheninja", 1098 | "id": 308049, 1099 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1100 | "gravatar_id": "", 1101 | "url": "https://api.github.com/users/ralphtheninja", 1102 | "html_url": "https://github.com/ralphtheninja", 1103 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1104 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1105 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1106 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1107 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1108 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1109 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1110 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1111 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1112 | "type": "User", 1113 | "site_admin": false 1114 | }, 1115 | "content_type": "application/gzip", 1116 | "state": "uploaded", 1117 | "size": 224578, 1118 | "download_count": 988, 1119 | "created_at": "2015-09-10T20:22:20Z", 1120 | "updated_at": "2015-09-10T20:22:22Z", 1121 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-linux-x64.tar.gz" 1122 | }, 1123 | { 1124 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/887035", 1125 | "id": 887035, 1126 | "name": "a-native-module-v1.0.0-node-v46-sunos-x64.tar.gz", 1127 | "label": null, 1128 | "uploader": { 1129 | "login": "ralphtheninja", 1130 | "id": 308049, 1131 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1132 | "gravatar_id": "", 1133 | "url": "https://api.github.com/users/ralphtheninja", 1134 | "html_url": "https://github.com/ralphtheninja", 1135 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1136 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1137 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1138 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1139 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1140 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1141 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1142 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1143 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1144 | "type": "User", 1145 | "site_admin": false 1146 | }, 1147 | "content_type": "application/gzip", 1148 | "state": "uploaded", 1149 | "size": 303005, 1150 | "download_count": 0, 1151 | "created_at": "2015-09-23T12:31:56Z", 1152 | "updated_at": "2015-09-23T12:31:59Z", 1153 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-sunos-x64.tar.gz" 1154 | }, 1155 | { 1156 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904880", 1157 | "id": 2904880, 1158 | "name": "a-native-module-v1.0.0-node-v46-win32-ia32.tar.gz", 1159 | "label": null, 1160 | "uploader": { 1161 | "login": "mathiask88", 1162 | "id": 4368785, 1163 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1164 | "gravatar_id": "", 1165 | "url": "https://api.github.com/users/mathiask88", 1166 | "html_url": "https://github.com/mathiask88", 1167 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1168 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1169 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1170 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1171 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1172 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1173 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1174 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1175 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1176 | "type": "User", 1177 | "site_admin": false 1178 | }, 1179 | "content_type": "application/x-gzip", 1180 | "state": "uploaded", 1181 | "size": 210261, 1182 | "download_count": 36, 1183 | "created_at": "2016-12-30T12:45:34Z", 1184 | "updated_at": "2016-12-30T12:45:35Z", 1185 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-win32-ia32.tar.gz" 1186 | }, 1187 | { 1188 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/887037", 1189 | "id": 887037, 1190 | "name": "a-native-module-v1.0.0-node-v46-win32-x64.tar.gz", 1191 | "label": null, 1192 | "uploader": { 1193 | "login": "ralphtheninja", 1194 | "id": 308049, 1195 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1196 | "gravatar_id": "", 1197 | "url": "https://api.github.com/users/ralphtheninja", 1198 | "html_url": "https://github.com/ralphtheninja", 1199 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1200 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1201 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1202 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1203 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1204 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1205 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1206 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1207 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1208 | "type": "User", 1209 | "site_admin": false 1210 | }, 1211 | "content_type": "application/gzip", 1212 | "state": "uploaded", 1213 | "size": 221024, 1214 | "download_count": 53, 1215 | "created_at": "2015-09-23T12:31:56Z", 1216 | "updated_at": "2015-09-23T12:32:00Z", 1217 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v46-win32-x64.tar.gz" 1218 | }, 1219 | { 1220 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/1073577", 1221 | "id": 1073577, 1222 | "name": "a-native-module-v1.0.0-node-v47-darwin-x64.tar.gz", 1223 | "label": null, 1224 | "uploader": { 1225 | "login": "ralphtheninja", 1226 | "id": 308049, 1227 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1228 | "gravatar_id": "", 1229 | "url": "https://api.github.com/users/ralphtheninja", 1230 | "html_url": "https://github.com/ralphtheninja", 1231 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1232 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1233 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1234 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1235 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1236 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1237 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1238 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1239 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1240 | "type": "User", 1241 | "site_admin": false 1242 | }, 1243 | "content_type": "application/gzip", 1244 | "state": "uploaded", 1245 | "size": 133659, 1246 | "download_count": 17, 1247 | "created_at": "2015-11-26T14:32:51Z", 1248 | "updated_at": "2015-11-26T14:32:57Z", 1249 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v47-darwin-x64.tar.gz" 1250 | }, 1251 | { 1252 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/1073574", 1253 | "id": 1073574, 1254 | "name": "a-native-module-v1.0.0-node-v47-linux-arm.tar.gz", 1255 | "label": null, 1256 | "uploader": { 1257 | "login": "ralphtheninja", 1258 | "id": 308049, 1259 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1260 | "gravatar_id": "", 1261 | "url": "https://api.github.com/users/ralphtheninja", 1262 | "html_url": "https://github.com/ralphtheninja", 1263 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1264 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1265 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1266 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1267 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1268 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1269 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1270 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1271 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1272 | "type": "User", 1273 | "site_admin": false 1274 | }, 1275 | "content_type": "application/gzip", 1276 | "state": "uploaded", 1277 | "size": 175528, 1278 | "download_count": 0, 1279 | "created_at": "2015-11-26T14:32:48Z", 1280 | "updated_at": "2015-11-26T14:32:51Z", 1281 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v47-linux-arm.tar.gz" 1282 | }, 1283 | { 1284 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/1073576", 1285 | "id": 1073576, 1286 | "name": "a-native-module-v1.0.0-node-v47-linux-ia32.tar.gz", 1287 | "label": null, 1288 | "uploader": { 1289 | "login": "ralphtheninja", 1290 | "id": 308049, 1291 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1292 | "gravatar_id": "", 1293 | "url": "https://api.github.com/users/ralphtheninja", 1294 | "html_url": "https://github.com/ralphtheninja", 1295 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1296 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1297 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1298 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1299 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1300 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1301 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1302 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1303 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1304 | "type": "User", 1305 | "site_admin": false 1306 | }, 1307 | "content_type": "application/gzip", 1308 | "state": "uploaded", 1309 | "size": 197762, 1310 | "download_count": 0, 1311 | "created_at": "2015-11-26T14:32:49Z", 1312 | "updated_at": "2015-11-26T14:32:56Z", 1313 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v47-linux-ia32.tar.gz" 1314 | }, 1315 | { 1316 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/1073575", 1317 | "id": 1073575, 1318 | "name": "a-native-module-v1.0.0-node-v47-linux-x64.tar.gz", 1319 | "label": null, 1320 | "uploader": { 1321 | "login": "ralphtheninja", 1322 | "id": 308049, 1323 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1324 | "gravatar_id": "", 1325 | "url": "https://api.github.com/users/ralphtheninja", 1326 | "html_url": "https://github.com/ralphtheninja", 1327 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1328 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1329 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1330 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1331 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1332 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1333 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1334 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1335 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1336 | "type": "User", 1337 | "site_admin": false 1338 | }, 1339 | "content_type": "application/gzip", 1340 | "state": "uploaded", 1341 | "size": 199769, 1342 | "download_count": 780, 1343 | "created_at": "2015-11-26T14:32:49Z", 1344 | "updated_at": "2015-11-26T14:32:53Z", 1345 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v47-linux-x64.tar.gz" 1346 | }, 1347 | { 1348 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904878", 1349 | "id": 2904878, 1350 | "name": "a-native-module-v1.0.0-node-v47-win32-ia32.tar.gz", 1351 | "label": null, 1352 | "uploader": { 1353 | "login": "mathiask88", 1354 | "id": 4368785, 1355 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1356 | "gravatar_id": "", 1357 | "url": "https://api.github.com/users/mathiask88", 1358 | "html_url": "https://github.com/mathiask88", 1359 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1360 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1361 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1362 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1363 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1364 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1365 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1366 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1367 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1368 | "type": "User", 1369 | "site_admin": false 1370 | }, 1371 | "content_type": "application/x-gzip", 1372 | "state": "uploaded", 1373 | "size": 210262, 1374 | "download_count": 6, 1375 | "created_at": "2016-12-30T12:44:58Z", 1376 | "updated_at": "2016-12-30T12:45:00Z", 1377 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v47-win32-ia32.tar.gz" 1378 | }, 1379 | { 1380 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904877", 1381 | "id": 2904877, 1382 | "name": "a-native-module-v1.0.0-node-v47-win32-x64.tar.gz", 1383 | "label": null, 1384 | "uploader": { 1385 | "login": "mathiask88", 1386 | "id": 4368785, 1387 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1388 | "gravatar_id": "", 1389 | "url": "https://api.github.com/users/mathiask88", 1390 | "html_url": "https://github.com/mathiask88", 1391 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1392 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1393 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1394 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1395 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1396 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1397 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1398 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1399 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1400 | "type": "User", 1401 | "site_admin": false 1402 | }, 1403 | "content_type": "application/x-gzip", 1404 | "state": "uploaded", 1405 | "size": 245312, 1406 | "download_count": 6, 1407 | "created_at": "2016-12-30T12:44:58Z", 1408 | "updated_at": "2016-12-30T12:44:59Z", 1409 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v47-win32-x64.tar.gz" 1410 | }, 1411 | { 1412 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/1627496", 1413 | "id": 1627496, 1414 | "name": "a-native-module-v1.0.0-node-v48-darwin-x64.tar.gz", 1415 | "label": null, 1416 | "uploader": { 1417 | "login": "ralphtheninja", 1418 | "id": 308049, 1419 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1420 | "gravatar_id": "", 1421 | "url": "https://api.github.com/users/ralphtheninja", 1422 | "html_url": "https://github.com/ralphtheninja", 1423 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1424 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1425 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1426 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1427 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1428 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1429 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1430 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1431 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1432 | "type": "User", 1433 | "site_admin": false 1434 | }, 1435 | "content_type": "application/gzip", 1436 | "state": "uploaded", 1437 | "size": 139878, 1438 | "download_count": 45, 1439 | "created_at": "2016-05-02T11:49:09Z", 1440 | "updated_at": "2016-05-02T11:49:11Z", 1441 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v48-darwin-x64.tar.gz" 1442 | }, 1443 | { 1444 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/1627497", 1445 | "id": 1627497, 1446 | "name": "a-native-module-v1.0.0-node-v48-linux-x64.tar.gz", 1447 | "label": null, 1448 | "uploader": { 1449 | "login": "ralphtheninja", 1450 | "id": 308049, 1451 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1452 | "gravatar_id": "", 1453 | "url": "https://api.github.com/users/ralphtheninja", 1454 | "html_url": "https://github.com/ralphtheninja", 1455 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1456 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1457 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1458 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1459 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1460 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1461 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1462 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1463 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1464 | "type": "User", 1465 | "site_admin": false 1466 | }, 1467 | "content_type": "application/gzip", 1468 | "state": "uploaded", 1469 | "size": 198200, 1470 | "download_count": 532, 1471 | "created_at": "2016-05-02T11:49:09Z", 1472 | "updated_at": "2016-05-02T11:49:12Z", 1473 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v48-linux-x64.tar.gz" 1474 | }, 1475 | { 1476 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904870", 1477 | "id": 2904870, 1478 | "name": "a-native-module-v1.0.0-node-v48-win32-ia32.tar.gz", 1479 | "label": null, 1480 | "uploader": { 1481 | "login": "mathiask88", 1482 | "id": 4368785, 1483 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1484 | "gravatar_id": "", 1485 | "url": "https://api.github.com/users/mathiask88", 1486 | "html_url": "https://github.com/mathiask88", 1487 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1488 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1489 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1490 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1491 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1492 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1493 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1494 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1495 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1496 | "type": "User", 1497 | "site_admin": false 1498 | }, 1499 | "content_type": "application/x-gzip", 1500 | "state": "uploaded", 1501 | "size": 210242, 1502 | "download_count": 36, 1503 | "created_at": "2016-12-30T12:44:44Z", 1504 | "updated_at": "2016-12-30T12:44:45Z", 1505 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v48-win32-ia32.tar.gz" 1506 | }, 1507 | { 1508 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904869", 1509 | "id": 2904869, 1510 | "name": "a-native-module-v1.0.0-node-v48-win32-x64.tar.gz", 1511 | "label": null, 1512 | "uploader": { 1513 | "login": "mathiask88", 1514 | "id": 4368785, 1515 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1516 | "gravatar_id": "", 1517 | "url": "https://api.github.com/users/mathiask88", 1518 | "html_url": "https://github.com/mathiask88", 1519 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1520 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1521 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1522 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1523 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1524 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1525 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1526 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1527 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1528 | "type": "User", 1529 | "site_admin": false 1530 | }, 1531 | "content_type": "application/x-gzip", 1532 | "state": "uploaded", 1533 | "size": 245318, 1534 | "download_count": 38, 1535 | "created_at": "2016-12-30T12:44:44Z", 1536 | "updated_at": "2016-12-30T12:44:46Z", 1537 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v48-win32-x64.tar.gz" 1538 | }, 1539 | { 1540 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2553406", 1541 | "id": 2553406, 1542 | "name": "a-native-module-v1.0.0-node-v51-darwin-x64.tar.gz", 1543 | "label": null, 1544 | "uploader": { 1545 | "login": "ralphtheninja", 1546 | "id": 308049, 1547 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1548 | "gravatar_id": "", 1549 | "url": "https://api.github.com/users/ralphtheninja", 1550 | "html_url": "https://github.com/ralphtheninja", 1551 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1552 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1553 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1554 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1555 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1556 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1557 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1558 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1559 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1560 | "type": "User", 1561 | "site_admin": false 1562 | }, 1563 | "content_type": "application/gzip", 1564 | "state": "uploaded", 1565 | "size": 138559, 1566 | "download_count": 254, 1567 | "created_at": "2016-10-29T15:10:52Z", 1568 | "updated_at": "2016-10-29T15:10:54Z", 1569 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v51-darwin-x64.tar.gz" 1570 | }, 1571 | { 1572 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2553405", 1573 | "id": 2553405, 1574 | "name": "a-native-module-v1.0.0-node-v51-linux-x64.tar.gz", 1575 | "label": null, 1576 | "uploader": { 1577 | "login": "ralphtheninja", 1578 | "id": 308049, 1579 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1580 | "gravatar_id": "", 1581 | "url": "https://api.github.com/users/ralphtheninja", 1582 | "html_url": "https://github.com/ralphtheninja", 1583 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1584 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1585 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1586 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1587 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1588 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1589 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1590 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1591 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1592 | "type": "User", 1593 | "site_admin": false 1594 | }, 1595 | "content_type": "application/gzip", 1596 | "state": "uploaded", 1597 | "size": 200332, 1598 | "download_count": 274, 1599 | "created_at": "2016-10-29T15:10:52Z", 1600 | "updated_at": "2016-10-29T15:10:53Z", 1601 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v51-linux-x64.tar.gz" 1602 | }, 1603 | { 1604 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904868", 1605 | "id": 2904868, 1606 | "name": "a-native-module-v1.0.0-node-v51-win32-ia32.tar.gz", 1607 | "label": null, 1608 | "uploader": { 1609 | "login": "mathiask88", 1610 | "id": 4368785, 1611 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1612 | "gravatar_id": "", 1613 | "url": "https://api.github.com/users/mathiask88", 1614 | "html_url": "https://github.com/mathiask88", 1615 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1616 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1617 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1618 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1619 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1620 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1621 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1622 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1623 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1624 | "type": "User", 1625 | "site_admin": false 1626 | }, 1627 | "content_type": "application/x-gzip", 1628 | "state": "uploaded", 1629 | "size": 210151, 1630 | "download_count": 6, 1631 | "created_at": "2016-12-30T12:44:29Z", 1632 | "updated_at": "2016-12-30T12:44:31Z", 1633 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v51-win32-ia32.tar.gz" 1634 | }, 1635 | { 1636 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/2904867", 1637 | "id": 2904867, 1638 | "name": "a-native-module-v1.0.0-node-v51-win32-x64.tar.gz", 1639 | "label": null, 1640 | "uploader": { 1641 | "login": "mathiask88", 1642 | "id": 4368785, 1643 | "avatar_url": "https://avatars2.githubusercontent.com/u/4368785?v=4", 1644 | "gravatar_id": "", 1645 | "url": "https://api.github.com/users/mathiask88", 1646 | "html_url": "https://github.com/mathiask88", 1647 | "followers_url": "https://api.github.com/users/mathiask88/followers", 1648 | "following_url": "https://api.github.com/users/mathiask88/following{/other_user}", 1649 | "gists_url": "https://api.github.com/users/mathiask88/gists{/gist_id}", 1650 | "starred_url": "https://api.github.com/users/mathiask88/starred{/owner}{/repo}", 1651 | "subscriptions_url": "https://api.github.com/users/mathiask88/subscriptions", 1652 | "organizations_url": "https://api.github.com/users/mathiask88/orgs", 1653 | "repos_url": "https://api.github.com/users/mathiask88/repos", 1654 | "events_url": "https://api.github.com/users/mathiask88/events{/privacy}", 1655 | "received_events_url": "https://api.github.com/users/mathiask88/received_events", 1656 | "type": "User", 1657 | "site_admin": false 1658 | }, 1659 | "content_type": "application/x-gzip", 1660 | "state": "uploaded", 1661 | "size": 245342, 1662 | "download_count": 61, 1663 | "created_at": "2016-12-30T12:44:29Z", 1664 | "updated_at": "2016-12-30T12:44:30Z", 1665 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v51-win32-x64.tar.gz" 1666 | }, 1667 | { 1668 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5966685", 1669 | "id": 5966685, 1670 | "name": "a-native-module-v1.0.0-node-v57-darwin-x64.tar.gz", 1671 | "label": null, 1672 | "uploader": { 1673 | "login": "ralphtheninja", 1674 | "id": 308049, 1675 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1676 | "gravatar_id": "", 1677 | "url": "https://api.github.com/users/ralphtheninja", 1678 | "html_url": "https://github.com/ralphtheninja", 1679 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1680 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1681 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1682 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1683 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1684 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1685 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1686 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1687 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1688 | "type": "User", 1689 | "site_admin": false 1690 | }, 1691 | "content_type": "application/gzip", 1692 | "state": "uploaded", 1693 | "size": 169373, 1694 | "download_count": 65, 1695 | "created_at": "2018-01-24T02:30:49Z", 1696 | "updated_at": "2018-01-24T02:30:51Z", 1697 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v57-darwin-x64.tar.gz" 1698 | }, 1699 | { 1700 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/4270474", 1701 | "id": 4270474, 1702 | "name": "a-native-module-v1.0.0-node-v57-linux-x64.tar.gz", 1703 | "label": null, 1704 | "uploader": { 1705 | "login": "ralphtheninja", 1706 | "id": 308049, 1707 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1708 | "gravatar_id": "", 1709 | "url": "https://api.github.com/users/ralphtheninja", 1710 | "html_url": "https://github.com/ralphtheninja", 1711 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1712 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1713 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1714 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1715 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1716 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1717 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1718 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1719 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1720 | "type": "User", 1721 | "site_admin": false 1722 | }, 1723 | "content_type": "application/gzip", 1724 | "state": "uploaded", 1725 | "size": 223154, 1726 | "download_count": 146, 1727 | "created_at": "2017-07-06T22:21:30Z", 1728 | "updated_at": "2017-07-06T22:21:31Z", 1729 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v57-linux-x64.tar.gz" 1730 | }, 1731 | { 1732 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5966686", 1733 | "id": 5966686, 1734 | "name": "a-native-module-v1.0.0-node-v57-win32-ia32.tar.gz", 1735 | "label": null, 1736 | "uploader": { 1737 | "login": "ralphtheninja", 1738 | "id": 308049, 1739 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1740 | "gravatar_id": "", 1741 | "url": "https://api.github.com/users/ralphtheninja", 1742 | "html_url": "https://github.com/ralphtheninja", 1743 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1744 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1745 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1746 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1747 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1748 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1749 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1750 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1751 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1752 | "type": "User", 1753 | "site_admin": false 1754 | }, 1755 | "content_type": "application/gzip", 1756 | "state": "uploaded", 1757 | "size": 211540, 1758 | "download_count": 39, 1759 | "created_at": "2018-01-24T02:30:50Z", 1760 | "updated_at": "2018-01-24T02:30:52Z", 1761 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v57-win32-ia32.tar.gz" 1762 | }, 1763 | { 1764 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5966687", 1765 | "id": 5966687, 1766 | "name": "a-native-module-v1.0.0-node-v57-win32-x64.tar.gz", 1767 | "label": null, 1768 | "uploader": { 1769 | "login": "ralphtheninja", 1770 | "id": 308049, 1771 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1772 | "gravatar_id": "", 1773 | "url": "https://api.github.com/users/ralphtheninja", 1774 | "html_url": "https://github.com/ralphtheninja", 1775 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1776 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1777 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1778 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1779 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1780 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1781 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1782 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1783 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1784 | "type": "User", 1785 | "site_admin": false 1786 | }, 1787 | "content_type": "application/gzip", 1788 | "state": "uploaded", 1789 | "size": 247259, 1790 | "download_count": 38, 1791 | "created_at": "2018-01-24T02:30:50Z", 1792 | "updated_at": "2018-01-24T02:30:53Z", 1793 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v57-win32-x64.tar.gz" 1794 | }, 1795 | { 1796 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5966688", 1797 | "id": 5966688, 1798 | "name": "a-native-module-v1.0.0-node-v59-darwin-x64.tar.gz", 1799 | "label": null, 1800 | "uploader": { 1801 | "login": "ralphtheninja", 1802 | "id": 308049, 1803 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1804 | "gravatar_id": "", 1805 | "url": "https://api.github.com/users/ralphtheninja", 1806 | "html_url": "https://github.com/ralphtheninja", 1807 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1808 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1809 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1810 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1811 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1812 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1813 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1814 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1815 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1816 | "type": "User", 1817 | "site_admin": false 1818 | }, 1819 | "content_type": "application/gzip", 1820 | "state": "uploaded", 1821 | "size": 169380, 1822 | "download_count": 39, 1823 | "created_at": "2018-01-24T02:30:50Z", 1824 | "updated_at": "2018-01-24T02:30:54Z", 1825 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v59-darwin-x64.tar.gz" 1826 | }, 1827 | { 1828 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5527146", 1829 | "id": 5527146, 1830 | "name": "a-native-module-v1.0.0-node-v59-linux-x64.tar.gz", 1831 | "label": null, 1832 | "uploader": { 1833 | "login": "ralphtheninja", 1834 | "id": 308049, 1835 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1836 | "gravatar_id": "", 1837 | "url": "https://api.github.com/users/ralphtheninja", 1838 | "html_url": "https://github.com/ralphtheninja", 1839 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1840 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1841 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1842 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1843 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1844 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1845 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1846 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1847 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1848 | "type": "User", 1849 | "site_admin": false 1850 | }, 1851 | "content_type": "application/gzip", 1852 | "state": "uploaded", 1853 | "size": 210057, 1854 | "download_count": 105, 1855 | "created_at": "2017-12-04T21:55:10Z", 1856 | "updated_at": "2017-12-04T21:55:11Z", 1857 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v59-linux-x64.tar.gz" 1858 | }, 1859 | { 1860 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5966689", 1861 | "id": 5966689, 1862 | "name": "a-native-module-v1.0.0-node-v59-win32-ia32.tar.gz", 1863 | "label": null, 1864 | "uploader": { 1865 | "login": "ralphtheninja", 1866 | "id": 308049, 1867 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1868 | "gravatar_id": "", 1869 | "url": "https://api.github.com/users/ralphtheninja", 1870 | "html_url": "https://github.com/ralphtheninja", 1871 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1872 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1873 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1874 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1875 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1876 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1877 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1878 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1879 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1880 | "type": "User", 1881 | "site_admin": false 1882 | }, 1883 | "content_type": "application/gzip", 1884 | "state": "uploaded", 1885 | "size": 211541, 1886 | "download_count": 39, 1887 | "created_at": "2018-01-24T02:30:50Z", 1888 | "updated_at": "2018-01-24T02:30:55Z", 1889 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v59-win32-ia32.tar.gz" 1890 | }, 1891 | { 1892 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/5966690", 1893 | "id": 5966690, 1894 | "name": "a-native-module-v1.0.0-node-v59-win32-x64.tar.gz", 1895 | "label": null, 1896 | "uploader": { 1897 | "login": "ralphtheninja", 1898 | "id": 308049, 1899 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1900 | "gravatar_id": "", 1901 | "url": "https://api.github.com/users/ralphtheninja", 1902 | "html_url": "https://github.com/ralphtheninja", 1903 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1904 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1905 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1906 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1907 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1908 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1909 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1910 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1911 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1912 | "type": "User", 1913 | "site_admin": false 1914 | }, 1915 | "content_type": "application/gzip", 1916 | "state": "uploaded", 1917 | "size": 247255, 1918 | "download_count": 42, 1919 | "created_at": "2018-01-24T02:30:51Z", 1920 | "updated_at": "2018-01-24T02:30:56Z", 1921 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v59-win32-x64.tar.gz" 1922 | }, 1923 | { 1924 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/7000166", 1925 | "id": 7000166, 1926 | "name": "a-native-module-v1.0.0-node-v64-darwin-x64.tar.gz", 1927 | "label": null, 1928 | "uploader": { 1929 | "login": "ralphtheninja", 1930 | "id": 308049, 1931 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1932 | "gravatar_id": "", 1933 | "url": "https://api.github.com/users/ralphtheninja", 1934 | "html_url": "https://github.com/ralphtheninja", 1935 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1936 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1937 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1938 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1939 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1940 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1941 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1942 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1943 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1944 | "type": "User", 1945 | "site_admin": false 1946 | }, 1947 | "content_type": "application/gzip", 1948 | "state": "uploaded", 1949 | "size": 169844, 1950 | "download_count": 0, 1951 | "created_at": "2018-04-27T16:18:50Z", 1952 | "updated_at": "2018-04-27T16:18:52Z", 1953 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v64-darwin-x64.tar.gz" 1954 | }, 1955 | { 1956 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/7000167", 1957 | "id": 7000167, 1958 | "name": "a-native-module-v1.0.0-node-v64-linux-x64.tar.gz", 1959 | "label": null, 1960 | "uploader": { 1961 | "login": "ralphtheninja", 1962 | "id": 308049, 1963 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1964 | "gravatar_id": "", 1965 | "url": "https://api.github.com/users/ralphtheninja", 1966 | "html_url": "https://github.com/ralphtheninja", 1967 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 1968 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 1969 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 1970 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 1971 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 1972 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 1973 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 1974 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 1975 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 1976 | "type": "User", 1977 | "site_admin": false 1978 | }, 1979 | "content_type": "application/gzip", 1980 | "state": "uploaded", 1981 | "size": 212858, 1982 | "download_count": 1, 1983 | "created_at": "2018-04-27T16:18:51Z", 1984 | "updated_at": "2018-04-27T16:18:53Z", 1985 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v64-linux-x64.tar.gz" 1986 | }, 1987 | { 1988 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/7000168", 1989 | "id": 7000168, 1990 | "name": "a-native-module-v1.0.0-node-v64-win32-ia32.tar.gz", 1991 | "label": null, 1992 | "uploader": { 1993 | "login": "ralphtheninja", 1994 | "id": 308049, 1995 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 1996 | "gravatar_id": "", 1997 | "url": "https://api.github.com/users/ralphtheninja", 1998 | "html_url": "https://github.com/ralphtheninja", 1999 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 2000 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 2001 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 2002 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 2003 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 2004 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 2005 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 2006 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 2007 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 2008 | "type": "User", 2009 | "site_admin": false 2010 | }, 2011 | "content_type": "application/gzip", 2012 | "state": "uploaded", 2013 | "size": 209581, 2014 | "download_count": 0, 2015 | "created_at": "2018-04-27T16:18:51Z", 2016 | "updated_at": "2018-04-27T16:18:54Z", 2017 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v64-win32-ia32.tar.gz" 2018 | }, 2019 | { 2020 | "url": "https://api.github.com/repos/ralphtheninja/a-native-module/releases/assets/7000169", 2021 | "id": 7000169, 2022 | "name": "a-native-module-v1.0.0-node-v64-win32-x64.tar.gz", 2023 | "label": null, 2024 | "uploader": { 2025 | "login": "ralphtheninja", 2026 | "id": 308049, 2027 | "avatar_url": "https://avatars2.githubusercontent.com/u/308049?v=4", 2028 | "gravatar_id": "", 2029 | "url": "https://api.github.com/users/ralphtheninja", 2030 | "html_url": "https://github.com/ralphtheninja", 2031 | "followers_url": "https://api.github.com/users/ralphtheninja/followers", 2032 | "following_url": "https://api.github.com/users/ralphtheninja/following{/other_user}", 2033 | "gists_url": "https://api.github.com/users/ralphtheninja/gists{/gist_id}", 2034 | "starred_url": "https://api.github.com/users/ralphtheninja/starred{/owner}{/repo}", 2035 | "subscriptions_url": "https://api.github.com/users/ralphtheninja/subscriptions", 2036 | "organizations_url": "https://api.github.com/users/ralphtheninja/orgs", 2037 | "repos_url": "https://api.github.com/users/ralphtheninja/repos", 2038 | "events_url": "https://api.github.com/users/ralphtheninja/events{/privacy}", 2039 | "received_events_url": "https://api.github.com/users/ralphtheninja/received_events", 2040 | "type": "User", 2041 | "site_admin": false 2042 | }, 2043 | "content_type": "application/gzip", 2044 | "state": "uploaded", 2045 | "size": 244128, 2046 | "download_count": 0, 2047 | "created_at": "2018-04-27T16:18:51Z", 2048 | "updated_at": "2018-04-27T16:18:55Z", 2049 | "browser_download_url": "https://github.com/ralphtheninja/a-native-module/releases/download/v1.0.0/a-native-module-v1.0.0-node-v64-win32-x64.tar.gz" 2050 | } 2051 | ], 2052 | "tarball_url": "https://api.github.com/repos/ralphtheninja/a-native-module/tarball/v1.0.0", 2053 | "zipball_url": "https://api.github.com/repos/ralphtheninja/a-native-module/zipball/v1.0.0", 2054 | "body": "This is a fake release used to test `prebuild-install`.\r\n" 2055 | } 2056 | ] 2057 | -------------------------------------------------------------------------------- /test/skip-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const path = require('path') 3 | const exec = require('child_process').exec 4 | const execFileSync = require('child_process').execFileSync 5 | const fs = require('fs') 6 | const tempy = require('tempy') // Locked to 0.2.1 for node 6 support 7 | const cleanEnv = require('./util/clean-env') 8 | const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm' 9 | 10 | test('skips download in git dependency', function (t) { 11 | // We're not testing this flag. Just that we do hit the code paths before it 12 | run(t, 'git', '--build-from-source', function (logs) { 13 | t.is(logs.pop(), 'prebuild-install info install installing from git repository, skipping download.') 14 | t.end() 15 | }) 16 | }) 17 | 18 | test('does not skip download in normal dependency', function (t) { 19 | // We're not testing this flag. Just that we don't hit the code paths before it 20 | run(t, 'tarball', '--build-from-source', function (logs) { 21 | t.is(logs.pop(), 'prebuild-install info install --build-from-source specified, not attempting download.') 22 | t.end() 23 | }) 24 | }) 25 | 26 | test('does not skip download in standalone package', function (t) { 27 | // We're not testing this flag. Just that we don't hit the code paths before it 28 | run(t, 'standalone', '--build-from-source', function (logs) { 29 | t.is(logs.pop(), 'prebuild-install info install --build-from-source specified, not attempting download.') 30 | t.end() 31 | }) 32 | }) 33 | 34 | function run (t, mode, args, cb) { 35 | const addon = tempy.directory() 36 | let cwd = addon 37 | 38 | writePackage(addon, { 39 | name: 'addon', 40 | version: '1.0.0', 41 | scripts: { 42 | install: 'node ' + path.resolve(__dirname, '..', 'bin.js') + ' ' + args + ' || exit 0' 43 | } 44 | }) 45 | 46 | if (mode !== 'standalone') { 47 | // Install as dependency of an app 48 | cwd = tempy.directory() 49 | 50 | writePackage(cwd, { 51 | name: 'app', 52 | dependencies: { 53 | addon: mode === 'git' ? prepareGit(addon) : prepareTarball(addon) 54 | } 55 | }) 56 | } 57 | 58 | const env = Object.assign(cleanEnv(process.env), { 59 | // We shouldn't hit npm or github 60 | npm_config_registry: 'http://localhost:1234', 61 | npm_config_addon_binary_host: 'http://localhost:1234', 62 | npm_config_prefer_offline: 'true', 63 | npm_config_audit: 'false', 64 | npm_config_foreground_scripts: true, 65 | npm_config_loglevel: 'info' 66 | }) 67 | 68 | exec(npm + ' install', { cwd, env }, function (err, stdout, stderr) { 69 | t.ifError(err, 'no install error') 70 | cb(logs(stderr)) 71 | }) 72 | } 73 | 74 | function writePackage (cwd, pkg) { 75 | fs.writeFileSync(path.join(cwd, 'package.json'), JSON.stringify(pkg)) 76 | } 77 | 78 | function prepareGit (cwd) { 79 | execFileSync('git', ['init', '.'], { cwd, stdio: 'ignore' }) 80 | execFileSync('git', ['config', 'user.name', 'test'], { cwd, stdio: 'ignore' }) 81 | execFileSync('git', ['config', 'user.email', 'test@localhost'], { cwd, stdio: 'ignore' }) 82 | execFileSync('git', ['add', 'package.json'], { cwd, stdio: 'ignore' }) 83 | execFileSync('git', ['commit', '-m', 'test'], { cwd, stdio: 'ignore' }) 84 | 85 | if (process.platform === 'win32' && npmVersion() >= 7) { 86 | // Otherwise results in invalid url error 87 | return 'git+file:///' + cwd 88 | } 89 | 90 | return 'git+file://' + cwd 91 | } 92 | 93 | function npmVersion () { 94 | return parseInt(execFileSync(npm, ['-v']).toString()) 95 | } 96 | 97 | function prepareTarball (cwd) { 98 | // Packs to -.tgz 99 | execFileSync(npm, ['pack'], { cwd, stdio: 'ignore' }) 100 | 101 | return 'file:' + path.join(cwd, 'addon-1.0.0.tgz') 102 | } 103 | 104 | function logs (stderr) { 105 | return (stderr || '').split(/\r?\n/).filter(isOurs).map(stripPrefix) 106 | } 107 | 108 | function isOurs (line) { 109 | return /^(npm ERR! )?prebuild-install /.test(line) 110 | } 111 | 112 | function stripPrefix (line) { 113 | return line.replace(/^npm ERR! /, '') 114 | } 115 | -------------------------------------------------------------------------------- /test/util-test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const fs = require('fs') 3 | const home = require('os').homedir 4 | const util = require('../util') 5 | const path = require('path') 6 | 7 | test('prebuildCache() for different environments', function (t) { 8 | const NPMCACHE = process.env.npm_config_cache 9 | delete process.env.npm_config_cache 10 | const APPDATA = process.env.APPDATA = 'somepathhere' 11 | t.equal(util.prebuildCache(), path.join(APPDATA, '/npm-cache/_prebuilds'), 'APPDATA set') 12 | delete process.env.APPDATA 13 | t.equal(util.prebuildCache(), path.join(home(), '/.npm/_prebuilds'), 'APPDATA not set') 14 | process.env.npm_config_cache = NPMCACHE 15 | t.equal(util.prebuildCache(), path.join(NPMCACHE, '/_prebuilds'), 'npm_config_cache set') 16 | t.end() 17 | }) 18 | 19 | test('cachedPrebuild() converts url to valid characters', function (t) { 20 | const url = 'https://github.com/level/leveldown/releases/download/v1.4.0/leveldown-v1.4.0-node-v14-linux-x64.tar.gz' 21 | const tail = 'https-github.com-level-leveldown-releases-download-v1.4.0-leveldown-v1.4.0-node-v14-linux-x64.tar.gz' 22 | const cached = util.cachedPrebuild(url) 23 | t.ok(cached.indexOf(tail)) 24 | t.end() 25 | }) 26 | 27 | test('tempFile() ends with pid and random number', function (t) { 28 | const url = 'https://github.com/level/leveldown/releases/download/v1.4.0/leveldown-v1.4.0-node-v14-linux-x64.tar.gz' 29 | const cached = util.cachedPrebuild(url) 30 | const tempFile = util.tempFile(cached) 31 | const regexp = /(\S+)\.(\d+)-([a-f0-9]+)\.tmp$/gi 32 | const match = regexp.exec(tempFile) 33 | t.ok(match, 'matches') 34 | t.equal(match[1], cached, 'starts with cached file name') 35 | fs.access(tempFile, fs.R_OK | fs.W_OK, function (err) { 36 | t.ok(err && err.code === 'ENOENT', 'file should not exist yet') 37 | t.end() 38 | }) 39 | }) 40 | 41 | test('urlTemplate() returns different templates based on pkg and rc', function (t) { 42 | const o1 = { download: 'd0000d' } 43 | const t1 = util.urlTemplate(o1) 44 | t.equal(t1, 'd0000d', 'template based on --download ') 45 | const o2 = { 46 | pkg: { binary: { host: 'http://foo.com' } } 47 | } 48 | const t2 = util.urlTemplate(o2) 49 | t.equal(t2, 'http://foo.com/{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz', 'template based on pkg.binary properties') 50 | const o3 = { 51 | pkg: { binary: { host: 'http://foo.com' } }, 52 | download: true 53 | } 54 | const t3 = util.urlTemplate(o3) 55 | t.equal(t3, t2, 'pkg: {} takes precedence over --download') 56 | const o4 = { 57 | pkg: { binary: { host: 'http://foo.com' } }, 58 | download: 'd0000d' 59 | } 60 | const t4 = util.urlTemplate(o4) 61 | t.equal(t4, t1, '--download always goes first') 62 | const o5 = { 63 | pkg: { binary: { host: 'http://foo.com', remote_path: 'w00t' } } 64 | } 65 | const t5 = util.urlTemplate(o5) 66 | t.equal(t5, 'http://foo.com/w00t/{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz', 'pkg.binary.remote_path is added after host, default format') 67 | const o6 = { 68 | pkg: { 69 | binary: { 70 | host: 'http://foo.com', 71 | remote_path: 'w00t', 72 | package_name: '{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz' 73 | } 74 | } 75 | } 76 | const t6 = util.urlTemplate(o6) 77 | t.equal(t6, 'http://foo.com/w00t/{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz', 'pkg.binary.package_name is added after host and remote_path, custom format') 78 | const o7 = { 79 | pkg: require('../package.json'), 80 | download: true 81 | } 82 | delete o7.binary 83 | let envProperty = 'npm_config_' + o7.pkg.name.replace(/[^a-zA-Z0-9]/g, '_') + '_binary_host' 84 | process.env[envProperty] = 'http://overriden-url.com/overriden-path' 85 | const t7 = util.urlTemplate(o7) 86 | delete process.env[envProperty] 87 | t.equal(t7, 'http://overriden-url.com/overriden-path/{tag_prefix}{version}/{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz', '--download with host mirror override') 88 | const o8 = { 89 | pkg: Object.assign({}, require('../package.json'), { 90 | binary: { 91 | host: 'http://foo.com', 92 | remote_path: 'w00t', 93 | package_name: '{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz' 94 | } 95 | }), 96 | download: true 97 | } 98 | envProperty += '_mirror' 99 | process.env[envProperty] = 'http://overriden-url.com/overriden-path' 100 | const t8 = util.urlTemplate(o8) 101 | delete process.env[envProperty] 102 | t.equal(t8, 'http://overriden-url.com/overriden-path/{tag_prefix}{version}/{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz', '--download with binary defined and host mirror override') 103 | const o9 = { pkg: require('../package.json'), download: true } 104 | const t9 = util.urlTemplate(o9) 105 | t.equal(t9, 'https://github.com/prebuild/prebuild-install/releases/download/{tag_prefix}{version}/{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz', '--download with no arguments, no pkg.binary, no host mirror, default format') 106 | t.end() 107 | }) 108 | 109 | test('urlTemplate() with pkg.binary cleans up leading ./ or / and trailing /', function (t) { 110 | const expected = 'http://foo.com/w00t/{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz' 111 | const o = { 112 | pkg: { 113 | binary: { 114 | host: 'http://foo.com/', 115 | remote_path: '/w00t', 116 | package_name: '/{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz' 117 | } 118 | } 119 | } 120 | t.equal(util.urlTemplate(o), expected) 121 | o.pkg.binary = { 122 | host: 'http://foo.com/', 123 | remote_path: './w00t/', 124 | package_name: './{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz' 125 | } 126 | t.equal(util.urlTemplate(o), expected) 127 | o.pkg.binary = { 128 | host: 'http://foo.com/', 129 | remote_path: 'w00t/', 130 | package_name: '{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz/' 131 | } 132 | t.equal(util.urlTemplate(o), expected) 133 | o.pkg.binary = { 134 | host: 'http://foo.com', 135 | remote_path: './w00t', 136 | package_name: '/{name}-{major}.{minor}-{runtime}-v{abi}-{platform}-{arch}.tar.gz/' 137 | } 138 | t.equal(util.urlTemplate(o), expected) 139 | t.end() 140 | }) 141 | 142 | test('getDownloadUrl() expands template to correct values', function (t) { 143 | const abi = process.versions.modules 144 | const o1 = { 145 | pkg: { 146 | name: 'a-native-module', 147 | version: 'x.y.z-alpha5', 148 | binary: { 149 | host: 'https://foo.com', 150 | module_name: 'a-native-module-bindings', 151 | package_name: '{name}-{package_name}-{version}-{major}-{minor}-{patch}-{prerelease}-{abi}-{node_abi}-{platform}-{arch}-{configuration}-{module_name}' 152 | } 153 | }, 154 | platform: 'coolplatform', 155 | arch: 'futureplatform' 156 | } 157 | const url1 = util.getDownloadUrl(o1) 158 | t.equal(url1, 'https://foo.com/a-native-module-a-native-module-x.y.z-alpha5-x-y-z-alpha5-alpha5-' + abi + '-' + abi + '-coolplatform-futureplatform-Release-a-native-module-bindings', 'weird url but testing everything is propagated, with prerelease and Release') 159 | const o2 = { 160 | pkg: { 161 | name: 'a-native-module', 162 | version: 'x.y.z+beta77', 163 | binary: { 164 | host: 'https://foo.com', 165 | module_name: 'a-native-module-bindings', 166 | package_name: '{name}-{package_name}-{version}-{major}-{minor}-{patch}-{build}-{abi}-{node_abi}-{platform}-{arch}-{configuration}-{module_name}' 167 | } 168 | }, 169 | platform: 'coolplatform', 170 | arch: 'futureplatform', 171 | debug: true 172 | } 173 | const url2 = util.getDownloadUrl(o2) 174 | t.equal(url2, 'https://foo.com/a-native-module-a-native-module-x.y.z+beta77-x-y-z+beta77-beta77-' + abi + '-' + abi + '-coolplatform-futureplatform-Debug-a-native-module-bindings', 'weird url but testing everything is propagated, with build and Debug') 175 | const o3 = { 176 | pkg: { 177 | name: '@scope/a-native-module', 178 | version: 'x.y.z+beta77', 179 | binary: { 180 | host: 'https://foo.com', 181 | module_name: 'a-native-module-bindings', 182 | package_name: '{name}-{package_name}-{version}-{major}-{minor}-{patch}-{build}-{abi}-{node_abi}-{platform}-{arch}-{configuration}-{module_name}' 183 | } 184 | }, 185 | platform: 'coolplatform', 186 | arch: 'futureplatform', 187 | debug: true 188 | } 189 | const url3 = util.getDownloadUrl(o3) 190 | t.equal(url3, url2, 'scope does not matter for download url') 191 | const o4 = { 192 | pkg: { 193 | name: '@scope-with.special~chars_/a-native-module', 194 | version: 'x.y.z+beta77', 195 | binary: { 196 | host: 'https://foo.com', 197 | module_name: 'a-native-module-bindings', 198 | package_name: '{name}-{package_name}-{version}-{major}-{minor}-{patch}-{build}-{abi}-{node_abi}-{platform}-{arch}-{configuration}-{module_name}' 199 | } 200 | }, 201 | platform: 'coolplatform', 202 | arch: 'futureplatform', 203 | debug: true 204 | } 205 | const url4 = util.getDownloadUrl(o4) 206 | t.equal(url4, url2, 'scope with special characters does not matter for download url') 207 | t.end() 208 | }) 209 | 210 | test('localPrebuild', function (t) { 211 | const envProp = 'npm_config_a_native_module_local_prebuilds' 212 | const basename = 'a-native-module-v1.4.0-node-v14-linux-x64.tar.gz' 213 | const url = 'https://github.com/a-native-module/a-native-module/releases/download/v1.4.0/' + basename 214 | const o1 = { 215 | pkg: { 216 | name: 'a-native-module' 217 | } 218 | } 219 | const path1 = util.localPrebuild(url, o1) 220 | t.equal(path1, path.join('prebuilds', basename)) 221 | const o2 = { 222 | pkg: { 223 | name: 'a-native-module' 224 | }, 225 | 'local-prebuilds': path.join('', 'path', 'to', 'prebuilds') 226 | } 227 | const path2 = util.localPrebuild(url, o2) 228 | t.equal(path2, path.join(o2['local-prebuilds'], basename), 'opts overrides default') 229 | const envPrefix = path.join('', 'overriden', 'path', 'to', 'prebuilds') 230 | process.env[envProp] = envPrefix 231 | const path3 = util.localPrebuild(url, o2) 232 | t.equal(path3, path.join(envPrefix, basename), 'env overrides opts') 233 | t.end() 234 | }) 235 | -------------------------------------------------------------------------------- /test/util/clean-env.js: -------------------------------------------------------------------------------- 1 | const hasOwnProperty = Object.prototype.hasOwnProperty 2 | 3 | module.exports = function (env) { 4 | const clean = {} 5 | 6 | for (const k in env) { 7 | if (!hasOwnProperty.call(env, k)) continue 8 | if (/^npm_/i.test(k)) continue 9 | 10 | clean[k] = env[k] 11 | } 12 | 13 | return clean 14 | } 15 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const github = require('github-from-package') 3 | const home = require('os').homedir 4 | const crypto = require('crypto') 5 | const expandTemplate = require('expand-template')() 6 | 7 | function getDownloadUrl (opts) { 8 | const pkgName = opts.pkg.name.replace(/^@[a-zA-Z0-9_\-.~]+\//, '') 9 | return expandTemplate(urlTemplate(opts), { 10 | name: pkgName, 11 | package_name: pkgName, 12 | version: opts.pkg.version, 13 | major: opts.pkg.version.split('.')[0], 14 | minor: opts.pkg.version.split('.')[1], 15 | patch: opts.pkg.version.split('.')[2], 16 | prerelease: opts.pkg.version.split('-')[1], 17 | build: opts.pkg.version.split('+')[1], 18 | abi: opts.abi || process.versions.modules, 19 | node_abi: process.versions.modules, 20 | runtime: opts.runtime || 'node', 21 | platform: opts.platform, 22 | arch: opts.arch, 23 | libc: opts.libc || '', 24 | configuration: (opts.debug ? 'Debug' : 'Release'), 25 | module_name: opts.pkg.binary && opts.pkg.binary.module_name, 26 | tag_prefix: opts['tag-prefix'] 27 | }) 28 | } 29 | 30 | function getApiUrl (opts) { 31 | return github(opts.pkg).replace('github.com', 'api.github.com/repos') + '/releases' 32 | } 33 | 34 | function getAssetUrl (opts, assetId) { 35 | return getApiUrl(opts) + '/assets/' + assetId 36 | } 37 | 38 | function urlTemplate (opts) { 39 | if (typeof opts.download === 'string') { 40 | return opts.download 41 | } 42 | 43 | const packageName = '{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz' 44 | const hostMirrorUrl = getHostMirrorUrl(opts) 45 | 46 | if (hostMirrorUrl) { 47 | return hostMirrorUrl + '/{tag_prefix}{version}/' + packageName 48 | } 49 | 50 | if (opts.pkg.binary && opts.pkg.binary.host) { 51 | return [ 52 | opts.pkg.binary.host, 53 | opts.pkg.binary.remote_path, 54 | opts.pkg.binary.package_name || packageName 55 | ].map(function (path) { 56 | return trimSlashes(path) 57 | }).filter(Boolean).join('/') 58 | } 59 | 60 | return github(opts.pkg) + '/releases/download/{tag_prefix}{version}/' + packageName 61 | } 62 | 63 | function getEnvPrefix (pkgName) { 64 | return 'npm_config_' + (pkgName || '').replace(/[^a-zA-Z0-9]/g, '_').replace(/^_/, '') 65 | } 66 | 67 | function getHostMirrorUrl (opts) { 68 | const propName = getEnvPrefix(opts.pkg.name) + '_binary_host' 69 | return process.env[propName] || process.env[propName + '_mirror'] 70 | } 71 | 72 | function trimSlashes (str) { 73 | if (str) return str.replace(/^\.\/|^\/|\/$/g, '') 74 | } 75 | 76 | function cachedPrebuild (url) { 77 | const digest = crypto.createHash('sha512').update(url).digest('hex').slice(0, 6) 78 | return path.join(prebuildCache(), digest + '-' + path.basename(url).replace(/[^a-zA-Z0-9.]+/g, '-')) 79 | } 80 | 81 | function npmCache () { 82 | const env = process.env 83 | return env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(home(), '.npm')) 84 | } 85 | 86 | function prebuildCache () { 87 | return path.join(npmCache(), '_prebuilds') 88 | } 89 | 90 | function tempFile (cached) { 91 | return cached + '.' + process.pid + '-' + Math.random().toString(16).slice(2) + '.tmp' 92 | } 93 | 94 | function packageOrigin (env, pkg) { 95 | // npm <= 6: metadata is stored on disk in node_modules 96 | if (pkg._from) { 97 | return pkg._from 98 | } 99 | 100 | // npm 7: metadata is exposed to environment by arborist 101 | if (env.npm_package_from) { 102 | // NOTE: seems undefined atm (npm 7.0.2) 103 | return env.npm_package_from 104 | } 105 | 106 | if (env.npm_package_resolved) { 107 | // NOTE: not sure about the difference with _from, but it's all we have 108 | return env.npm_package_resolved 109 | } 110 | } 111 | 112 | function localPrebuild (url, opts) { 113 | const propName = getEnvPrefix(opts.pkg.name) + '_local_prebuilds' 114 | const prefix = process.env[propName] || opts['local-prebuilds'] || 'prebuilds' 115 | return path.join(prefix, path.basename(url)) 116 | } 117 | 118 | const noopLogger = { 119 | http: function () {}, 120 | silly: function () {}, 121 | debug: function () {}, 122 | info: function () {}, 123 | warn: function () {}, 124 | error: function () {}, 125 | critical: function () {}, 126 | alert: function () {}, 127 | emergency: function () {}, 128 | notice: function () {}, 129 | verbose: function () {}, 130 | fatal: function () {} 131 | } 132 | 133 | exports.getDownloadUrl = getDownloadUrl 134 | exports.getApiUrl = getApiUrl 135 | exports.getAssetUrl = getAssetUrl 136 | exports.urlTemplate = urlTemplate 137 | exports.cachedPrebuild = cachedPrebuild 138 | exports.localPrebuild = localPrebuild 139 | exports.prebuildCache = prebuildCache 140 | exports.npmCache = npmCache 141 | exports.tempFile = tempFile 142 | exports.packageOrigin = packageOrigin 143 | exports.noopLogger = noopLogger 144 | --------------------------------------------------------------------------------