Skip to content

Commit d45cf4f

Browse files
committed
Initial commit
0 parents  commit d45cf4f

23 files changed

+3874
-0
lines changed

.denolint.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"files": {
3+
"exclude": [
4+
"dist"
5+
]
6+
},
7+
"rules": {
8+
"tags": ["recommended"]
9+
}
10+
}

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true

.github/workflows/ci.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Build and Test or Release
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
tags-ignore:
8+
- '**'
9+
paths-ignore:
10+
- '**/*.md'
11+
- LICENSE
12+
pull_request:
13+
14+
jobs:
15+
16+
linux:
17+
runs-on: ubuntu-latest
18+
outputs:
19+
changed: ${{ steps.bump.outputs.bumped }}
20+
changed-files: ${{ steps.bump.outputs.changed-files }}
21+
steps:
22+
- name: Checkout Sources
23+
uses: actions/checkout@v4
24+
- name: Install PNPM
25+
uses: pnpm/action-setup@v2
26+
with:
27+
version: latest
28+
- name: Install Node
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: 'lts/*'
32+
cache: 'pnpm'
33+
- name: Install Dependencies
34+
run: pnpm i --frozen-lockfile --no-verify-store-integrity
35+
- name: Build
36+
run: npm run build
37+
- name: Test
38+
run: npm test
39+
40+
macos:
41+
runs-on: macos-latest
42+
steps:
43+
- name: Checkout Sources
44+
uses: actions/checkout@v4
45+
- name: Install PNPM
46+
uses: pnpm/action-setup@v2
47+
with:
48+
version: latest
49+
- name: Install Node
50+
uses: actions/setup-node@v4
51+
with:
52+
node-version: 'lts/*'
53+
cache: 'pnpm'
54+
- name: Install Dependencies
55+
run: pnpm i --frozen-lockfile --no-verify-store-integrity
56+
- name: Build
57+
run: npm run build
58+
- name: Test
59+
run: |
60+
npm run test-cover
61+
./test.sh
62+
- name: Coverage
63+
uses: codecov/codecov-action@v2
64+
65+
windows:
66+
runs-on: windows-latest
67+
steps:
68+
- name: Checkout Sources
69+
uses: actions/checkout@v4
70+
- name: Install PNPM
71+
uses: pnpm/action-setup@v2
72+
with:
73+
version: latest
74+
- name: Install Node
75+
uses: actions/setup-node@v4
76+
with:
77+
node-version: 'lts/*'
78+
cache: 'pnpm'
79+
- name: Install Dependencies
80+
run: pnpm i --frozen-lockfile --no-verify-store-integrity
81+
- name: Build
82+
run: npm run build
83+
- name: Test
84+
run: npm test
85+
86+
release:
87+
if: ${{ github.ref_name == 'master' }}
88+
needs:
89+
- linux
90+
- macos
91+
- windows
92+
runs-on: ubuntu-latest
93+
steps:
94+
- name: Checkout Sources
95+
uses: actions/checkout@v4
96+
- name: Install PNPM
97+
uses: pnpm/action-setup@v2
98+
with:
99+
version: latest
100+
- name: Install Node
101+
uses: actions/setup-node@v4
102+
with:
103+
node-version: 'lts/*'
104+
cache: 'pnpm'
105+
registry-url: 'https://registry.npmjs.org'
106+
- name: Install Dependencies
107+
run: pnpm i --frozen-lockfile --no-verify-store-integrity
108+
- name: Build
109+
run: npm run build
110+
- name: Publish
111+
uses: cycjimmy/semantic-release-action@v4
112+
with:
113+
branches: master
114+
env:
115+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
coverage
3+
dist
4+
node_modules

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"files.exclude": {
3+
"coverage": true,
4+
"dist": true,
5+
"node_modules": true
6+
}
7+
}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changes
2+
3+
## 2023-10-26 (0.0.1)
4+
5+
Initial release

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Ferdinand Prantl
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 all
13+
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 THE
21+
SOFTWARE.

README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Grab GitHub Release
2+
3+
[![Latest version](https://img.shields.io/npm/v/grab-github-release)
4+
![Dependency status](https://img.shields.io/librariesio/release/npm/grab-github-release)
5+
](https://www.npmjs.com/package/grab-github-release)
6+
[![Coverage](https://codecov.io/gh/prantlf/grab-github-release/branch/master/graph/badge.svg)](https://codecov.io/gh/prantlf/grab-github-release)
7+
8+
Downloads and optionally unpacks an archive from GitHub release assets for the current platform.
9+
10+
## Synopsis
11+
12+
```js
13+
import grab from 'grab-github-release'
14+
15+
try {
16+
const repository = 'prantlf/v-jsonlint'
17+
const platformSuffixes = {
18+
darwin: 'macos',
19+
win32: 'windows'
20+
}
21+
// downloads and unpacks the jsonlint executable to the current directory
22+
await grab({ repository, platformSuffixes, unpackExecutable: true })
23+
} catch(err) {
24+
console.error(err.message)
25+
process.exitCode = 1
26+
}
27+
```
28+
29+
## Installation
30+
31+
This package can be installed globally, if you want to use the `grab-github-release` script (or the `ggr` alias). You can install it during the first usage with `npx` too:
32+
33+
```sh
34+
$ npm i -g grab-github-release
35+
$ npx grab-github-release ...
36+
```
37+
38+
This package can be installed locally too, if you want to use it programmatically:
39+
40+
```sh
41+
$ npm i -g grab-github-release
42+
$ npx grab-github-release ...
43+
```
44+
45+
Make sure, that you use [Node.js] version 18 or newer.
46+
47+
## Command-line Usage
48+
49+
Usage: [options]
50+
51+
Options:
52+
-r|--repository <repository> GitHub repository formatted "owner/name"
53+
-i|--version-spec <semver> semantic version specifier or "latest"
54+
-n|--name <file-name> archive name without the platform suffix
55+
-p|--platform-suffixes <map> unpack the executable and remove the archive
56+
-e|--unpack-exe unpack the executable and remove the archive
57+
-v|--verbose prints extra information on the console
58+
-V|--version print version number and exit
59+
-h|--help print usage instructions and exit
60+
61+
The version specifier is "latest" by default. The file name will be inferred
62+
from the first archive asset found for the current platform, if not specified.
63+
64+
Examples:
65+
$ grab-github-release -r prantlf/v-jsonlint -p darwin=macos,win32=windows -u
66+
$ grab-github-release -r prantlf/v-jsonlint -i >=0.0.6
67+
68+
## API
69+
70+
```ts
71+
// map where keys are Node.js platform names and values are their replacements
72+
// to be used in names of archive looked for among GitHub release assets
73+
type PlatformSuffixes = Record<string, string>
74+
75+
interface GrabOptions {
76+
// GitHub repository formatted "owner/name", mandatory
77+
repository: string
78+
// semantic version specifier or "latest"; defaults to "latest", if unspecified
79+
version?: string
80+
// archive name without the platform and architecture suffix
81+
// and without the ".zip" extension as well
82+
name?: string
83+
// archive name without the platform suffix; if not specified, it will be
84+
// inferred from the first archive asset found for the current platform
85+
platformSuffixes?: PlatformSuffixes
86+
// unpack the executable and remove the archive
87+
unpackExecutable?: boolean
88+
}
89+
90+
interface GrabResult {
91+
// actual version number as specified or picked from the list of releases
92+
version: string
93+
// downloaded archive name, if not removed (and the executable not unpacked)
94+
archive?: string
95+
// executable file name, if it was unpacked (and the archive removed)
96+
executable?: string
97+
}
98+
99+
// downloads and optionally unpacks an archive from GitHub release assets
100+
// for the current platform
101+
export default function grab(options: GrabOptions): GrabResult
102+
```
103+
104+
## Contributing
105+
106+
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using Grunt.
107+
108+
## License
109+
110+
Copyright (c) 2023 Ferdinand Prantl
111+
112+
Licensed under the MIT license.
113+
114+
[Node.js]: http://nodejs.org/

bin/grab-github-release.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env node
2+
3+
import grab from '../dist/index.mjs'
4+
5+
function help() {
6+
console.log(`Generates a unique build number with a human-readable build time.
7+
8+
Usage: [options]
9+
10+
Options:
11+
-r|--repository <repository> GitHub repository formatted "owner/name"
12+
-i|--version-spec <semver> semantic version specifier or "latest"
13+
-n|--name <file-name> archive name without the platform suffix
14+
-p|--platform-suffixes <map> unpack the executable and remove the archive
15+
-e|--unpack-exe unpack the executable and remove the archive
16+
-v|--verbose prints extra information on the console
17+
-V|--version print version number and exit
18+
-h|--help print usage instructions and exit
19+
20+
The version specifier is "latest" by default. The file name will be inferred
21+
from the first archive asset found for the current platform, if not specified.
22+
23+
Examples:
24+
$ grab-github-release -r prantlf/v-jsonlint -p darwin=macos,win32=windows -u
25+
$ grab-github-release -r prantlf/v-jsonlint -i >=0.0.6`)
26+
}
27+
28+
function fail(message) {
29+
console.error(message)
30+
process.exit(1)
31+
}
32+
33+
const { argv } = process
34+
let repository, version, name, platformSuffixes, unpackExecutable, verbose
35+
36+
for (let i = 2, l = argv.length; i < l; ++i) {
37+
const arg = argv[i]
38+
const match = /^(-|--)(no-)?([a-zA-Z][-a-zA-Z]*)(?:=(.*))?$/.exec(arg)
39+
if (match) {
40+
const parseArg = async (arg, flag) => {
41+
let entries
42+
switch (arg) {
43+
case 'r': case 'repository':
44+
repository = match[4] || argv[++i]
45+
return
46+
case 'i': case 'version':
47+
version = match[4] || argv[++i]
48+
return
49+
case 'n': case 'name':
50+
name = match[4] || argv[++i]
51+
return
52+
case 'p': case 'platform-suffixes':
53+
entries = match[4] || argv[++i]
54+
if (!entries) fail('missing platform suffix map')
55+
if (!platformSuffixes) platformSuffixes = {}
56+
for (const entry of entries.trim().split(',')) {
57+
const [key, val] = entry.trim().split('=')
58+
platformSuffixes[key.trim()] = val.trim()
59+
}
60+
return
61+
case 'e': case 'unpack-exe':
62+
unpackExecutable = flag
63+
return
64+
case 'v': case 'verbose':
65+
verbose = flag
66+
return
67+
case 'V': case 'version-spec':
68+
{
69+
const { readFile } = await import('fs/promises')
70+
const { fileURLToPath } = await import('url')
71+
const { join, dirname } = await import('path')
72+
const pkg = join(dirname(fileURLToPath(import.meta.url)), '../package.json')
73+
console.log(JSON.parse(await readFile(pkg, 'utf8')).version)
74+
process.exit(0)
75+
}
76+
break
77+
case 'h': case 'help':
78+
help()
79+
process.exit(0)
80+
}
81+
fail(`unknown option: "${arg}"`)
82+
}
83+
if (match[1] === '-') {
84+
const flags = match[3].split('')
85+
for (const flag of flags) await parseArg(flag, true)
86+
} else {
87+
await parseArg(match[3], match[2] !== 'no-')
88+
}
89+
continue
90+
}
91+
fail(`unrecognized argument: "${arg}"`)
92+
}
93+
94+
if (!repository) {
95+
if (argv.length > 2) fail('missing repository')
96+
help()
97+
process.exit(0)
98+
}
99+
100+
try {
101+
await grab({ repository, version, name, platformSuffixes, unpackExecutable, verbose })
102+
} catch(err) {
103+
console.error(err.message)
104+
process.exitCode = 1
105+
}

0 commit comments

Comments
 (0)