Compare commits

...

450 Commits

Author SHA1 Message Date
AJ ONeal
8bb6dec5d8 chore(release): bump to v1.1.1 2024-01-02 16:07:22 -07:00
AJ ONeal
ca03de16c6 fix(builds-cacher): fallback to setting build.name from build.download 2024-01-02 16:01:43 -07:00
AJ ONeal
11e1bf94f0 fix(ci): add GITHUB_TOKEN to ENVs for running tests 2024-01-02 15:59:57 -07:00
AJ ONeal
8da2ff15b6 chore(release): bump to v1.1.0 2024-01-02 15:44:23 -07:00
AJ ONeal
8e62e12334 feat(webi): show latest project release version on error 2024-01-02 15:29:19 -07:00
AJ ONeal
f13d582749 ref(webi): show supported OSes, etc with space rather than comma 2024-01-02 15:29:19 -07:00
AJ ONeal
c6bb79648a fix(installer): accept 'ANYOS' if no explicit OS matches 2024-01-02 15:29:19 -07:00
AJ ONeal
22ba86dbed feat(installer): replace getReleases with new builds classifier 2024-01-02 15:29:18 -07:00
AJ ONeal
72c0cd5985 fix(webi): remove bogus ./webi/releases.js 2024-01-02 15:27:09 -07:00
AJ ONeal
eec6452769 ref(classify): parallelize release downloads to fetch *much* faster 2024-01-02 15:27:09 -07:00
AJ ONeal
541fdc565e feat: add builds classifier, and lint all builds 2024-01-02 15:27:09 -07:00
AJ ONeal
dc438ad1ba chore(ci): add git submodule 2024-01-02 15:27:09 -07:00
AJ ONeal
51b16ba53d feat: add build-classifier submodule 2024-01-02 15:27:09 -07:00
AJ ONeal
9bcf126df2 chore: add package.json.scripts.bump 2024-01-02 15:27:08 -07:00
AJ ONeal
3fa4b207e4 fix(flutter): use _filename to not generate extraneous terms from download url 2024-01-02 15:27:08 -07:00
AJ ONeal
539dd83ee8 fix(flutter): set full download string directly on build info 2024-01-02 12:27:43 -07:00
AJ ONeal
c11d22c9dd fix(go): set full download string directly on build info 2024-01-02 12:27:42 -07:00
AJ ONeal
7bb8040404 fix(ollama-darwin): filter out .app, manually set arch for universal 2023-12-28 02:04:26 -07:00
AJ ONeal
3ae143e48b fix(goreleaser): handle extraneous build term '1' 2023-12-28 02:04:26 -07:00
AJ ONeal
2cd242c945 fix(watchexec): remove 'cli-' prefix from literal version 2023-12-28 02:04:26 -07:00
AJ ONeal
5b32ecdf08 ref(shellcheck): remove superfluous target matching 2023-12-28 02:04:26 -07:00
AJ ONeal
6b25145795 fix(zoxide): remove incorrect arch detection 2023-12-28 02:04:26 -07:00
AJ ONeal
9a3cfbb573 fix(zig): filter out legacy armv6kz (RPi 1) one-off 2023-12-17 03:20:48 -07:00
AJ ONeal
50069182eb fix(pwsh): arch = 'musl' should be libc = 'musl' 2023-12-17 03:20:24 -07:00
AJ ONeal
546aee8fbb ref: releases.js => installers.js 2023-12-12 02:57:03 -07:00
AJ ONeal
7c61a19e20 ref(internal): packages.js => projects.js 2023-12-12 02:57:02 -07:00
AJ ONeal
e92081b08c fix(sd): update for v1 package structure 2023-12-12 02:56:40 -07:00
AJ ONeal
281c004445 ref(webi): show supported OSes, Arches, Libcs & Packages more clearly on error 2023-12-12 02:56:01 -07:00
AJ ONeal
e2300c6999 fix: fn_get_os for bootstrap & install 2023-12-12 01:58:58 -07:00
AJ ONeal
c116cb417f fix: remove extra / in doc url 2023-12-12 01:58:58 -07:00
AJ ONeal
c080b96fcc fix(git-tag): repo => _repo to distinguish from installers 2023-12-12 01:58:57 -07:00
AJ ONeal
e9a473d14a fix(git-tag): circumvent race condition on simultaneous duplicate git clone 2023-12-12 01:58:57 -07:00
AJ ONeal
3966d3adf8 fix(releases): bump timeout to 15s for uncached github requests 2023-12-12 01:44:11 -07:00
AJ ONeal
2fe6824472 chore: bump version to 1.2.8 2023-11-22 10:08:18 -07:00
AJ ONeal
4510a61cf0 fix(envman): split non-portable function.env => function.sh, function.fish 2023-11-22 10:05:12 -07:00
AJ ONeal
0da4b5d7cd chore: bump version to 1.2.7 2023-11-21 14:17:42 -07:00
AJ ONeal
948030b9cb ref: move shell integrations to their own functions 2023-11-21 13:56:15 -07:00
AJ ONeal
faaed1d2ec feat(completions): add --<option>s to the list 2023-11-21 13:56:15 -07:00
AJ ONeal
6875648daf feat(webi): save install options for shell completions 2023-11-21 13:56:15 -07:00
AJ ONeal
e8ba56e75f fix(zsh): must 'compinit' before 'compdef' 2023-11-21 13:56:15 -07:00
RubenRam
1f858bca4d feat(webi): add webi init option
Add shell package completion for bash, zsh and fish
2023-11-21 13:55:57 -07:00
AJ ONeal
f10be58d64 ref: move 'webi --info' to its own function 2023-11-21 13:52:56 -07:00
AJ ONeal
79ded46033 doc(webi-essentials): fix typo in sudo prompt 2023-11-21 13:35:21 -07:00
AJ ONeal
7eec48ea20 fix(posix): detect current/login shell even when rc file is absent 2023-11-21 11:28:17 -07:00
AJ ONeal
74676206f1 feat+ref!(posix): rewrite to load ENVs only once for all shells 2023-11-21 11:28:17 -07:00
Lockszmith
b6a02eaf21 ref: use cat << EOF > load.sh rather than echo 2023-11-21 11:28:17 -07:00
Lockszmith
ea99790768 fix(posix): run load.sh only once when .bashrc sources .profile 2023-11-21 11:28:17 -07:00
AJ ONeal
ec33462ae0 chore: fix typo in PATH.env message 2023-11-21 11:28:17 -07:00
AJ ONeal
89c437700e fix(posix): ensure PATH.env exists before checking contents 2023-11-21 11:28:16 -07:00
AJ ONeal
c75bbe7bd9 doc(webi-essentials): show command before running it 2023-11-21 11:28:16 -07:00
AJ ONeal
0767db7a7b doc(webi-essentials): clarify as per feedback in #768 2023-11-20 09:20:10 -07:00
AJ ONeal
b71a824e13 feat: distinguish between ACTION REQUIRED types 2023-11-20 09:20:09 -07:00
AJ ONeal
b3506079ac feat: add webi-essentials - a quick way to install curl, git, zip, etc 2023-11-20 09:20:09 -07:00
AJ ONeal
6fa045a4f9 fix(busybox): prefer curl over wget, limit wget options on busybox 2023-11-20 09:18:35 -07:00
AJ ONeal
64b58cf246 fix(pwsh): correctly classify musl, ignore c# builds 2023-11-19 12:52:32 -07:00
AJ ONeal
a4a386dd8a ref: rename to package-install.tpl.sh to match namespace style 2023-11-18 23:57:33 -07:00
AJ ONeal
e39e9722d7 ref(posix): various whitespace and text style updates 2023-11-18 23:00:26 -07:00
AJ ONeal
b56229ca62 ref(install): minor tweaks to differences from bootstrap 2023-11-18 23:00:25 -07:00
AJ ONeal
98ed726ec4 ref(install): bring into parity with curl-pipe-bootstrap 2023-11-18 23:00:25 -07:00
AJ ONeal
37201dc257 ref(bootstrap): organize functions into commented sections 2023-11-18 19:04:37 -07:00
AJ ONeal
4f53a412e5 ref(bootstrap): word order, wording, & style cleanup 2023-11-18 19:04:37 -07:00
AJ ONeal
e4c4fd8c08 fix(bootstrap): preserve file handle: mv & rm rather overwrite webi 2023-11-18 19:04:37 -07:00
AJ ONeal
7fe17220b7 ref(bootstrap): use -- in printf -- '%s' "${foo}" just to be safe 2023-11-18 19:04:37 -07:00
AJ ONeal
24f6703a2d fix(bootstrap): POSIX doesn't define 'interactive', detect TTY instead 2023-11-18 19:04:37 -07:00
AJ ONeal
6296dece2a ref(bootstrap): organize text styles 2023-11-18 19:04:36 -07:00
AJ ONeal
3908a7df9f fix(bootstrap): only apply text style & color to TTYs 2023-11-18 19:04:36 -07:00
AJ ONeal
1267272902 fix(bootstrap): track if just upgraded to limit upgrade attempts 2023-11-18 19:04:36 -07:00
AJ ONeal
4869336b24 fix(webi): track if welcome message was already shown 2023-11-18 19:04:36 -07:00
AJ ONeal
ddda72daac fix(bootstrap): track if welcome message was already shown 2023-11-18 19:03:45 -07:00
AJ ONeal
bfb54691e8 fix(bootstrap): typo '-f sSL' => '-f -sSL' 2023-11-18 18:09:32 -07:00
AJ ONeal
8aa665be56 fix(posix): set WEBI_CHECKSUM in both bootstrap and installer 2023-11-18 18:09:31 -07:00
AJ ONeal
c0c7e61cb5 fix: revert curl --fail-with-body to --fail 2023-11-17 08:57:24 -07:00
AJ ONeal
993e2dd75b fix!(chromedriver): rewrite for latest releases URL 2023-11-15 21:59:33 -07:00
AJ ONeal
286efb7898 fix(yq): filter out special non-build files 2023-11-15 21:31:20 -07:00
AJ ONeal
5ee4ecde56 fix(terraform): remove incorrect arch detection 2023-11-15 21:31:20 -07:00
AJ ONeal
af645df848 fix(kube*): filter out companion util and legacy scripts 2023-11-15 21:31:20 -07:00
AJ ONeal
18b1effadd fix(jq): filter out special non-build files 2023-11-15 21:31:20 -07:00
AJ ONeal
0f96b709f0 fix(go): filter out typoed beta release 2023-11-15 21:31:20 -07:00
AJ ONeal
b908672f12 fix(flutter): add _version to match version in filepath 2023-11-15 21:31:20 -07:00
AJ ONeal
b925ebe746 fix(flutter): remove incorrect os/arch detection 2023-11-15 21:31:19 -07:00
AJ ONeal
777b28a924 fix(fd): filter out unmatchable legacy build 2023-11-15 21:31:19 -07:00
AJ ONeal
46374beb68 ref(dashcore): remove custom filtering on generic asset type 2023-11-15 21:31:19 -07:00
AJ ONeal
852bae9d3d fix(dashcore): add _version to match version in filepath 2023-11-15 21:31:19 -07:00
AJ ONeal
afbebb8676 fix(cmake): add _version to match version in filepath 2023-11-15 21:31:19 -07:00
AJ ONeal
b63eb6d839 fix(ssh-adduser): check if SSH_ADDUSER_AUTO is set 2023-11-14 14:46:04 -07:00
AJ ONeal
8ea2e01f4c fix(iterm2): don't update to the same version 2023-11-14 13:58:34 -07:00
AJ ONeal
b843c689a0 ref(releases): filter out various one-offs and non-builds 2023-11-14 12:10:04 -07:00
AJ ONeal
dc1ab03aeb ref(releases): add classification helpers: _names, _version, _filenames 2023-11-14 12:10:04 -07:00
AJ ONeal
e9485d85ad chore: burn junk comments 2023-11-14 12:10:04 -07:00
AJ ONeal
42f9e73b55 ref(flutter): cleanup releases.js, add hash, don't resort 2023-11-14 12:10:04 -07:00
AJ ONeal
e49ba2ba92 fix(flutter): update release URLs for v3.x 2023-11-14 12:09:53 -07:00
AJ ONeal
3d2280b026 fix(pwsh): '$pkg_dst' is a link, not a directory 2023-11-14 12:08:18 -07:00
AJ ONeal
aa03b9e4f1 fix(flutter): fix bad 'v' match: don't replace 'dev' with 'de' 2023-11-14 10:55:08 -07:00
AJ ONeal
e0820b5517 fix(gitea): check for and install 'git' on Windows 2023-11-14 10:55:07 -07:00
AJ ONeal
e05551cb06 fix(zig): use semver version for beta / 'master' 2023-11-14 10:55:07 -07:00
AJ ONeal
cf621183b4 fix(windows): use -L with curl (for redirects from Github assets, etc) 2023-11-14 10:55:07 -07:00
AJ ONeal
3c05298c6a fix(arm): don't match 'arm64' for 'arm' without bit or version specifier 2023-11-14 10:55:07 -07:00
AJ ONeal
0c438aff8f ref!: make 'pwsh' canonical package for PowerShell Core 2023-11-12 02:32:27 -07:00
AJ ONeal
fc8cef1308 ref!: add 'powershell' as alias for 'pwsh' 2023-11-12 02:32:27 -07:00
AJ ONeal
8ed7909e15 ref!: transitory rename of 'powershell' to 'pwsh' 2023-11-12 02:32:27 -07:00
AJ ONeal
de36cf6b10 ref!: delete alias 'pwsh' in prep for powershell => pwsh transition 2023-11-12 02:32:27 -07:00
AJ ONeal
0c99e0272e fix(webi): add exec bit 2023-11-12 02:24:28 -07:00
AJ ONeal
3e97590568 fix(windows): show machine info 2023-11-12 02:24:27 -07:00
AJ ONeal
e5bda52ef3 fix(windows): classify arm32 as armv7 2023-11-12 02:24:27 -07:00
AJ ONeal
344c1d6af8 fix(windows): make pwsh alias 'powershell ...' rather '... | powershell" 2023-11-11 23:39:18 -07:00
AJ ONeal
f69d3658c7 doc(pwsh): update for Windows and pwsh-essentials 2023-11-11 23:38:49 -07:00
AJ ONeal
a22ae3e5db feat(pwsh): add Windows installer 2023-11-11 23:29:32 -07:00
AJ ONeal
ee65e50f54 doc(pwsh): update modules dirs, add ToC 2023-11-11 23:29:32 -07:00
AJ ONeal
079035fe56 doc(pwsh-essentials): add section on command arguments 2023-11-11 23:29:32 -07:00
AJ ONeal
f48b84822c doc(pwsh-essentials): include critical things to know 2023-11-11 23:29:32 -07:00
AJ ONeal
87463e2edc feat: add pwsh-essentials 2023-11-11 23:29:31 -07:00
AJ ONeal
d1cc8308d6 feat(pwsh-essentials): add pwsh-run 2023-11-11 23:08:06 -07:00
AJ ONeal
4e8d056aac feat(pwsh-essentials): add pwsh-lint 2023-11-11 23:08:06 -07:00
AJ ONeal
98799f6743 feat(pwsh-essentials): add pwsh-fix 2023-11-11 23:08:06 -07:00
AJ ONeal
08392618c9 feat(pwsh-fmt): use -Recurse rather than... recurse manually 2023-11-11 23:08:06 -07:00
AJ ONeal
b9c224a1d3 feat(pwsh-essentials): add pwsh-fmt 2023-11-11 23:08:05 -07:00
AJ ONeal
62c9dc597c feat: add PSScriptAnalyzer (pwsh fmt, fix, lint) 2023-11-11 23:08:05 -07:00
AJ ONeal
693e91d275 feat(windows): auto-update before exit after running installer 2023-11-11 23:07:28 -07:00
AJ ONeal
1af9db79bb feat(windows): show welcome message 2023-11-11 23:07:28 -07:00
AJ ONeal
1f634be3bd fix(windows): only show PATH update message once 2023-11-11 23:07:28 -07:00
AJ ONeal
fe363814a1 fix(windows): preserve parent installer's ENVs by reverting in-process call operator to new-process powershell 2023-11-11 23:07:28 -07:00
AJ ONeal
2046371f62 fix(windows): downgrade Elevated Privileges error to warn-once (for ssh) 2023-11-11 23:06:32 -07:00
AJ ONeal
e41e27b442 doc(ssh-authorize): add Windows notes 2023-11-11 23:06:31 -07:00
AJ ONeal
c40be4a2cb feat(ssh-authorize): powershell edition for Windows 2023-11-11 21:29:55 -07:00
AJ ONeal
79c2f434bf ref!(webi): use $HOME instead of switching and relying on the current directory 2023-11-11 21:27:06 -07:00
AJ ONeal
ea36def846 ref(webi): run cmd.exe with %USERPROFILE% path rather than pushd / popd 2023-11-11 21:01:04 -07:00
AJ ONeal
37d9644530 fix(windows): switch to Install-WebiHostedScript to install scripts without PATH conflits between .bat and .ps1 2023-11-11 21:01:04 -07:00
AJ ONeal
941b84092e ref(windows): use Invoke-DownloadUrl in webi itself 2023-11-11 21:01:04 -07:00
AJ ONeal
e4c5c99a72 feat(webi): add common Invoke-DownloadUrl for Windows installers 2023-11-11 20:56:55 -07:00
AJ ONeal
03a23606b2 doc: vanquish typo 'xbin' => 'bin' 2023-11-11 13:24:02 -07:00
AJ ONeal
3a011f9574 doc(windows): typo 'xbin' => 'bin' 2023-11-11 13:24:02 -07:00
AJ ONeal
5d5fa8f695 fix: '.local\xbin' => '.local\bin' 2023-11-11 13:24:01 -07:00
AJ ONeal
82d8d3a6cb ref(windows): use Sync-EnvPath to guarantee $Env:Path correctness 2023-11-11 13:18:20 -07:00
AJ ONeal
22fd0a8a0a ref(webi): use Write-Host for the whole output block 2023-11-11 13:18:20 -07:00
AJ ONeal
2b49f92647 fix(windows): set $Env:Path in-place when modifying path 2023-11-11 13:18:20 -07:00
AJ ONeal
c26e2165e4 fix(windows): make Remove-Item ignore non-existing rather than fail 2023-11-11 09:53:02 -07:00
AJ ONeal
026d849fa7 doc(caddy): use Windows' $HOME for firewall rule example 2023-11-11 09:48:15 -07:00
AJ ONeal
1b3d575b0f fix(windows): include User-Agent MS so that alias installs work in dev 2023-11-08 00:41:20 -07:00
AJ ONeal
c2f100d940 feat(bootstrap): show curl/wget command on error 2023-11-08 00:04:26 -07:00
AJ ONeal
96634ea093 fix(bootstrap): ensure that the download directory exists 2023-11-07 23:48:03 -07:00
AJ ONeal
112311ae35 fix(webi): replace == with = for POSIX sh
See https://github.com/koalaman/shellcheck/issues/2857
2023-11-07 23:38:09 -07:00
AJ ONeal
7285d9efb6 fix: missed a spot on the bootstrap.sh => curl-pipe-bootstrap.tpl.sh 2023-11-07 23:02:28 -07:00
AJ ONeal
92b0b2fe42 ref! readable template script 2023-11-07 12:46:52 -07:00
AJ ONeal
838f2d5288 ref(webi): move powershell script into its installer dir 2023-11-07 12:46:52 -07:00
AJ ONeal
2a2600b11c ref(webi): use more descriptive filenames 2023-11-07 12:46:52 -07:00
AJ ONeal
c6b37324a0 fix(iterm2-utils): add ~/.iterm2/ to PATH 2023-11-07 03:24:10 -07:00
AJ ONeal
ffba40507e fix: don't switch template file name yet 2023-11-07 03:24:09 -07:00
AJ ONeal
90aafc1df1 feat: bring in bootstrap renderer 2023-11-07 00:45:00 -07:00
AJ ONeal
b1d7df199f feat(webi): add webi.sh to own installer 2023-11-07 00:25:18 -07:00
AJ ONeal
50f98f2928 ref: rename module 'serveInstaller' => 'Installers' 2023-11-06 23:55:34 -07:00
AJ ONeal
0afe701dd8 ref!: move regexp to list 2023-11-06 19:13:21 -07:00
AJ ONeal
a4b324f3dc ref(goreleaser): don't use hacky echo where 'tr -d " "' will do 2023-11-06 19:12:57 -07:00
AJ ONeal
18217b2f17 chore: remove junk files 2023-11-06 18:59:57 -07:00
AJ ONeal
525d1bd8cb chore: expose helper function & update test 2023-11-06 18:50:02 -07:00
AJ ONeal
fc36f3e93e ref(webi): add explicit libc detection 2023-11-06 18:50:02 -07:00
AJ ONeal
ffa49401aa feat(windows): set libc=mvsc via User-Agent and query params 2023-11-06 18:50:02 -07:00
AJ ONeal
5ab83fcb37 feat(node): add explicit libc detection 2023-11-06 18:50:01 -07:00
AJ ONeal
b2dec6d467 ref(cmake): add explicit libc detection 2023-11-06 18:50:01 -07:00
AJ ONeal
156e8ddc5e fix(cmake): fix windows dir copy and path update 2023-11-06 18:50:01 -07:00
AJ ONeal
1ac39b70fe feat(AtomicParsley): add explicit libc detection 2023-11-06 18:50:01 -07:00
AJ ONeal
53d023aac9 ref(api): use await to de-indent some async functions 2023-11-06 16:26:49 -07:00
AJ ONeal
a0222527c0 feat: add TinyGo 2023-11-06 16:23:12 -07:00
AJ ONeal
4fe71af9d0 fix(windows): use home directory, not current directory 2023-11-05 11:39:32 -07:00
AJ ONeal
a2c694fd8a fix(webi): remove incorrect regex escape 2023-11-05 11:38:37 -07:00
AJ ONeal
bf5482c453 ref+fix(vim-commentary): fix inconsistencies in install files 2023-11-02 17:30:34 -06:00
AJ ONeal
bd47c3bfb5 feat(vim-zig): add zig.vim alias 2023-11-02 17:30:33 -06:00
AJ ONeal
81d2eb4288 feat: add vim-zig 2023-11-02 17:30:33 -06:00
AJ ONeal
8bf2fffa1a feat(git-tag): add HEAD, which is beta as long as any other tags are available 2023-11-02 17:30:33 -06:00
AJ ONeal
8a16909813 feat(git-tag): if already cloned, fetch to update 2023-11-02 17:30:33 -06:00
AJ ONeal
3f29c43513 ref!(git-tag): clone(url, path) => clone(path, url) so that repoPath is always first 2023-11-02 17:30:33 -06:00
AJ ONeal
c95f2924cc ref(git-tag): cleanup tag handling 2023-11-02 17:30:33 -06:00
AJ ONeal
dcc2034f88 fix(git-tag): resolve 'repoBaseDir' relative to cwd 2023-11-02 17:30:33 -06:00
AJ ONeal
5ab26dbb36 test(git-tag): show output from simpler, better targeted repos 2023-11-02 17:30:32 -06:00
RubenRam
1d9dadd8cf doc(zoxide): update README Files section 2023-11-02 17:28:49 -06:00
RubenRam
22b5298091 doc(yq): update README Files section 2023-11-02 17:28:49 -06:00
RubenRam
f0d730ad35 doc(xz): update README Files section 2023-11-02 17:28:49 -06:00
RubenRam
69ea717e06 doc(webi): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
d3b7959451 doc(watchexec): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
0a71df82b3 doc(vps-utils): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
605ebf1234 doc(vps-addswap): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
9cb6550371 doc(vim-whitespace): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
f44554392b doc(vim-viminfo): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
99e2ac7708 doc(vim-syntastic): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
929b920c28 doc(vim-sensible): update README Files section 2023-11-02 17:28:48 -06:00
RubenRam
15f72de19b doc(vim-rust): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
a41b1e4300 doc(vim-nerdtree): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
2b5e3e4cb9 doc(vim-leader): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
b3b685b2e1 doc(vim-lastplace): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
50bae5dd8b doc(vim-gui): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
c057e4be94 doc(vim-go): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
62e717900c doc(vim-devicons): update README Files section 2023-11-02 17:28:47 -06:00
RubenRam
b347f406f5 doc(vim-beyondcode): update README Files section 2023-11-02 17:28:46 -06:00
RubenRam
f2ef731cd3 doc(vim-ale): update README Files section 2023-11-02 17:28:46 -06:00
AJ ONeal
23cbb707e9 doc(serviceman): add service file locations, revamped cheat sheet, add ToC 2023-11-02 10:42:27 -06:00
AJ ONeal
29a9a16a4b doc(pathman): add shell config info, cheat sheet, and add ToC 2023-11-02 10:42:27 -06:00
RubenRam
262ba70ab3 doc(syncthing): update README Files section 2023-11-02 10:42:27 -06:00
RubenRam
fe797329d2 doc(sttr): update README Files section 2023-11-02 10:42:27 -06:00
RubenRam
27edb0599f doc(sshd-prohibit-password): update README Files section 2023-11-02 10:42:27 -06:00
RubenRam
1188bcb99c doc(ssh-utils): update README Files section 2023-11-02 10:42:26 -06:00
RubenRam
1124f0d72d doc(ssh-setpass): update README Files section 2023-11-02 10:42:26 -06:00
RubenRam
8c24a907c3 doc(ssh-pubkey): update README Files section 2023-11-02 10:42:26 -06:00
RubenRam
c747baa9a8 doc(ssh-authorize): update README Files section 2023-11-02 09:59:08 -06:00
RubenRam
124aa60197 doc(ssh-adduser): update README Files section 2023-11-02 09:59:07 -06:00
RubenRam
cb5b355c07 doc(serviceman): update README Files section 2023-11-02 09:59:07 -06:00
RubenRam
b1af98ed37 doc(setcap-netbind): update README Files section
Add Files section and fix broken homepage link
2023-11-02 09:59:07 -06:00
RubenRam
67aa55017d doc(sd): update README Files section 2023-11-02 09:59:07 -06:00
RubenRam
f934f2b75d doc(sclient): update README Files section 2023-11-02 09:59:07 -06:00
RubenRam
bdbb3b9b92 doc(sass): update README Files section 2023-11-02 09:59:07 -06:00
RubenRam
133af72ad7 doc(rustlang): update README Files section 2023-11-02 09:59:07 -06:00
RubenRam
0a3bdf0708 doc(postgres): update README Files section 2023-11-02 09:59:06 -06:00
RubenRam
5a6599412f doc(pathman): update README Files section 2023-11-02 09:34:23 -06:00
RubenRam
b93c8f8d8d doc(pandoc): update README Files section 2023-11-02 09:34:22 -06:00
AJ ONeal
6c696d7e46 ref!(vim-commentary): use git source releases 2023-10-31 15:09:48 -06:00
AJ ONeal
9c075ee60d ref: add WEBI_PKG_PATHNAME to replace WEBI_PKG_FILE (which is sometimes a dir) 2023-10-31 15:09:48 -06:00
AJ ONeal
7996a52dfe fix(pkg_no_exec): also guard against linking and adding to PATH 2023-10-31 15:09:48 -06:00
AJ ONeal
d34101aa80 fix(powershell): always create the downloads folder 2023-10-31 15:09:48 -06:00
AJ ONeal
fb1790dde0 ref!(test): show release info for git tags 2023-10-31 15:09:48 -06:00
AJ ONeal
5dd3a79faa ref!: release info from git tags 2023-10-31 15:09:48 -06:00
AJ ONeal
8926e27d41 feat: add raw git tags support (generated by GPT) 2023-10-31 15:09:47 -06:00
AJ ONeal
927151100a fix(xcaddy): use updated PATH after installing Go 2023-10-31 15:07:58 -06:00
AJ ONeal
46d2c358a7 fix(xcaddy): check for 'go' with 'Get-Command' in PowerShell rather than exact path 2023-10-31 15:07:58 -06:00
AJ ONeal
f68f5b2c13 fix(webi): reserve RED for ERROR in powershell too 2023-10-31 15:07:57 -06:00
AJ ONeal
4359bfd87e ref!(webi_download): separate error to complete transition to 2 args 2023-10-30 18:03:42 -06:00
AJ ONeal
ad1cdd1eac ref(webi_download): always use 2-3 args on as many lines 2023-10-30 18:03:41 -06:00
AJ ONeal
433ae9065b ref(template.sh): check exit code or size rather than stdout 2023-10-30 18:02:55 -06:00
AJ ONeal
ecee9dbb34 ref(setcap-netbind): check exit code rather than stdout 2023-10-30 18:02:55 -06:00
AJ ONeal
afd490e47f ref(sh): check exit code rather than stdout 2023-10-30 18:02:55 -06:00
AJ ONeal
9f391b30be feat(webi): add preliminary --list and --info 2023-10-30 01:35:39 -06:00
AJ ONeal
317b94cbbc feat: guard 'xattr' & 'chmod' with 'pkg_no_exec' 2023-10-30 01:35:39 -06:00
AJ ONeal
81e4007715 lint(webi): SC2086,SC2155 (exports and quotes) 2023-10-30 01:35:39 -06:00
AJ ONeal
bfb7a437d0 ref: un-expand $HOME to ~ for printed paths 2023-10-30 01:35:39 -06:00
AJ ONeal
8373df5e50 chore: update or remove old comments 2023-10-30 01:35:38 -06:00
AJ ONeal
480dc0801c fix: use single quotes for WEBI_VERSION 2023-10-30 01:35:38 -06:00
AJ ONeal
e6713da28d chore: whitespace 2023-10-30 01:35:38 -06:00
AJ ONeal
37bd443310 doc(git): various minor corrections, including git switch 2023-10-28 19:59:34 -06:00
RubenRam
f36cf182b3 doc(mutagen): update README Files section 2023-10-28 19:59:34 -06:00
RubenRam
0172c186af doc(myip): update README Files section 2023-10-28 19:59:33 -06:00
RubenRam
714d48dd89 doc(kubens): update README Files section 2023-10-28 19:59:33 -06:00
RubenRam
7bb4be510b doc(kubectx): update README Files section 2023-10-28 19:59:33 -06:00
RubenRam
bcdfcb6807 doc(kind): update README Files section 2023-10-28 19:59:33 -06:00
RubenRam
875e95818b doc(keypairs): update README Files section 2023-10-28 19:59:33 -06:00
RubenRam
89a05179ea doc(jq): update README Files section 2023-10-28 19:59:33 -06:00
RubenRam
3f74b451ec doc(hexyl): update README Files section 2023-10-28 19:59:32 -06:00
RubenRam
cd2d49383a doc(gprox): update README Files section 2023-10-28 19:59:32 -06:00
RubenRam
a71402d1ba doc(goreleaser): update README Files section 2023-10-28 19:59:32 -06:00
RubenRam
223ed22513 doc(gitea): update README Files section 2023-10-28 19:56:13 -06:00
RubenRam
de5c671edb doc(gitdeploy): update README Files section 2023-10-28 19:47:32 -06:00
RubenRam
e2b3f3370a doc(git): update README Files section 2023-10-28 19:47:32 -06:00
RubenRam
6579be2474 doc(gh): update README Files section 2023-10-28 19:47:32 -06:00
AJ ONeal
32242220b4 doc(ollama): update README 2023-10-28 17:05:12 -06:00
AJ ONeal
57aa98404a chore: whitespace 2023-10-28 17:05:12 -06:00
AJ ONeal
b3e2f0c2d1 workaround(ollama): filter out v0.0.x releases that are sorting too high 2023-10-28 17:05:12 -06:00
Tod Hansmann
ccc0ed17b9 feat: add ollama (LLM, like ChatGPT) 2023-10-28 17:05:12 -06:00
AJ ONeal
4dc9e2d699 fix(crabz): fix installers, update README 2023-10-28 15:53:54 -06:00
Don Johnson
81d1e16d0e feat: add crabz (multi-threaded gzip compression) 2023-10-28 15:39:23 -06:00
AJ ONeal
b8c97dd9e0 chore(lint): don't create a function in a loop 2023-10-28 01:43:55 -06:00
AJ ONeal
70cc64e8fc fix: better (lazier) 'stable' vs 'beta' detection 2023-10-28 01:43:55 -06:00
AJ ONeal
1bc66676a5 fix(macOS): handle ./CMake.app/Contents/ 2023-10-28 01:36:25 -06:00
AJ ONeal
ed41ce092b doc(sclient): add '--alpn <proto>' 2023-10-28 01:35:06 -06:00
AJ ONeal
1c96c5d366 doc(goreleaser): update for goreleaser v1.x 2023-10-28 01:35:06 -06:00
AJ ONeal
f89ba373a6 chore: alias 'trip' (command name) as 'trippy' (package name) 2023-10-28 01:30:15 -06:00
AJ ONeal
51ccfab586 ref: move 'trippy' to 'trip', as that's the command name 2023-10-28 01:30:15 -06:00
AJ ONeal
30f161f032 fix(sudo): always create sudo.cmd in Windows user's home directory 2023-10-28 01:30:15 -06:00
AJ ONeal
a22af8815e fix(trip): various installer fixes and doc updates 2023-10-28 01:30:15 -06:00
Don Johnson
f2b9533fd3 feat: add network tool trip (trippy) 2023-10-28 01:24:02 -06:00
AJ ONeal
b63a9cb686 chore(fmt): whitespace 2023-10-28 01:24:02 -06:00
Sean Alter
5502cb912d fix: add missing WEBI_WELCOME=true & welcome message check 2023-10-27 22:57:27 -06:00
AJ ONeal
3501e891c5 chore: fmt whitespace 2023-10-25 11:48:09 -06:00
AJ ONeal
d592d338df chore(windows): remove .bat cruft 2023-10-25 11:48:09 -06:00
AJ ONeal
37705a7b4f chore(lint): only run checks on latest node 2023-10-25 11:12:11 -06:00
AJ ONeal
0109e612e4 feat: add xcaddy 2023-10-25 11:12:11 -06:00
Shovit
7020cf494e fix(brew): add --workdir flag to updater service 2023-10-25 11:10:10 -06:00
AJ ONeal
874f081401 doc(redis-commander): style updates 2023-10-24 22:58:57 -06:00
AJ ONeal
73d8f673ff feat(redis-commander): add PowerShell installer + update POSIX installer 2023-10-24 22:58:57 -06:00
Mr.Online
fb8e5ea740 feat(redis-commander): add redis commander and cleanup based on guidelines 2023-10-24 22:58:56 -06:00
AJ ONeal
29fa3551b3 feat(prettier): add PowerShell installer 2023-10-24 22:58:56 -06:00
AJ ONeal
389c67805d doc(py*): consistent Files & Install, add libzma-dev 2023-10-24 13:16:54 -06:00
AJ ONeal
1306fd6ebc fix: replace leftover 'pathman add' with 'webi_path_add' 2023-10-24 13:16:54 -06:00
AJ ONeal
4b8f62a1b8 doc(caddy): add conditional logic example 2023-10-22 21:31:31 -06:00
AJ ONeal
4ddf971a24 doc(caddy): correct and expand CORS examples 2023-10-22 21:30:19 -06:00
AJ ONeal
5d1e975b5e doc(caddy): remove mistaken nesting / indentation 2023-10-22 15:00:41 -06:00
AJ ONeal
47e675b92f doc(caddy): alias Snippets as Macros for easier search 2023-10-22 15:00:41 -06:00
AJ ONeal
5acb130bc2 doc!(caddy): complete cheat sheet rewrite + extras 2023-10-20 17:23:26 -06:00
AJ ONeal
9194866f38 doc(node): and fmt + lint + ci workflows 2023-10-20 17:21:52 -06:00
AJ ONeal
264dbec4ff doc(node): more service examples + lint + fmt 2023-10-20 17:21:52 -06:00
AJ ONeal
251920a67c doc(node): add serviceman examples 2023-10-20 17:21:52 -06:00
AJ ONeal
6888d845aa hotfix(windows): '{{ installer }}' must commented as to not be changed by Invoke-Formatter 2023-10-18 02:44:16 -06:00
AJ ONeal
d9ec3860d6 doc(pwsh): add info about linting and fmt 2023-10-18 02:27:47 -06:00
AJ ONeal
6b0c5263af fix(pwsh): previews should be beta, not stable 2023-10-18 02:21:09 -06:00
AJ ONeal
589edd6940 chore(pwsh): add .ps1 linting to ci/cd 2023-10-18 02:11:48 -06:00
AJ ONeal
3d51b8373a chore(pwsh): lint+fix 2023-10-18 02:10:55 -06:00
AJ ONeal
f98086b38e chore(pwsh): fmt 2023-10-18 02:10:55 -06:00
AJ ONeal
18eb622850 chore(pwsh): add tooling & configs 2023-10-18 02:10:41 -06:00
AJ ONeal
21fc19d866 doc(dotenv-linter): reword some sections 2023-10-17 21:42:44 -06:00
Sean Alter
4c6d87740d docs(dotenv-linter): Update Cheat Sheet to be compatable with dotenv-linter v3.1 2023-10-17 21:39:59 -06:00
Sean Alter
f0a9f5463e feat(bat): Add mscv update to bat installer 2023-10-17 21:17:16 -06:00
AJ ONeal
dcffa0b7b1 fix(AtomicParsley): remove symlink for local dev on macOS 2023-10-17 17:21:06 -06:00
AJ ONeal
51ac195ca0 fix(ffuf): fix version command, and package path for Windows 2023-10-17 21:02:42 +00:00
AJ ONeal
84722bd140 doc(ffuf): add newbie-friendly examples 2023-10-17 20:47:05 +00:00
Don Johnson
091f9ac647 feat: add ffuf (fuzz tester) 2023-10-17 20:47:04 +00:00
AJ ONeal
cbb3d26dcd feat(dotenv-linter): auto-install MSVC 2023-10-17 19:12:31 +00:00
AJ ONeal
ee211f475a doc(dotenv-linter): update Files section and Cheat Sheet for v3 2023-10-17 19:12:30 +00:00
AJ ONeal
f60b20ad76 style: change color scheme 2023-10-17 18:55:52 +00:00
Sean Alter
81b08a0ecd style(template&bootstrap): Change red text to yellow 2023-10-17 18:14:00 +00:00
AJ ONeal
17f4b99f23 doc: minor updates to various Files sections 2023-10-17 17:23:07 +00:00
RubenRam
54635a5d11 doc(fzf): update Files section 2023-10-17 17:18:47 +00:00
RubenRam
acf02f31d9 doc(flutter): update Files section 2023-10-17 17:18:47 +00:00
RubenRam
0e2d6b146e doc(ffmpeg): update Files section 2023-10-17 17:18:46 +00:00
RubenRam
be7448b44d doc(fd): update Files section 2023-10-17 17:18:45 +00:00
RubenRam
e31f3fa106 doc(dotenv-linter): update Files section 2023-10-17 17:18:45 +00:00
RubenRam
92f136959f doc(dotenv): update Files section 2023-10-17 17:18:44 +00:00
RubenRam
2eae656b94 doc(curlie): update Files section 2023-10-17 17:18:44 +00:00
RubenRam
5e97827781 doc(comrak): update Files section 2023-10-17 17:18:44 +00:00
RubenRam
799d3883fd doc(caddy): update Files section 2023-10-17 17:18:43 +00:00
RubenRam
396294f2be doc(brew): update Files section 2023-10-17 17:18:43 +00:00
RubenRam
6c7e099adb doc(awless): update Files section 2023-10-17 17:18:42 +00:00
starvader13
f767a24218 doc(xsv): add missing | to command list
Signed-off-by: starvader13 <ashutoshgupta1311@gmail.com>
2023-10-17 04:09:41 +00:00
AJ ONeal
8d4c099bc7 doc(xsv): reorganized and updated with examples 2023-10-16 23:17:32 +00:00
Don Johnson
b138dd5ef2 feat: add xsv (query CSV like SQL) 2023-10-16 23:17:32 +00:00
AJ ONeal
aa616be917 ref(terraform): oppa AJ style 2023-10-16 20:04:44 +00:00
Jake Curragh
621f2ecded feat(terraform): add Terraform package 2023-10-16 19:51:03 +00:00
howyallare
7e6367eab5 doc(atomicparsley): README.md corrections
- artwork image manipulation is macOS-only
- ffmpeg 4.0+ can add artwork to mp4 files
2023-10-16 19:15:07 +00:00
AJ ONeal
c31c292765 ref(sqlpkg): oppa AJ-style 2023-10-16 18:02:50 +00:00
Stephen Margheim
73b233eed9 feat(sqlpkg) Add sqlpkg CLI utility for installing SQLite extensions 2023-10-16 18:02:49 +00:00
AJ ONeal
a2cf9f39bb doc(grype): fmt + minor cleanup 2023-10-12 05:15:28 +00:00
Muhammed Ibrahim
cfc02f4e1a add grype + cheatsheet 2023-10-12 05:15:27 +00:00
AJ ONeal
73af48eb9d chore: move git hooks to js 2023-10-12 04:40:25 +00:00
Shyam Mahajan
db5113e025 chore: add pre-commit git hooks to repo #474 2023-10-12 04:14:13 +00:00
AJ ONeal
41f1c69a30 chore: add _scripts/git-hook-init.js (husky alternative) 2023-10-12 04:13:30 +00:00
AJ ONeal
bcb514482b chore: npm install to update package-lock.json 2023-10-12 04:13:10 +00:00
AJ ONeal
35992b4aa4 chore: update version and lockfile 2023-10-12 02:34:41 +00:00
AJ ONeal
ca46158d68 chore: add lint & fmt to ci/cd 2023-10-12 02:34:40 +00:00
AJ ONeal
c78662056d chore: lint & fix shell files 2023-10-12 00:08:28 +00:00
AJ ONeal
4ac9e69f92 chore: lint & fix js files 2023-10-12 00:08:27 +00:00
AJ ONeal
7d49bf1981 chore: update lint config 2023-10-12 00:08:27 +00:00
AJ ONeal
e276bb32a5 chore: use '&&' rather than ';' for 'npm run fmt' on Windows 2023-10-11 23:29:10 +00:00
AJ ONeal
476548cd06 chore: fmt (prettier@3.x) 2023-10-11 23:25:10 +00:00
AJ ONeal
eff21a266c chore: update to prettier@3.x 2023-10-11 23:25:10 +00:00
AJ ONeal
a2e4368da8 chore: fmt (prettier@2.x) 2023-10-11 23:20:36 +00:00
AJ ONeal
22e35e612e chore: update prettier config 2023-10-11 23:19:48 +00:00
AJ ONeal
55a5280057 fix(dashcore-utils): add missing example block 2023-10-11 23:19:01 +00:00
Tristan Engel
f5843b8519 fix(rclone): update install.ps1 (fix #636) 2023-10-11 22:11:15 +00:00
AJ ONeal
9cd869e3bb ref: improve readability of arch sort 2023-10-11 22:06:12 +00:00
AJ ONeal
b882a30659 fix(ppc64le): add ppc64le to arch list for POSIX shell projects 2023-10-11 22:06:03 +00:00
AJ ONeal
489c27ea01 fix(ppc64le): sort by specificity: ppc64le comes BEFORE ppc64 2023-10-11 22:05:22 +00:00
AJ ONeal
456bb61896 fix(vim-ale): show linter code after linter name 2023-10-11 04:09:34 +00:00
AJ ONeal
9fe80496d3 feat: link AtomicParsley to atomicparsley to accommodate real command name 2023-10-09 08:34:29 +00:00
AJ ONeal
fdc6968322 feat: add atomicparsley 2023-10-09 08:34:29 +00:00
AJ ONeal
dcc542d70b feat: add common aliases of vcruntime 2023-10-09 08:34:03 +00:00
AJ ONeal
6b7a78a18a feat: add vcruntime (vcruntime140.dll) 2023-10-09 08:34:03 +00:00
AJ ONeal
b476074f04 ref(hugo): update for latest standard and extended editions 2023-09-16 23:25:11 +00:00
AJ ONeal
cf1c486616 feat: add hugo-extended 2023-09-16 23:05:30 +00:00
AJ ONeal
1d87306129 ref!(go): separate go from go-essentials for 1.21 2023-08-09 23:29:25 +00:00
AJ ONeal
5a92412348 ref: make 'go' canonical installer, not just alias 2023-08-09 23:28:50 +00:00
AJ ONeal
80dace64f3 fix(node): map 'osx' os identifier to 'darwin' filename 2023-07-27 15:45:42 +00:00
AJ ONeal
e6d02f52c7 doc(dashd): add QuickStart, update dash.conf 2023-07-17 18:22:16 +00:00
AJ ONeal
ff015b445a ref(dashcore-utils): add zmqpubhashchainlock and zmqpubrawchainlock to dash.example.conf 2023-07-17 18:22:15 +00:00
jojobyte
005f0fd4f7 doc(ffmpeg): Add DNS resolution note and example 2023-07-15 00:04:32 -06:00
AJ ONeal
0b127b3c6e ref!(node): add officially unofficial musl builds for alpine 2023-07-05 20:03:05 +00:00
AJ ONeal
a3954bad3d ref(alpine): enable musl filtering 2023-07-05 20:03:04 +00:00
AJ ONeal
4ad1d3da4b ref(lsd): enable musl builds, update docs 2023-07-05 17:48:00 +00:00
AJ ONeal
5f96be9593 fix(vim-ale): use ascii rather than utf8 2023-07-05 17:35:28 +00:00
AJ ONeal
2e7f68ef2e feat: add brew-update-service-install 2023-07-05 17:33:49 +00:00
AJ ONeal
36d825a225 ref(ssh-setpass): add usage and helpful checks and messages 2023-07-05 17:31:03 +00:00
AJ ONeal
039280a05f ref(ssh-utils): remove redundant .sh suffix 2023-07-05 17:31:02 +00:00
AJ ONeal
27030f28d2 ref!(cmake): use pkg template rather than exe template 2023-07-03 22:40:56 +00:00
brthngnsh
d00bf9eb12 feat(cmake): Added CMake package
Addon changes:
- Added share directory to install script to fix CMAKE_ROOT not found
  issue.
- Added a simple CPP Hello World program for CMake usage.

Caution:
- Need to test Powershell script.
2023-06-28 07:14:37 +00:00
AJ ONeal
d2980418dc doc(dashcore): note zmq bind behavior 2023-06-25 03:31:41 +00:00
AJ ONeal
af06adb0bb doc(dashcore): note rpcconnect, update indexing times 2023-06-25 02:54:02 +00:00
AJ ONeal
0c877a5516 fix(dashd): turn on indexes 2023-06-21 23:05:37 +00:00
AJ ONeal
cec86d4ebf ref!: move convenience scripts to dashcore-utils 2023-06-21 22:08:58 +00:00
AJ ONeal
ce2f07ef26 feat: add dashd Dash Full Node service 2023-06-21 21:55:18 +00:00
AJ ONeal
731d3c95c1 feat: add Dash Core (dash-qt) 2023-06-12 08:03:20 +00:00
AJ ONeal
b944352e1b ref(ssh-utils): alphabetize, fix typos 2023-06-11 08:40:09 +00:00
AJ ONeal
12de674f54 feat(ssh-utils): add ssh-authorize 2023-06-11 08:27:52 +00:00
AJ ONeal
a3d62393c7 feat: add ssh-authorize 2023-06-11 08:27:13 +00:00
AJ ONeal
aefba90af9 ref!(ssh-adduser): move sshd-prohibit-password to own command 2023-06-10 00:04:56 +00:00
AJ ONeal
3a574392f0 feat: add sshd-prohibit-password 2023-06-09 23:38:50 +00:00
AJ ONeal
6baa78f95d doc(vim-airline): add Files section, update format and wording 2023-06-01 06:13:11 +00:00
Ryan Burnette
54d41336c4 add vim-airline-themes installer 2023-06-01 06:03:04 +00:00
Ryan Burnette
b406af83a6 add vim-airline installer 2023-06-01 06:03:04 +00:00
AJ ONeal
7c717d994f fix(vim-italics): rewrite 2023-06-01 05:59:28 +00:00
Aritra Mallick
3383c1e322 feat: add vim-italics 2023-06-01 05:32:35 +00:00
alexravenna
522ebaddd0 docs(nerdfont): Update Windows Terminal settings to 1.16.10262.0 2023-06-01 05:20:29 +00:00
alexravenna
fd69b0b57a docs(fd): Fix typo in fd readme 2023-06-01 05:20:25 +00:00
AJ ONeal
160e13ccaa ref!: replace 'pathman' with POSIX 'webi_path_add' 2023-05-31 17:36:04 +00:00
AJ ONeal
9e523f0c0e fix(windows): detect Windows ARM64, with AMD64 fallback 2023-05-28 23:46:36 +00:00
AJ ONeal
ffd757edd6 ref(webi): shfmt 2023-05-28 05:02:01 +00:00
AJ ONeal
3ab9eee352 fix(nerdfont): pin version to tag 'v2.3.3' rather than 'master' (now v3.x) 2023-05-25 20:29:09 +00:00
AJ ONeal
efb67109fb ref!: replace pathman with PowerShell [Environment]::SetEnvironmentVariable 2023-05-09 00:04:59 +00:00
AJ ONeal
ba4571ec35 fix(windows): correctly bootstrap pathman install 2023-05-08 20:57:00 +00:00
AJ ONeal
d6757752d5 feat: add beyond-shell (for Workshops) 2023-03-07 12:09:43 +00:00
AJ ONeal
2a081b527b fix(node): remove deprecated 'scripts-prepend-node-path' option 2023-03-07 11:26:15 +00:00
Abhimanyu Sharma
51c7c20c59 feat: add sttr 2023-03-07 11:17:41 +00:00
AJ ONeal
6025fb3b8e fix(vim-prettier): add back explicit file extension detection 2023-03-07 00:58:30 +00:00
AJ ONeal
89c46b728b docs: update Files section for (vim-)prettier, shellcheck, and (vim-)shfmt 2023-03-06 23:21:45 +00:00
AJ ONeal
480a102f64 fix(vim-prettier): run when 'filetype' is javascript, remove outdated
config
2023-03-06 20:21:09 +00:00
AJ ONeal
1b37080425 fix(vim-shfmt): remove duplicate autocmd (now exists upstream) 2023-03-06 20:21:08 +00:00
AJ ONeal
e5ffc8a931 feat(ssh-adduser): allow authorized_keys url 2023-03-04 01:04:18 +00:00
AJ ONeal
af3658e240 doc(vim-essentials): mention vim-commentary, update vim-nerdtree description 2023-03-04 01:03:49 +00:00
AJ ONeal
a53fbe5f36 fix(smartcase): add powershell install for windows 2023-03-04 01:03:49 +00:00
AJ ONeal
fea715f9bd fix(vim-essentials): install bins last in case they fail (looking at you sudo'd npm) 2023-03-04 00:56:45 +00:00
AJ ONeal
30f9fd6271 doc(lsd): update alias section 2023-02-26 06:32:26 +00:00
AJ ONeal
3602972090 doc(curlie): add alias section 2023-02-26 06:26:30 +00:00
AJ ONeal
6cf153de8c doc(bat): update alias 2023-02-26 06:21:16 +00:00
AJ ONeal
410bb1c3e3 feat(bat): upsert config file 2023-02-26 06:17:42 +00:00
AJ ONeal
9f7ce9842c feat(rg): upsert config file 2023-02-26 06:17:35 +00:00
HacDan
1984d10878 doc(rg): add Files section 2023-02-26 06:15:22 +00:00
HacDan
bbaeb0dd38 doc(arc): add Files section 2023-02-26 06:12:45 +00:00
HacDan
811d5c7d63 doc(bat): add Files section 2023-02-26 06:08:47 +00:00
AJ ONeal
817e71a8f5 ref(vim-sensible): use common vim install scripts 2023-02-26 06:03:14 +00:00
AJ ONeal
0024f69109 doc: replace ``txt blocks with ``text blocks 2023-02-26 05:57:14 +00:00
Ryan Burnette
f84a4c5b69 feat: add vim-commentary 2023-02-26 05:57:14 +00:00
AJ ONeal
b25da4cae8 doc(bat): show aliasman usage too 2023-02-23 01:00:43 +00:00
AJ ONeal
a2b68dbca8 doc(delta): move Files section to standard location 2023-02-23 00:57:07 +00:00
AJ ONeal
991592e88a fix(fish): ensure the config file always exists (otherwise pathman detection goes sadways) 2023-02-23 00:39:30 +00:00
AJ ONeal
0782aa6eb8 feat(postgres): install to macOS /Applications/ rather than ~/Applications/ (works better) 2023-02-22 08:10:16 +00:00
AJ ONeal
482027b880 doc(node): update the Files description 2023-02-22 08:10:15 +00:00
AJ ONeal
4678e04654 feat(iterm2): install to macOS /Applications/ rather than ~/Applications/ (works better) 2023-02-22 08:10:15 +00:00
AJ ONeal
a782c32d59 doc(golang): add Files section 2023-02-21 08:04:05 +00:00
AJ ONeal
a1be2e4aa7 doc(node): add Files section 2023-02-21 08:04:04 +00:00
AJ ONeal
56cb856357 doc(vim-spell): add Files section 2023-02-21 08:04:04 +00:00
AJ ONeal
f73a284569 doc(vim-shell): fix typo, update Files section 2023-02-21 08:04:04 +00:00
AJ ONeal
7f4f9afaaa doc(vim-essentials): add Files section 2023-02-21 08:04:03 +00:00
AJ ONeal
9199531bf5 feat(vim-essentials): add vim-smartcase 2023-02-21 08:04:03 +00:00
AJ ONeal
061f9b643e feat: add vim-smartcase 2023-02-21 08:03:28 +00:00
AJ ONeal
9feca1c7af fix(vim-ale): update config for v3.2.0 + unicode fix 2023-02-21 08:03:12 +00:00
AJ ONeal
e5685b9025 ref: make fresh/stale/expires cache access more readable 2023-02-13 19:23:14 +00:00
AJ ONeal
6c76ff728c hotfix: never hard fail when usable data is available 2023-02-13 19:08:54 +00:00
AJ ONeal
f663bb7822 hotfix: more specific bad response errors 2023-02-13 19:03:35 +00:00
AJ ONeal
2bcc903f80 hotfix: don't use debug stale times values in production, DUH! 2023-02-13 18:44:15 +00:00
AJ ONeal
5b6b700943 fix: replace 'uname -a' with 'uname -srm' for OS/Arch detection 2023-02-11 21:57:39 +00:00
AJ ONeal
b5fc4d742b chore: update shfmt 2023-02-11 21:39:07 +00:00
AJ ONeal
5fc3d0e174 doc(aliasman): supercharge the cheat sheet 2023-02-09 09:30:58 +00:00
AJ ONeal
c26c67662b feat: add aliasman 2023-02-09 09:13:08 +00:00
AJ ONeal
e2702eea08 hotfix: log headers and body on GitHub API error 2023-02-03 20:34:26 +00:00
AJ ONeal
c2f1882de5 feat: add bun 2023-01-30 09:17:30 +00:00
AJ ONeal
90a919736b fix: allow installers without releases 2023-01-30 07:16:00 +00:00
AJ ONeal
bab564bdeb fix(k9s): add release update timeout 2023-01-24 18:02:53 +00:00
AJ ONeal
21c668b4b7 chore(fmt): set .prettierrc.json.trailingComma = "all" 2023-01-24 17:54:39 +00:00
AJ ONeal
2149eeb7f8 chore(fmt): mv .prettierrc .prettierrc.json 2023-01-24 17:54:39 +00:00
AJ ONeal
9c1be4bc3a chore(fmt): npm run prettier 2023-01-24 17:54:39 +00:00
David McClellan
967eff256a Missing word - VIM Leader Cheatsheet
Updated VIM Leader cheatsheet - added missing word. 

-- Sorry @coolaj86 it bothered me for too long, had to submit
2023-01-18 22:51:26 +00:00
AJ ONeal
eddef3c24b ref: simplify download folder check 2023-01-15 07:26:54 +00:00
Vladimir Siritsa
4723810e2b feat: set Downloads dir from xdg-user-dir DOWNLOAD when available 2023-01-15 07:13:41 +00:00
AJ ONeal
40f37cb916 feat: add duckdns -> DuckDNS.sh alias 2023-01-15 03:25:02 +00:00
AJ ONeal
25b003996a feat: add DuckDNS.sh 2023-01-15 03:23:45 +00:00
AJ ONeal
22aa8ba596 feat(releases): add github source releases (no assets) 2023-01-15 02:22:33 +00:00
AJ ONeal
2f618384cc fix: update @root/request for hanging error bugfix 2022-11-14 17:39:50 +00:00
AJ ONeal
75284b2a93 ref!: remove 'shmatter' and update 'marked' 2022-10-20 20:48:41 +00:00
507 changed files with 21957 additions and 4387 deletions

37
.github/workflows/node.js.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Node.js CI
on:
push:
branches: ['main']
pull_request:
jobs:
build:
name: "Fmt, Lint, & Test"
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
#- 20.x
- latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: "Webi: Install 'shfmt' and 'shellcheck', and update PATH"
run: |
sh ./_scripts/install-ci-deps
echo "${HOME}/.local/bin" >> $GITHUB_PATH
echo "${HOME}/.local/opt/pwsh" >> $GITHUB_PATH
- run: |
git submodule init
git submodule update
- run: shfmt --version
- run: shellcheck -V
- run: node --version
- run: npm run fmt
- run: npm ci
- run: npm run lint
- env:
GITHUB_TOKEN: ${{ github.token }}
run: npm run test

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "_webi/build-classifier"]
path = _webi/build-classifier
url = https://github.com/webinstall/webi-build-classifier.git

1
.jshintignore Normal file
View File

@@ -0,0 +1 @@
DELETEMEnode_modules/**/*

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules
jsconfig.json
package.json
package-lock.json

View File

@@ -1,6 +0,0 @@
{
"trailingComma": "none",
"tabWidth": 2,
"singleQuote": true,
"proseWrap": "always"
}

9
.prettierrc.json Normal file
View File

@@ -0,0 +1,9 @@
{
"bracketSpacing": true,
"printWidth": 80,
"proseWrap": "always",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"semi": true
}

6
.vimrc Normal file
View File

@@ -0,0 +1,6 @@
" Note: You must enable per-project .vimrc in your ~/.vimrc
" set secure
" set exrc
" PowerShell settings
let g:ale_powershell_psscriptanalyzer_exclusions = "PSAvoidUsingWriteHost,PSUseDeclaredVarsMoreThanAssignments"

View File

@@ -45,7 +45,7 @@
<https://gist.github.com/nicerobot/53cee11ee0abbdc997661e65b348f375>
- Common exceptions:
```txt
```text
# We make use of `.` (source) to import without exports
SC2034: foo appears unused. Verify it or export it.
SC2154: var is referenced but not assigned.
@@ -94,15 +94,15 @@ The general format is `<type>(<package>): <description>`, using these _types_:
Try to write your commit messages (in the present tense) like this:
```txt
```text
fix(node): update install.sh (fix #200)
```
```txt
```text
feat(delta): add cheat sheet and install.sh
```
```txt
```text
docs(ssh-adduser): document that foo does bar
```

View File

@@ -14,7 +14,7 @@ function getAllReleases(request, formula) {
return request({
url: 'https://formulae.brew.sh/api/formula/' + formula + '.json',
fail: true, // https://git.coolaj86.com/coolaj86/request.js/issues/2
json: true
json: true,
})
.then(failOnBadStatus)
.then(function (resp) {
@@ -26,16 +26,16 @@ function getAllReleases(request, formula) {
return [
{
version: ver,
download: dl.replace(/{{ v }}/g, ver)
}
download: dl.replace(/{{ v }}/g, ver),
},
].concat(
resp.body.versioned_formulae.map(function (f) {
var ver = f.replace(/.*@/, '');
return {
version: ver,
download: dl
download: dl,
};
})
}),
);
})
.catch(function (err) {

218
_common/git-tag.js Normal file
View File

@@ -0,0 +1,218 @@
'use strict';
require('dotenv').config({ path: '.env' });
var Crypto = require('crypto');
var util = require('util');
var exec = util.promisify(require('child_process').exec);
var Fs = require('node:fs/promises');
var FsSync = require('node:fs');
var Path = require('node:path');
var repoBaseDir = process.env.REPO_BASE_DIR || '';
if (!repoBaseDir) {
repoBaseDir = Path.resolve('./_repos');
// for stderr
console.error(`[Warn] REPO_BASE_DIR= not set, ${repoBaseDir}`);
}
var Repos = {};
Repos.clone = async function (repoPath, gitUrl) {
let uuid = Crypto.randomUUID();
let tmpPath = `${repoPath}.${uuid}.tmp`;
let bakPath = `${repoPath}.${uuid}.dup`;
await exec(`git clone --bare --filter=tree:0 ${gitUrl} ${tmpPath}`);
try {
FsSync.accessSync(repoPath);
return;
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
// sync to avoid race conditions
try {
FsSync.renameSync(repoPath, bakPath);
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
FsSync.renameSync(tmpPath, repoPath);
await Fs.rm(bakPath, { force: true, recursive: true });
};
Repos.checkExists = async function (repoPath) {
let err = await Fs.access(repoPath).catch(Object);
if (!err) {
return true;
}
if (err.code !== 'ENOENT') {
throw err;
}
return false;
};
Repos.fetch = async function (repoPath) {
await exec(`git --git-dir=${repoPath} fetch`);
};
Repos.getTags = async function (repoPath) {
var { stdout } = await exec(`git --git-dir=${repoPath} tag`);
var rawTags = stdout.trim().split('\n');
let tags = [];
for (let tag of rawTags) {
// ex: v1, v2, v1.1, 1.1.0-rc
let maybeVersionRe = /^(v\d+|v?\d+\.\d+)/;
let maybeVersion = maybeVersionRe.test(tag);
if (maybeVersion) {
tags.push(tag);
}
}
tags = tags.reverse();
return tags;
};
Repos.getTipInfo = async function (repoPath) {
var { stdout } = await exec(
`git --git-dir=${repoPath} rev-parse --abbrev-ref HEAD`,
);
var branch = stdout.trim();
var info = await Repos.getCommitInfo(repoPath, 'HEAD');
info.commitish = branch;
return info;
};
Repos.getCommitInfo = async function (repoPath, commitish) {
var { stdout } = await exec(
`git --git-dir=${repoPath} log -1 --format="%h %H %ad %cd" --date=iso-strict ${commitish}`,
);
stdout = stdout.trim();
var commitParts = stdout.split(/\s+/g);
return {
commitish: commitish,
commit_id: `${commitParts[0]}`,
commit: `${commitParts[1]}`,
date: commitParts[2],
date_authored: commitParts[3],
};
};
/**
* Lists GitHub Releases (w/ uploaded assets)
*
* @param request
* @param {string} owner
* @param {string} gitUrl
* @returns {PromiseLike<any> | Promise<any>}
*/
async function getAllReleases(gitUrl) {
let all = {
releases: [],
download: '',
};
let repoName = gitUrl.split('/').pop();
repoName = repoName.replace(/\.git$/, '');
let repoPath = `${repoBaseDir}/${repoName}.git`;
let isCloned = await Repos.checkExists(repoPath);
if (!isCloned) {
await Repos.clone(repoPath, gitUrl);
} else {
await Repos.fetch(repoPath);
}
let commitInfos = [];
let tags = await Repos.getTags(repoPath);
for (let tag of tags) {
let commitInfo = await Repos.getCommitInfo(repoPath, tag);
Object.assign(commitInfo, { version: tag, channel: '' });
commitInfos.push(commitInfo);
}
{
let tipInfo = await Repos.getTipInfo(repoPath);
// "2024-01-01T00:00:00-05:00" => "2024-01-01T05:00:00"
let date = new Date(tipInfo.date);
// "2024-01-01T05:00:00" => "v2024.01.01-05.00.00"
let version = date.toISOString();
// strip '.000Z'
version = version.replace(/\.\d+Z/, '');
version = version.replace(/[:\-]/g, '.');
version = version.replace(/T/, '-');
Object.assign(tipInfo, { version: `v${version}`, channel: '' });
if (commitInfos.length > 1) {
tipInfo.channel = 'beta';
}
commitInfos.push(tipInfo);
}
let releases = [];
for (let commitInfo of commitInfos) {
let version = commitInfo.version.replace(/^v/, '');
let date = new Date(commitInfo.date);
let isoDate = date.toISOString();
isoDate = isoDate.replace(/\.\d+Z/, '');
// tags and HEAD qualify for '--branch <branchish>'
let branch = commitInfo.commitish;
let rel = {
name: `${repoName}-v${version}`,
version: version,
git_tag: branch,
git_commit_hash: commitInfo.commit_id,
lts: false,
channel: commitInfo.channel,
date: isoDate,
os: '*',
arch: '*',
ext: 'git',
download: gitUrl,
};
releases.push(rel);
}
all.releases = releases;
return all;
}
module.exports = getAllReleases;
if (module === require.main) {
(async function main() {
let testRepos = [
// just a few tags, and a different HEAD
'https://github.com/tpope/vim-commentary.git',
// no tags, just HEAD
'https://github.com/ziglang/zig.vim.git',
// many, many tags
//'https://github.com/dense-analysis/ale.git',
];
for (let url of testRepos) {
let all = await getAllReleases(url);
all = require('../_webi/normalize.js')(all);
console.info(JSON.stringify(all, null, 2));
}
})()
.then(function () {
process.exit(0);
})
.catch(function (err) {
console.error(err);
});
}

View File

@@ -15,11 +15,11 @@ function getAllReleases(request, owner, repo, baseurl) {
if (!baseurl) {
return Promise.reject('missing baseurl');
}
return ghRelease(request, owner, repo, baseurl + '/api/v1').then(function (
all
) {
return all;
});
return ghRelease(request, owner, repo, baseurl + '/api/v1').then(
function (all) {
return all;
},
);
}
module.exports = getAllReleases;
@@ -29,11 +29,11 @@ if (module === require.main) {
require('@root/request'),
'coolaj86',
'go-pathman',
'https://git.coolaj86.com'
'https://git.coolaj86.com',
).then(
//getAllReleases(require('@root/request'), 'root', 'serviceman', 'https://git.rootprojects.org').then(
function (all) {
console.info(JSON.stringify(all, null, 2));
}
},
);
}

133
_common/github-source.js Normal file
View File

@@ -0,0 +1,133 @@
'use strict';
require('dotenv').config();
/**
* Gets the releases for 'ripgrep'. This function could be trimmed down and made
* for use with any github release.
*
* @param request
* @param {string} owner
* @param {string} repo
* @returns {PromiseLike<any> | Promise<any>}
*/
async function getAllReleases(
request,
owner,
repo,
oses,
arches,
baseurl = 'https://api.github.com',
) {
if (!owner) {
return Promise.reject('missing owner for repo');
}
if (!repo) {
return Promise.reject('missing repo name');
}
let req = {
url: `${baseurl}/repos/${owner}/${repo}/releases`,
json: true,
};
// TODO I really don't like global config, find a way to do better
if (process.env.GITHUB_USERNAME) {
req.auth = {
user: process.env.GITHUB_USERNAME,
pass: process.env.GITHUB_TOKEN,
};
}
let resp = await request(req);
let gHubResp = resp.body;
let all = {
releases: [],
// TODO make this ':baseurl' + ':releasename'
download: '',
};
for (let release of gHubResp) {
// TODO tags aren't always semver / sensical
let tag = release['tag_name'];
let lts = /(\b|_)(lts)(\b|_)/.test(release['tag_name']);
let channel = 'stable';
if (release['prerelease']) {
channel = 'beta';
}
let date = release['published_at'] || '';
date = date.replace(/T.*/, '');
let urls = [release.tarball_url, release.zipball_url];
for (let url of urls) {
let resp = await request({
method: 'HEAD',
followRedirect: true,
followAllRedirects: true,
followOriginalHttpMethod: true,
url: url,
stream: true,
});
// Workaround for bug where method changes to GET
resp.destroy();
// content-disposition: attachment; filename=BeyondCodeBootcamp-DuckDNS.sh-v1.0.1-0-ga2f4bde.zip
let name = resp.headers['content-disposition'].replace(
/.*filename=([^;]+)(;|$)/,
'$1',
);
all.releases.push({
name: name,
version: tag,
lts: lts,
channel: channel,
date: date,
os: '', // will be guessed by download filename
arch: '', // will be guessed by download filename
ext: '', // will be normalized
download: resp.request.uri.href,
});
}
}
if (oses) {
return combinate(all, oses, arches);
}
return all;
}
function combinate(all, oses, arches) {
let releases = all.releases;
// ex: arches = ['amd64', 'arm64', 'armv7l', 'armv6l', 'x86'];
// ex: oses = ['macos', 'linux', 'bsd', 'posix'];
let combos = [];
for (let release of releases) {
for (let arch of arches) {
for (let os of oses) {
let combo = {
arch: arch,
os: os,
};
let rel = Object.assign({}, release, combo);
combos.push(rel);
}
}
}
all.releases = combos;
return all;
}
module.exports = getAllReleases;
if (module === require.main) {
getAllReleases(
require('@root/request'),
'BeyondCodeBootcamp',
'DuckDNS.sh',
).then(function (all) {
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -3,66 +3,92 @@
require('dotenv').config();
/**
* Gets the releases for 'ripgrep'. This function could be trimmed down and made
* for use with any github release.
* Lists GitHub Releases (w/ uploaded assets)
*
* @param request
* @param {string} owner
* @param {string} repo
* @returns {PromiseLike<any> | Promise<any>}
*/
function getAllReleases(
async function getAllReleases(
request,
owner,
repo,
baseurl = 'https://api.github.com'
baseurl = 'https://api.github.com',
) {
if (!owner) {
return Promise.reject('missing owner for repo');
throw new Error('missing owner for repo');
}
if (!repo) {
return Promise.reject('missing repo name');
throw new Error('missing repo name');
}
var req = {
url: `${baseurl}/repos/${owner}/${repo}/releases`,
json: true
json: true,
};
// TODO I really don't like global config, find a way to do better
if (process.env.GITHUB_USERNAME) {
req.auth = {
user: process.env.GITHUB_USERNAME,
pass: process.env.GITHUB_TOKEN
pass: process.env.GITHUB_TOKEN,
};
}
return request(req).then((resp) => {
const gHubResp = resp.body;
const all = {
releases: [],
// todo make this ':baseurl' + ':releasename'
download: ''
};
let resp = await request(req);
if (!resp.ok) {
console.error('Bad Resp Headers:', resp.headers);
console.error('Bad Resp Body:', resp.body);
throw new Error('the elusive releases BOOGEYMAN strikes again');
}
gHubResp.forEach((release) => {
release['assets'].forEach((asset) => {
const name = asset['name'];
all.releases.push({
name: name,
version: release['tag_name'], // TODO tags aren't always semver / sensical
lts: /(\b|_)(lts)(\b|_)/.test(release['tag_name']),
channel: !release['prerelease'] ? 'stable' : 'beta',
date: (release['published_at'] || '').replace(/T.*/, ''),
os: '', // will be guessed by download filename
arch: '', // will be guessed by download filename
ext: '', // will be normalized
download: asset['browser_download_url']
});
let gHubResp = resp.body;
let all = {
releases: [],
// todo make this ':baseurl' + ':releasename'
download: '',
};
try {
gHubResp.forEach(transformReleases);
} catch (e) {
console.error(e.message);
console.error('Error Headers:', resp.headers);
console.error('Error Body:', resp.body);
throw e;
}
function transformReleases(release) {
for (let asset of release['assets']) {
let name = asset['name'];
let date = release['published_at']?.replace(/T.*/, '');
let download = asset['browser_download_url'];
// TODO tags aren't always semver / sensical
let version = release['tag_name'];
let channel;
if (release['prerelease']) {
// -rcX, -preview, -beta, etc will be checked in _webi/normalize.js
channel = 'beta';
}
let lts = /(\b|_)(lts)(\b|_)/.test(release['tag_name']);
all.releases.push({
name: name,
version: version,
lts: lts,
channel: channel,
date: date,
os: '', // will be guessed by download filename
arch: '', // will be guessed by download filename
ext: '', // will be normalized
download: download,
});
});
}
}
return all;
});
return all;
}
module.exports = getAllReleases;
@@ -71,6 +97,6 @@ if (module === require.main) {
getAllReleases(require('@root/request'), 'BurntSushi', 'ripgrep').then(
function (all) {
console.info(JSON.stringify(all, null, 2));
}
},
);
}

View File

@@ -20,7 +20,7 @@ etc).
These are the files / directories that are created and/or modified with this
install:
```txt
```text
~/.config/envman/PATH.env
~/.local/bin/foo
~/.local/opt/foo

View File

@@ -15,47 +15,45 @@ $pkg_src_bin = "$Env:USERPROFILE\.local\opt\foobar-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\foobar-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | out-null
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"))
{
echo "Downloading foobar from $Env:WEBI_PKG_URL to $pkg_download"
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading foobar from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& move "$pkg_download.part" "$pkg_download"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd"))
{
echo "Installing foobar"
IF (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing foobar"
# TODO: create package-specific temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\foobar-v*" -Recurse -ErrorAction Ignore
Remove-Item -Path ".\foo.exe" -Recurse -ErrorAction Ignore
# Remove any leftover tmp cruft
Remove-Item -Path ".\foobar-v*" -Recurse -ErrorAction Ignore
Remove-Item -Path ".\foo.exe" -Recurse -ErrorAction Ignore
# NOTE: DELETE THIS COMMENT IF NOT USED
# Move single binary into root of temporary folder
#& move "$pkg_download" "foo.exe"
# NOTE: DELETE THIS COMMENT IF NOT USED
# Move single binary into root of temporary folder
#& move "$pkg_download" "foo.exe"
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
echo "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Settle unpacked archive into place
echo "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | out-null
Move-Item -Path ".\foobar-*\foo.exe" -Destination "$pkg_src_bin"
# Settle unpacked archive into place
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Move-Item -Path ".\foobar-*\foo.exe" -Destination "$pkg_src_bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | out-null
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

View File

@@ -1,15 +1,12 @@
'use strict';
var pkg = require('../package.json');
var spawn = require('child_process').spawn;
var os = require('os');
var path = require('path');
function spawner(args) {
return new Promise(function (resolve, reject) {
var bin = args.shift();
var runner = spawn(bin, args, {
windowsHide: true
windowsHide: true,
});
runner.stdout.on('data', function (chunk) {
console.info(chunk.toString('utf8'));

View File

@@ -19,7 +19,7 @@ if (/^win/i.test(os.platform())) {
return;
}
exec('curl -fsS https://webi.sh/webi | sh', function(err, stdout, stderr) {
exec('curl -fsS https://webi.sh/webi | sh', function (err, stdout, stderr) {
if (err) {
console.error(err);
}

57
_scripts/git-hooks-init.js Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env node
'use strict';
var Fs = require('node:fs/promises');
var Path = require('node:path');
async function copyScripts() {
var pkgDir = Path.join(__dirname, '..');
var scriptsDir = Path.join(pkgDir, '_scripts');
var gitFile = Path.join(pkgDir, '.git');
// if this is a submodule, '.git' will be a file with a
// path to the actual git module directory
var gitDir = await Fs.readFile(gitFile, 'utf8')
.catch(function (e) {
// console.error(e);
//return 'gitdir: ../.git/modules/installers';
return 'gitdir: ./.git';
})
.then(function (str) {
var parts = str.split(': ');
str = parts[1];
str = str.trim();
return Path.resolve(pkgDir, str);
});
var gitHooksDir = Path.join(gitDir, 'hooks');
var src = Path.join(scriptsDir, 'git-hooks-pre-commit.js');
var dst = Path.join(gitHooksDir, 'pre-commit');
console.info(`[git-hooks] Checking for pre-commit hooks...`);
var relSrc = Path.relative(pkgDir, src);
var relDst = Path.relative(pkgDir, dst);
await Fs.access(dst)
.then(function () {
console.info(`[git-hooks] Found ${relDst}`);
})
.catch(async function (e) {
// ignore e
await Fs.mkdir(gitHooksDir, { recursive: true });
await Fs.copyFile(src, dst);
await Fs.chmod(dst, 0o755);
console.info(`[git-hooks] Found template ${relSrc}`);
console.info(`[git-hooks] Initialized ${relDst}`);
});
}
copyScripts()
.then(function () {
process.exit(0);
})
.catch(function (e) {
console.error(e);
process.exit(1);
});

7
_scripts/git-hooks-pre-commit Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
set -u
npm run fmt
npm run lint
npm run test

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env node
'use strict';
var Process = require('child_process');
var procOpts = { stdio: 'inherit' };
var commands = ['npm run fmt', 'npm run lint', 'npm run test'];
for (let cmd of commands) {
console.info(`[pre-commit] exec: ${cmd}`);
Process.execSync(cmd, procOpts);
}

15
_scripts/install-ci-deps Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
set -e
set -u
# Install 'shfmt 3.7.x'
curl -fsS https://webi.sh/shfmt@3.7 | sh
# Install 'shellcheck v0.9.x'
curl -fsS https://webi.sh/shellcheck@0.9 | sh
# Install 'pwsh (PowerShell Core) v7.x'
curl -fsS https://webi.sh/pwsh@7 | sh
# shellcheck disable=SC1090
. ~/.config/envman/PATH.env
pwsh -Command "Install-Module -Name PSScriptAnalyzer -Scope CurrentUser -AllowClobber"

27
_scripts/pwsh-fmt Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/sh
set -e
set -u
echo "Formatting */*.ps1 ... "
for my_ps1 in */*.ps1; do
my_dir="$(
dirname "${my_ps1}"
)"
if test -L "${my_ps1}" ||
test -L "${my_dir}" ||
! test -f "${my_ps1}" ||
! test -d "${my_dir}"; then
printf ' SKIP %s (non-regular file or parent directory)\n' "${my_ps1}"
continue
fi
printf " %s" "${my_ps1}"
# -Settings ./Settings/CodeFormatting.psd1
my_new_file="$(
pwsh -Command "Invoke-Formatter -ScriptDefinition (Get-Content -Path '${my_ps1}' -Raw)"
)"
printf '%s\n' "${my_new_file}" > "${my_ps1}"
printf '\n'
done

40
_scripts/pwsh-fmt.ps1 Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env pwsh
$ErrorActionPreference = "Stop"
Write-Host "Formatting */*.ps1 ... "
$my_cwd = Get-Location
$my_dirs = Get-ChildItem -Directory
$my_status = 0
foreach ($my_dir in $my_dirs) {
$my_files = Get-ChildItem -Path $my_dir.FullName -File -Filter *.ps1
foreach ($my_file in $my_files) {
$my_ps1 = [System.IO.Path]::GetRelativePath($my_cwd, $my_file.FullName)
$my_dir = [System.IO.Path]::GetDirectoryName($my_file.FullName)
if (-Not (Test-Path -PathType Leaf -Path $my_ps1) -or
-Not (Test-Path -PathType Container -Path $my_dir)) {
Write-Host (" SKIP {0} (non-regular file or parent directory)" -f $my_ps1)
continue
}
Write-Host (" {0}" -f $my_ps1)
$text = Get-Content -Path $my_ps1 -Raw
$my_new_file = Invoke-Formatter -ScriptDefinition $text
$my_new_file = $my_new_file.Trim()
# note: trailing newline is added back on write
$my_new_file | Set-Content -Path $my_ps1
$my_new_file = $my_new_file + "`n"
IF ($text -ne $my_new_file) {
$my_status = 1
}
}
}
exit $my_status

36
_scripts/pwsh-lint Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/sh
set -e
set -u
echo "Formatting */*.ps1 ..."
for my_ps1 in */*.ps1; do
my_dir="$(
dirname "${my_ps1}"
)"
if test -L "${my_ps1}" ||
test -L "${my_dir}" ||
! test -f "${my_ps1}" ||
! test -d "${my_dir}"; then
printf ' SKIP %s (non-regular file or parent directory)\n' "${my_ps1}"
continue
fi
printf " %s" "${my_ps1}"
# -Settings ./PSScriptAnalyzerSettings.psd1
pwsh -Command "Invoke-ScriptAnalyzer -Fix -ExcludeRule PSAvoidUsingWriteHost,PSUseDeclaredVarsMoreThanAssignment -Path '$my_ps1'"
#
# fmt MUST happen after lint due to Byte-Order Marker (BOM) issues
# See https://github.com/PowerShell/PSScriptAnalyzer/issues/1743
#
# -Settings ./Settings/CodeFormatting.psd1
my_new_file="$(
pwsh -Command "Invoke-Formatter -ScriptDefinition (Get-Content -Path '${my_ps1}' -Raw)"
)"
printf '%s\n' "${my_new_file}" > "${my_ps1}"
printf '\n'
done

48
_scripts/pwsh-lint.ps1 Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env pwsh
$ErrorActionPreference = "Stop"
Write-Host "Linting */*.ps1 ... "
$my_cwd = Get-Location
$my_dirs = Get-ChildItem -Directory
$my_status = 0
foreach ($my_dir in $my_dirs) {
$my_files = Get-ChildItem -Path $my_dir.FullName -File -Filter *.ps1
foreach ($my_file in $my_files) {
$my_ps1 = [System.IO.Path]::GetRelativePath($my_cwd, $my_file.FullName)
$my_dir = [System.IO.Path]::GetDirectoryName($my_file.FullName)
if (-Not (Test-Path -PathType Leaf -Path $my_ps1) -or
-Not (Test-Path -PathType Container -Path $my_dir)) {
Write-Host (" SKIP {0} (non-regular file or parent directory)" -f $my_ps1)
continue
}
Write-Host (" {0}" -f $my_ps1)
$my_old_file = (Get-Content -Path $my_ps1 -Raw)
Invoke-ScriptAnalyzer -Fix -ExcludeRule PSAvoidUsingWriteHost, PSUseDeclaredVarsMoreThanAssignment -Path $my_ps1
#
# fmt MUST happen after lint due to Byte-Order Marker (BOM) issues
# See https://github.com/PowerShell/PSScriptAnalyzer/issues/1743
#
$my_fixed_file = (Get-Content -Path $my_ps1 -Raw)
$my_new_file = Invoke-Formatter -ScriptDefinition $my_fixed_file
$my_new_file = $my_new_file.Trim()
# note: trailing newline is added back on write
$my_new_file | Set-Content -Path $my_ps1
$my_new_file = $my_new_file + "`n"
IF ($my_old_file -ne $my_new_file) {
$my_status = 1
}
}
}
exit $my_status

32
_scripts/tooling-init.js Executable file
View File

@@ -0,0 +1,32 @@
#!/usr/bin/env node
'use strict';
var Cmd = require('node:child_process');
var Os = require('node:os');
var procOpts = { stdio: 'inherit' };
var commands = ['shfmt --version', 'shellcheck --version'];
for (let cmd of commands) {
console.info(`[tooling-init] checking for '${cmd}':`);
try {
Cmd.execSync(cmd, procOpts);
} catch (e) {
// ignore e
printInstallHelp(cmd);
process.exit(1);
}
}
function printInstallHelp(cmd) {
console.error('');
console.error('ERROR');
console.error(` could not run '${cmd}'`);
console.error('POSSIBLE FIX');
if (/^win/i.test(Os.platform())) {
console.error(` curl.exe https://webi.ms/${cmd} | powershell`);
} else {
console.error(` curl -fsS https://webi.sh/${cmd} | sh`);
}
console.error('');
}

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env pwsh
IF (!(Test-Path -Path "$Env:USERPROFILE\.vim\pack\plugins\start")) {
New-Item -Path "$Env:USERPROFILE\.vim\pack\plugins\start" -ItemType Directory -Force | out-null
New-Item -Path "$Env:USERPROFILE\.vim\pack\plugins\start" -ItemType Directory -Force | Out-Null
}
Remove-Item -Path "$Env:USERPROFILE\.vim\pack\plugins\start\example" -Recurse -ErrorAction Ignore
& git clone --depth=1 https://github.com/CHANGEME/example.git "$Env:USERPROFILE\.vim\pack\plugins\start\example"

View File

@@ -1,28 +0,0 @@
setlocal
@echo off
pushd "%userprofile%" || goto :error
IF NOT EXIST .local (
mkdir .local || goto :error
)
IF NOT EXIST .local\bin (
mkdir .local\bin || goto :error
)
rem {{ baseurl }}
rem {{ version }}
pushd .local\bin || goto :error
if NOT EXIST webi.bat (
rem without SilentlyContinue this is SLOOOOOOOOOOOOOOOW!
powershell $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://webinstall.dev/packages/_webi/webi.bat -OutFile webi.bat || goto :error
)
call .\webi {{ exename }} || goto :error
rem pathman add "%userprofile%\.local\bin" >nul 2>&1 || goto :error
pathman add "%userprofile%\.local\bin" || goto :error
popd || goto :error
popd
goto :EOF
:error
echo Failed with error #%errorlevel%.
exit /b %errorlevel%

View File

@@ -1,230 +0,0 @@
#!/bin/sh
#<pre>
############################################################
# <h1>Cheat Sheet at CHEATSHEET_URL</h1>
# <meta http-equiv="refresh" content="3; URL='CHEATSHEET_URL'" />
############################################################
#set -x
__install_webi() {
#WEBI_PKG=
#WEBI_HOST=https://webinstall.dev
export WEBI_HOST
echo ""
printf "Thanks for using webi to install '\e[32m%s\e[0m' on '\e[31m%s/%s\e[0m'.\n" "${WEBI_PKG:-}" "$(uname -s)" "$(uname -m)"
echo "Have a problem? Experience a bug? Please let us know:"
echo " https://github.com/webinstall/webi-installers/issues"
echo ""
printf "\e[31mLovin'\e[0m it? Say thanks with a \e[34mStar on GitHub\e[0m:\n"
printf " \e[32mhttps://github.com/webinstall/webi-installers\e[0m\n"
echo ""
WEBI_WELCOME=true
export WEBI_WELCOME
set -e
set -u
mkdir -p "$HOME/.local/bin"
cat << EOF > "$HOME/.local/bin/webi"
#!/bin/sh
set -e
set -u
#set -x
__webi_main() {
export WEBI_TIMESTAMP="\$(date +%F_%H-%M-%S)"
export _webi_tmp="\${_webi_tmp:-\$(mktemp -d -t webi-\$WEBI_TIMESTAMP.XXXXXXXX)}"
if [ -n "\${_WEBI_PARENT:-}" ]; then
export _WEBI_CHILD=true
else
export _WEBI_CHILD=
fi
export _WEBI_PARENT=true
##
## Detect acceptable package formats
##
my_ext=""
set +e
# NOTE: the order here is least favorable to most favorable
if [ -n "\$(command -v pkgutil)" ]; then
my_ext="pkg,\$my_ext"
fi
# disable this check for the sake of building the macOS installer on Linux
#if [ -n "\$(command -v diskutil)" ]; then
# note: could also detect via hdiutil
my_ext="dmg,\$my_ext"
#fi
if [ -n "\$(command -v git)" ]; then
my_ext="git,\$my_ext"
fi
if [ -n "\$(command -v unxz)" ]; then
my_ext="xz,\$my_ext"
fi
if [ -n "\$(command -v unzip)" ]; then
my_ext="zip,\$my_ext"
fi
# for mac/linux 'exe' refers to the uncompressed binary without extension
my_ext="exe,\$my_ext"
if [ -n "\$(command -v tar)" ]; then
my_ext="tar,\$my_ext"
fi
my_ext="\$(echo "\$my_ext" | sed 's/,$//')" # nix trailing comma
set -e
##
## Detect http client
##
set +e
export WEBI_CURL="\$(command -v curl)"
export WEBI_WGET="\$(command -v wget)"
set -e
export WEBI_HOST="\${WEBI_HOST:-https://webinstall.dev}"
export WEBI_UA="\$(uname -a)"
webinstall() {
my_package="\${1:-}"
if [ -z "\$my_package" ]; then
>&2 echo "Usage: webi <package>@<version> ..."
>&2 echo "Example: webi node@lts rg"
exit 1
fi
export WEBI_BOOT="\$(mktemp -d -t "\$my_package-bootstrap.\$WEBI_TIMESTAMP.XXXXXXXX")"
my_installer_url="\$WEBI_HOST/api/installers/\$my_package.sh?formats=\$my_ext"
set +e
if [ -n "\$WEBI_CURL" ]; then
curl -fsSL "\$my_installer_url" -H "User-Agent: curl \$WEBI_UA" \\
-o "\$WEBI_BOOT/\$my_package-bootstrap.sh"
else
wget -q "\$my_installer_url" --user-agent="wget \$WEBI_UA" \\
-O "\$WEBI_BOOT/\$my_package-bootstrap.sh"
fi
if ! [ \$? -eq 0 ]; then
>&2 echo "error fetching '\$my_installer_url'"
exit 1
fi
set -e
(
cd "\$WEBI_BOOT"
sh "\$my_package-bootstrap.sh"
)
rm -rf "\$WEBI_BOOT"
}
show_path_updates() {
if ! [ -n "\${_WEBI_CHILD}" ]; then
if [ -f "\$_webi_tmp/.PATH.env" ]; then
my_paths=\$(cat "\$_webi_tmp/.PATH.env" | sort -u)
if [ -n "\$my_paths" ]; then
printf 'PATH.env updated with:\\n'
printf "%s\\n" "\$my_paths"
printf '\\n'
printf "\\e[31mTO FINISH\\e[0m: copy, paste & run the following command:\\n"
printf "\\n"
printf " \\e[34msource ~/.config/envman/PATH.env\\e[0m\\n"
printf " (newly opened terminal windows will update automatically)\\n"
fi
rm -f "\$_webi_tmp/.PATH.env"
fi
fi
}
version() {
my_version=v1.1.15
printf "\\e[31mwebi\\e[32m %s\\e[0m Copyright 2020+ AJ ONeal\\n" "\${my_version}"
printf " \\e[34mhttps://webinstall.dev/webi\\e[0m\\n"
}
# show help if no params given or help flags are used
usage() {
echo ""
version
echo ""
printf "\\e[1mSUMMARY\\e[0m\\n"
echo " Webi is the best way to install the modern developer tools you love."
echo " It's fast, easy-to-remember, and conflict free."
echo ""
printf "\\e[1mUSAGE\\e[0m\\n"
echo " webi <thing1>[@version] [thing2] ..."
echo ""
printf "\\e[1mUNINSTALL\\e[0m\\n"
echo " Almost everything that is installed with webi is scoped to"
echo " ~/.local/opt/<thing1>, so you can remove it like so:"
echo ""
echo " rm -rf ~/.local/opt/<thing1>"
echo " rm -f ~/.local/bin/<thing1>"
echo ""
echo " Some packages have special uninstall instructions, check"
echo " https://webinstall.dev/<thing1> to be sure."
echo ""
printf "\\e[1mFAQ\\e[0m\\n"
printf " See \\e[34mhttps://webinstall.dev/faq\\e[0m\\n"
echo ""
printf "\\e[1mALWAYS REMEMBER\\e[0m\\n"
echo " Friends don't let friends use brew for simple, modern tools that don't need it."
echo " (and certainly not apt either **shudder**)"
echo ""
}
if [ \$# -eq 0 ] || echo "\$1" | grep -q -E '^(-V|--version|version)$'; then
version
exit 0
fi
if echo "\$1" | grep -q -E '^(-h|--help|help)$'; then
usage "\$@"
exit 0
fi
for pkgname in "\$@"
do
webinstall "\$pkgname"
done
show_path_updates
}
__webi_main "\$@"
EOF
chmod a+x "$HOME/.local/bin/webi"
if [ -n "${WEBI_PKG:-}" ]; then
"$HOME/.local/bin/webi" "${WEBI_PKG}"
else
echo ""
echo "Hmm... no WEBI_PKG was specified. This is probably an error in the script."
echo ""
echo "Please open an issue with this information: Package '${WEBI_PKG:-}' on '$(uname -s)/$(uname -m)'"
echo " https://github.com/webinstall/packages/issues"
echo ""
fi
}
__install_webi

958
_webi/builds-cacher.js Normal file
View File

@@ -0,0 +1,958 @@
'use strict';
var BuildsCacher = module.exports;
let Fs = require('node:fs/promises');
let Path = require('node:path');
let HostTargets = require('./build-classifier/host-targets.js');
let Lexver = require('./build-classifier/lexver.js');
let Triplet = require('./build-classifier/triplet.js');
let request = require('@root/request');
var ALIAS_RE = /^alias: (\w+)$/m;
var LEGACY_ARCH_MAP = {
'*': 'ANYARCH',
arm64: 'aarch64',
armv6l: 'armv6',
armv7l: 'armv7',
amd64: 'x86_64',
mipsle: 'mipsel',
mips64le: 'mips64el',
mipsr6le: 'mipsr6el',
mips64r6le: 'mips64r6el',
// yes... el for arm and mips, but le for ppc
// (perhaps the joke got old?)
ppc64el: 'ppc64le',
386: 'x86',
};
var LEGACY_OS_MAP = {
'*': 'ANYOS',
macos: 'darwin',
posix: 'posix_2017',
};
var TERMS_META = [
// pattern
'{ARCH}',
'{EXT}',
'{LIBC}',
'{NAME}',
'{OS}',
'{VENDOR}',
// // os-/arch-indepedent
// 'ANYARCH',
// 'ANYOS',
// // libc
// 'none',
// channel
'beta',
'dev',
'preview',
'stable',
];
/** @typedef {String} TripletString - {arch}-{vendor}-{os}-{libc} */
/** @typedef {String} VersionString */
/** @typedef {Object.<VersionString, Array<BuildAsset>>} PackagesByRelease */
/**
* @typedef ProjectInfo
* @prop {Array<BuildAsset>} releases
* @prop {Array<BuildAsset>} packages
* @prop {Object.<TripletString, PackagesByRelease>} releasesByTriplet
* @prop {Array<import('./build-classifier/types.js').ArchString>} arches
* @prop {Array<import('./build-classifier/types.js').OsString>} oses
* @prop {Array<import('./build-classifier/types.js').LibcString>} libcs
* @prop {Array<String>} channels
* @prop {Array<String>} formats
* @prop {Array<String>} triplets
* @prop {Array<String>} versions
* @prop {Array<String>} lexvers
* @prop {Object.<String, String>} lexversMap
*/
/**
* @typedef BuildAsset
* @prop {String} name
* @prop {String} version
* @prop {Boolean} lts
* @prop {String} date
* @prop {String} arch
* @prop {String} os
* @prop {String} libc
* @prop {String} ext
* @prop {String} download
*/
/**
* @typedef VersionTarget
* @prop {String} version
* @prop {Boolean} lts
* @prop {String} channel
*/
/** @typedef {TargetTriplet & HostTargetPartial} HostTarget */
/** @typedef {import('./build-classifier/types.js').TargetTriplet} TargetTriplet */
/**
* @typedef HostTargetPartial
* @prop {String} target.triplet - os-vendor-arch-libc
* @prop {Error} target.error
*/
async function getPartialHeader(path) {
let readme = `${path}/README.md`;
let head = await readFirstBytes(readme).catch(function (err) {
if (err.code !== 'ENOENT') {
console.warn(`warn: ${path}: ${err.message}`);
}
return null;
});
return head;
}
// let fsOpen = util.promisify(Fs.open);
// let fsRead = util.promisify(Fs.read);
async function readFirstBytes(path) {
let start = 0;
let n = 1024;
let fh = await Fs.open(path, 'r');
let buf = Buffer.alloc(n);
let result = await fh.read(buf, start, n);
let str = result.buffer.toString('utf8');
await fh.close();
return str;
}
let promises = {};
async function getLatestBuilds(Releases, installersDir, cacheDir, name, date) {
if (!Releases) {
Releases = require(`${installersDir}/${name}/releases.js`);
}
// TODO update all releases files with module.exports.xxxx = 'foo';
if (!Releases.latest) {
Releases.latest = Releases;
}
let id = `${cacheDir}/${name}`;
if (!promises[id]) {
promises[id] = Promise.resolve();
}
promises[id] = promises[id].then(async function () {
return await getLatestBuildsInner(Releases, cacheDir, name, date);
});
return await promises[id];
}
async function getLatestBuildsInner(Releases, cacheDir, name, date) {
let data = await Releases.latest(request);
if (!date) {
date = new Date();
}
let isoDate = date.toISOString();
let yearMonth = isoDate.slice(0, 7);
// TODO hash file
let dataFile = `${cacheDir}/${yearMonth}/${name}.json`;
// TODO fsstat releases.js vs require-ing time as well
let tsFile = `${cacheDir}/${yearMonth}/${name}.updated.txt`;
let dirPath = Path.dirname(dataFile);
await Fs.mkdir(dirPath, { recursive: true });
let json = JSON.stringify(data, null, 2);
await Fs.writeFile(dataFile, json, 'utf8');
let seconds = date.valueOf();
let ms = seconds / 1000;
let msStr = ms.toFixed(3);
await Fs.writeFile(tsFile, msStr, 'utf8');
return data;
}
BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
let installersDir = installers;
let cacheDir = caches;
if (!ALL_TERMS) {
ALL_TERMS = Triplet.TERMS_PRIMARY_MAP;
}
let bc = {};
bc.usedTerms = {};
bc.orphanTerms = Object.assign({}, ALL_TERMS);
bc.unknownTerms = {};
bc.formats = [];
bc._triplets = {};
bc._targetsByBuildIdCache = {};
bc._caches = {};
bc._staleAge = 15 * 60 * 1000;
bc._allFormats = {};
bc._allTriplets = {};
for (let term of TERMS_META) {
delete bc.orphanTerms[term];
}
bc.getProjectsByType = async function () {
let dirs = {
hidden: {},
errors: {},
alias: {},
invalid: {},
selfhosted: {},
valid: {},
};
let entries = await Fs.readdir(installersDir, { withFileTypes: true });
for (let entry of entries) {
let meta = await bc.getProjectTypeByEntry(entry);
if (meta.type === 'not_found') {
let err = meta.detail;
console.error('');
console.error('PROBLEM');
console.error(` ${err.message}`);
console.error('');
console.error('SOLUTION');
console.error(' npm clean-install');
console.error('');
throw new Error(
'[SANITY FAIL] should never have missing modules in prod',
);
}
dirs[meta.type][entry.name] = meta.detail;
}
return dirs;
};
/**
* Get project type and detail - alias, selfhosted, valid (and the invalids)
* @param {String} name - filename
*/
bc.getProjectType = async function (name) {
let filepath = Path.join(installersDir, name);
let entry;
try {
entry = await Fs.lstat(filepath);
Object.assign(entry, { name: name });
} catch (e) {
return { type: 'errors', detail: 'not found' };
}
let info = await bc.getProjectTypeByEntry(entry);
return info;
};
/**
* Get project type and detail - alias, selfhosted, valid (and the invalids)
* @param {fs.Stats|fs.Dirent} entry
*/
bc.getProjectTypeByEntry = async function (entry) {
let path = Path.join(installersDir, entry.name);
// skip non-installer dirs
if (entry.isSymbolicLink()) {
let link = await Fs.readlink(path);
return { type: 'alias', detail: link };
}
if (!entry.isDirectory()) {
return { type: 'hidden', detail: '!directory' };
}
if (entry.name === 'node_modules') {
return { type: 'hidden', detail: 'node_modules' };
}
if (entry.name.startsWith('_')) {
return { type: 'hidden', detail: '_*' };
}
if (entry.name.startsWith('.')) {
return { type: 'hidden', detail: '.*' };
}
if (entry.name.startsWith('~')) {
return { type: 'hidden', detail: '~*' };
}
if (entry.name.endsWith('~')) {
return { type: 'hidden', detail: '*~' };
}
// skip invalid installers
let head = await getPartialHeader(path);
if (!head) {
return { type: 'invalid', detail: '!README.md' };
}
let alias = head.match(ALIAS_RE);
if (alias) {
let link = alias[1];
return { type: 'alias', detail: link };
}
let releasesPath = Path.join(path, 'releases.js');
try {
void require(releasesPath);
} catch (err) {
if (err.code !== 'MODULE_NOT_FOUND') {
return { type: 'errors', detail: err };
}
if (err.message.includes(`Cannot find module '${releasesPath}'`)) {
return { type: 'selfhosted', detail: true };
}
return { type: 'not_found', detail: err };
}
return { type: 'valid', detail: true };
};
// Typically a package is organized by release (ex: go has 1.20, 1.21, etc),
// but we will organize by the build (ex: go1.20-darwin-arm64.tar.gz, etc).
bc.getPackages = async function ({ Releases, name, date }) {
if (!date) {
date = new Date();
}
let isoDate = date.toISOString();
let yearMonth = isoDate.slice(0, 7);
let dataFile = `${cacheDir}/${yearMonth}/${name}.json`;
// let secondsStr = await Fs.readFile(tsFile, 'ascii').catch(function (err) {
// if (err.code !== 'ENOENT') {
// throw err;
// }
// return '0';
// });
// secondsStr = secondsStr.trim();
// let seconds = parseFloat(secondsStr) || 0;
// let age = now - seconds;
let projInfo = bc._caches[name];
let meta = {
// version info
versions: projInfo?.versions || [],
lexvers: projInfo?.lexvers || [],
lexversMap: projInfo?.lexversMap || {},
// culled release assets
packages: projInfo?.packages || [],
releasesByTriplet: projInfo?.releasesByTriplet || {},
// target info
triplets: projInfo?.triplets || [],
oses: projInfo?.oses || [],
arches: projInfo?.arches || [],
libcs: projInfo?.libcs || [],
formats: projInfo?.formats || [],
// TODO channels: projInfo?.channels || [],
};
if (!projInfo) {
let json = await Fs.readFile(dataFile, 'ascii').catch(
async function (err) {
if (err.code !== 'ENOENT') {
throw err;
}
return null;
},
);
try {
projInfo = JSON.parse(json);
} catch (e) {
console.error(`error: ${dataFile}:\n\t${e.message}`);
projInfo = null;
}
}
if (!projInfo) {
projInfo = await getLatestBuilds(Releases, installersDir, cacheDir, name);
}
process.nextTick(async function () {
let now = date.valueOf();
let age = now - projInfo.updated;
if (age < bc._staleAge) {
return;
}
projInfo = await getLatestBuilds(Releases, installersDir, cacheDir, name);
transformAndUpdate(name, projInfo, meta, date);
});
transformAndUpdate(name, projInfo, meta, date);
return projInfo;
};
function transformAndUpdate(name, projInfo, meta, date) {
meta.packages = [];
let updated = date.valueOf();
Object.assign(projInfo, { name, updated }, meta);
for (let build of projInfo.releases) {
let buildTarget = bc.classify(projInfo, build);
if (!buildTarget) {
// ignore known, non-package extensions
continue;
}
if (buildTarget.error) {
let err = buildTarget.error;
let code = err.code || '';
console.error(`[ERROR]: ${code} ${projInfo.name}: ${build.name}`);
console.error(`>>> ${err.message} <<<`);
console.error(projInfo);
console.error(build);
console.error(`^^^ ${err.message} ^^^`);
console.error(err.stack);
continue;
}
if (!build.name) {
build.name = build.download.replace(/.*\//, '');
}
build.target = buildTarget;
meta.packages.push(build);
}
updateReleasesByTriplet(meta);
updateAndSortVersions(projInfo, meta);
Object.assign(projInfo, { name, updated }, meta);
bc._caches[name] = projInfo;
}
function updateReleasesByTriplet(meta) {
for (let build of meta.packages) {
let target = build.target;
let triplet = `${target.os}-${target.arch}-${target.libc}`;
if (!meta.releasesByTriplet[triplet]) {
meta.releasesByTriplet[triplet] = {};
}
let buildsByRelease = meta.releasesByTriplet[triplet];
if (!buildsByRelease[build.version]) {
buildsByRelease[build.version] = [];
}
let packages = buildsByRelease[build.version];
packages.push(build);
}
}
// TODO
// - tag channels
function updateAndSortVersions(projInfo, meta) {
for (let build of projInfo.packages) {
let hasVersion = meta.versions.includes(build.version);
if (!hasVersion) {
build.lexver = Lexver.parseVersion(build.version);
meta.lexversMap[build.lexver] = build.version;
}
}
meta.lexvers = Object.keys(meta.lexversMap);
meta.lexvers.sort();
meta.lexvers.reverse();
meta.versions = [];
for (let lexver of meta.lexvers) {
let version = meta.lexversMap[lexver];
meta.versions.push(version);
}
projInfo.packages.sort(function (a, b) {
if (a.lexver > b.lexver) {
return -1;
}
if (a.lexver < b.lexver) {
return 1;
}
return 0;
});
}
// Makes sure that packages are updated once an hour, on average
bc._staleNames = [];
bc._freshenTimeout = null;
bc.freshenRandomPackage = async function (minDelay) {
if (!minDelay) {
minDelay = 15 * 1000;
}
if (bc._staleNames.length === 0) {
let dirs = await bc.getProjectsByType();
bc._staleNames = Object.keys(dirs.valid);
bc._staleNames.sort(function () {
return 0.5 - Math.random();
});
}
let name = bc._staleNames.pop();
void (await bc.getPackages({
//Releases: Releases,
name: name,
date: new Date(),
}));
let hour = 60 * 60 * 1000;
let delay = minDelay;
let spread = hour / bc._staleNames.length;
let seed = Math.random();
delay += seed * spread;
clearTimeout(bc._freshenTimeout);
bc._freshenTimeout = setTimeout(bc.freshenRandomPackage, delay);
bc._freshenTimeout.unref();
};
bc.classify = function (projInfo, build) {
/* jshint maxcomplexity: 25 */
let maybeInstallable = Triplet.maybeInstallable(projInfo, build);
if (!maybeInstallable) {
return null;
}
if (LEGACY_OS_MAP[build.os]) {
build.os = LEGACY_OS_MAP[build.os];
}
if (LEGACY_ARCH_MAP[build.arch]) {
build.arch = LEGACY_ARCH_MAP[build.arch];
}
// because some packages are shimmed to match a single download against
let preTarget = Object.assign({ os: '', arch: '', libc: '' }, build);
let targetId = `${preTarget.os}:${preTarget.arch}:${preTarget.libc}`;
let buildId = `${projInfo.name}:${targetId}@${build.download}`;
let target = bc._targetsByBuildIdCache[buildId];
if (target) {
Object.assign(build, { target: target, triplet: target.triplet });
return target;
}
let pattern = Triplet.toPattern(projInfo, build);
if (!pattern) {
let err = new Error(`no pattern generated for ${projInfo.name}`);
err.code = 'E_BUILD_NO_PATTERN';
target = { error: err };
bc._targetsByBuildIdCache[buildId] = target;
return target;
}
let rawTerms = pattern.split(/[_\{\}\/\.\-]+/g);
for (let term of rawTerms) {
delete bc.orphanTerms[term];
bc.usedTerms[term] = true;
}
// {NAME}/{NAME}-{VER}-Windows-x86_64_v2-musl.exe =>
// {NAME}.windows.x86_64v2.musl.exe
let terms = Triplet.patternToTerms(pattern);
if (!terms.length) {
let err = new Error(`'${terms}' was trimmed to ''`);
target = { error: err };
bc._targetsByBuildIdCache[buildId] = target;
return target;
}
for (let term of terms) {
if (!term) {
continue;
}
if (ALL_TERMS[term]) {
delete bc.orphanTerms[term];
bc.usedTerms[term] = true;
continue;
}
bc.unknownTerms[term] = true;
}
// {NAME}.windows.x86_64v2.musl.exe
// windows-x86_64_v2-musl
target = { triplet: '' };
void Triplet.termsToTarget(target, projInfo, build, terms);
target.triplet = `${target.arch}-${target.vendor}-${target.os}-${target.libc}`;
{
// TODO I don't love this hidden behavior
// perhaps classify should just happen when the package is loaded
// (and the sanity error should be removed, or thrown after the loop is complete)
let hasTriplet = projInfo.triplets.includes(target.triplet);
if (!hasTriplet) {
projInfo.triplets.push(target.triplet);
}
let hasOs = projInfo.oses.includes(target.os);
if (!hasOs) {
projInfo.oses.push(target.os);
}
let hasArch = projInfo.arches.includes(target.arch);
if (!hasArch) {
projInfo.arches.push(target.arch);
}
let hasLibc = projInfo.libcs.includes(target.libc);
if (!hasLibc) {
projInfo.libcs.push(target.libc);
}
if (!build.ext) {
build.ext = Triplet.buildToPackageType(build);
}
if (build.ext) {
if (!build.ext.startsWith('.')) {
build.ext = `.${build.ext}`;
}
}
let hasExt = projInfo.formats.includes(build.ext);
if (!hasExt) {
projInfo.formats.push(build.ext);
}
let hasGlobalExt = bc.formats.includes(build.ext);
if (!hasGlobalExt) {
bc.formats.push(build.ext);
}
}
bc._triplets[target.triplet] = true;
bc._targetsByBuildIdCache[buildId] = target;
let triple = [target.arch, target.vendor, target.os, target.libc];
for (let term of triple) {
if (!ALL_TERMS[term]) {
throw new Error(
`[SANITY FAIL] '${projInfo.name}' '${target.triplet}' generated unknown term '${term}'`,
);
}
delete bc.orphanTerms[term];
bc.usedTerms[term] = true;
}
return target;
};
/**
* Given a list of acceptable formats, get the sorted list of of formats.
* Actually used (as per node _webi/lint-builds.js):
* .7z
* .app.zip
* .dmg
* .exe
* .exe.xz
* .git
* .gz
* .msi
* .pkg
* .pkg.tar.zst
* .sh
* .tar.gz
* .tar.xz
* .xz
* .zip
*/
bc.getSortedFormats = function (formats) {
/* jshint maxcomplexity: 25 */
formats.sort();
let id = formats.join(',');
if (bc._allFormats[id]) {
return bc._allFormats[id];
}
// we don't know how to handle any of these yet
// let exclude = [];
// let isAndroid = false;
// if (!isAndroid) {
// exclude.push('.apk');
// }
// let isDebian = false;
// if (!isDebian) {
// exclude.push('.deb');
// }
// let isEnterpriseLinux = false;
// if (!isEnterpriseLinux) {
// exclude.push('.rpm');
// }
// let isArch = false;
// if (!isArch) {
// exclude.push('.pkg.tar.zst');
// }
let hasExe = formats.includes('exe') || formats.includes('.exe');
/** @type {Array<String>} */
let exts = [];
let hasXz = formats.includes('xz') || formats.includes('.xz');
if (hasXz) {
exts.push('.tar.xz');
if (hasExe) {
exts.push('.exe.xz');
}
exts.push('.xz');
}
let hasZst = formats.includes('zst') || formats.includes('.zst');
if (hasZst) {
exts.push('.tar.zst');
exts.push('.zst');
}
let hasZip = formats.includes('zip') || formats.includes('.zip');
if (hasZip) {
exts.push('.zip');
}
let has7z = false;
if (has7z) {
exts.push('.7z');
}
// let hasBz2 = formats.includes('bz2') || formats.includes('.bz2');
// if (hasBz2) {
// exts.push('.bz2');
// }
if (hasExe) {
if (!hasZip) {
exts.push('.zip');
}
exts.push('.tar.gz');
exts.push('.gz');
exts.push('.exe');
exts.push('.msi');
//exts.push('.msixbundle');
} else {
exts.push('.tar.gz');
exts.push('.gz');
exts.push('.sh');
}
exts.push('.git');
// Fallbacks
exts.push('.app.zip');
exts.push('.dmg');
exts.push('.pkg');
if (!hasXz) {
exts.push('.tar.xz');
if (hasExe) {
exts.push('.exe.xz');
}
exts.push('.xz');
}
if (!hasZip) {
if (!hasExe) {
exts.push('.zip');
}
}
if (!hasZst) {
exts.push('.tar.zst');
exts.push('.zst');
}
if (!has7z) {
exts.push('.7z');
}
// if (!hasRar) {
// exts.push('.rar');
// }
// exts.push('.tar.bz2');
// exts.push('.bz2');
bc._allFormats[id] = exts;
return exts;
};
bc.selectPackage = function (packages, formats) {
if (packages.length === 1) {
return packages[0];
}
let exts = bc.getSortedFormats(formats);
for (let ext of exts) {
for (let build of packages) {
if (build.ext === ext) {
return build;
}
}
}
return packages[0];
};
/**
* @param {ProjectInfo} projInfo
* @param {HostTarget} hostTarget
*/
bc.enumerateLatestVersions = function (projInfo, hostTarget) {
let lexPrefix = '';
let matchInfo = Lexver.matchSorted(projInfo.lexvers, lexPrefix);
let verInfo = {
default: projInfo.lexversMap[matchInfo.default],
previous: projInfo.lexversMap[matchInfo.previous],
stable: projInfo.lexversMap[matchInfo.stable],
latest: projInfo.lexversMap[matchInfo.latest],
};
return verInfo;
};
/**
* @param {ProjectInfo} projInfo
* @param {HostTarget} hostTarget
* @param {VersionTarget} verTarget
*/
bc.findMatchingPackages = function (projInfo, hostTarget, verTarget) {
let matchInfo = bc._enumerateVersions(projInfo, verTarget.version);
let triplets = bc._enumerateTriplets(hostTarget);
//console.log('dbg: matchInfo', matchInfo);
if (matchInfo) {
for (let _triplet of triplets) {
let targetReleases = projInfo.releasesByTriplet[_triplet];
if (!targetReleases) {
continue;
}
// Make sure that these releases are the expected version
// (ex: jq1.7 => darwin-arm64-libc, jq1.6 => darwin-x86_64-libc)
for (let matchver of matchInfo.matches) {
let ver = projInfo.lexversMap[matchver] || matchver;
let packages = targetReleases[ver];
if (!packages) {
continue;
}
let match = {
triplet: _triplet,
packages: packages,
latest: projInfo.versions[0],
version: ver,
versions: matchInfo,
};
return match;
}
}
return null;
}
for (let _triplet of triplets) {
let targetReleases = projInfo.releasesByTriplet[_triplet];
if (!targetReleases) {
continue;
}
let versions = Object.keys(targetReleases);
//console.log('dbg: targetRelease versions', versions);
let lexvers = [];
for (let version of versions) {
let lexPrefix = Lexver.parseVersion(version);
lexvers.push(lexPrefix);
}
lexvers.sort();
lexvers.reverse();
// TODO get the other matchInfo props
// Make sure that these releases are the expected version
// (ex: jq1.7 => darwin-arm64-libc, jq1.6 => darwin-x86_64-libc)
for (let matchver of lexvers) {
let ver = projInfo.lexversMap[matchver] || matchver;
let packages = targetReleases[ver];
//console.log('dbg: packages', packages);
if (!packages) {
continue;
}
let pkg = packages[0];
if (verTarget.lts) {
if (!pkg.lts) {
continue;
}
let match = {
triplet: _triplet,
packages: packages,
latest: projInfo.versions[0],
version: ver,
versions: matchInfo,
};
return match;
}
let wantChannel = verTarget.channel || 'stable';
let isChannel = pkg.channel || 'stable';
if (wantChannel === 'stable') {
if (isChannel !== 'stable') {
continue;
}
}
// latest, beta, alpha, rc, preview
let match = {
triplet: _triplet,
packages: packages,
latest: projInfo.versions[0],
version: ver,
versions: matchInfo,
};
return match;
}
}
return null;
};
bc._enumerateTriplets = function (hostTarget) {
let id = [hostTarget.os, hostTarget.arch, hostTarget.libc].join(',');
let triplets = bc._allTriplets[id] || [];
if (triplets.length > 0) {
return triplets;
}
let oses = [];
if (hostTarget.os === 'windows') {
oses = ['ANYOS', 'windows'];
} else if (hostTarget.os === 'android') {
oses = ['ANYOS', 'posix_2017', 'android', 'linux'];
} else {
oses = ['ANYOS', 'posix_2017', hostTarget.os];
}
let waterfall = HostTargets.WATERFALL[hostTarget.os] || {};
let arches = waterfall[hostTarget.arch] ||
HostTargets.WATERFALL.ANYOS[hostTarget.arch] || [hostTarget.arch];
arches = ['ANYARCH'].concat(arches);
let libcs = waterfall[hostTarget.libc] ||
HostTargets.WATERFALL.ANYOS[hostTarget.libc] || [hostTarget.libc];
for (let os of oses) {
for (let arch of arches) {
for (let libc of libcs) {
let triplet = `${os}-${arch}-${libc}`;
triplets.push(triplet);
}
}
}
bc._allTriplets[id] = triplets;
return triplets;
};
bc._enumerateVersions = function (projInfo, ver) {
if (!ver) {
return null;
}
let lexPrefix = Lexver.parsePrefix(ver);
let matchInfo = Lexver.matchSorted(projInfo.lexvers, lexPrefix);
return matchInfo;
};
return bc;
};

41
_webi/builds.js Normal file
View File

@@ -0,0 +1,41 @@
'use strict';
let Builds = module.exports;
let Path = require('node:path');
let BuildsCacher = require('./builds-cacher.js');
// let HostTargets = require('./build-classifier/host-targets.js');
let Parallel = require('./parallel.js');
var INSTALLERS_DIR = Path.join(__dirname, '..');
var CACHE_DIR = Path.join(__dirname, '../_cache');
let bc = BuildsCacher.create({
caches: CACHE_DIR,
installers: INSTALLERS_DIR,
});
bc.freshenRandomPackage(600 * 1000);
Builds.init = async function () {
bc.freshenRandomPackage(600 * 1000);
let dirs = await bc.getProjects();
let projNames = Object.keys(dirs.valid);
let parallel = 25;
await Parallel.run(parallel, projNames, getAll);
async function getAll(name) {
void (await bc.getPackages({
//Releases: Releases,
name: name,
date: new Date(),
}));
}
};
Builds.enumerateLatestVersions = bc.enumerateLatestVersions;
Builds.findMatchingPackages = bc.findMatchingPackages;
Builds.getPackage = bc.getPackages;
Builds.getProjectType = bc.getProjectType;
Builds.selectPackage = bc.selectPackage;

View File

@@ -4,9 +4,9 @@
# <h1>Cheat Sheet at CHEATSHEET_URL</h1>
# <meta http-equiv="refresh" content="3; URL='CHEATSHEET_URL'" />
############################################################
New-Item -Path "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | out-null
New-Item -Path "$Env:USERPROFILE\.local\bin" -ItemType Directory -Force | out-null
IF ($Env:WEBI_HOST -eq $null -or $Env:WEBI_HOST -eq "") { $Env:WEBI_HOST = "https://webinstall.dev" }
curl.exe -s -A "windows" "$Env:WEBI_HOST/packages/_webi/webi-pwsh.ps1" -o "$Env:USERPROFILE\.local\bin\webi-pwsh.ps1"
New-Item -Path "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
New-Item -Path "$Env:USERPROFILE\.local\bin" -ItemType Directory -Force | Out-Null
IF ($null -eq $Env:WEBI_HOST -or $Env:WEBI_HOST -eq "") { $Env:WEBI_HOST = "https://webinstall.dev" }
curl.exe -s -A "windows" "$Env:WEBI_HOST/packages/webi/webi-pwsh.ps1" -o "$Env:USERPROFILE\.local\bin\webi-pwsh.ps1"
Set-ExecutionPolicy -Scope Process Bypass
& "$Env:USERPROFILE\.local\bin\webi-pwsh.ps1" "{{ exename }}"

View File

@@ -0,0 +1,338 @@
#!/bin/sh
#<pre>
############################################################
# <h1>Cheat Sheet at CHEATSHEET_URL</h1>
# <meta http-equiv="refresh" content="3; URL='CHEATSHEET_URL'" />
############################################################
export WEBI_PKG=webi
export WEBI_HOST=https://webinstall.dev
export WEBI_CHECKSUM=06a7fb9f
#########################################
# #
# Display Debug Info in Case of Failure #
# #
#########################################
fn_show_welcome() { (
echo ""
echo ""
# invert t_task and t_pkg for top-level welcome message
printf -- ">>> %s %s <<<\n" \
"$(t_pkg 'Welcome to') $(t_task 'Webi')$(t_pkg '!')" \
"$(t_dim "- modern tools, instant installs.")"
echo " We expect your experience to be $(t_em 'absolutely perfect')!"
echo ""
echo " $(t_attn 'Success')? Star it! $(t_url 'https://github.com/webinstall/webi-installers')"
echo " $(t_attn 'Problem')? Report it: $(t_url 'https://github.com/webinstall/webi-installers/issues')"
echo " $(t_dim "(your system is") $(t_host "$(fn_get_os)")/$(t_host "$(uname -m)") $(t_dim "with") $(t_host "$(fn_get_libc)") $(t_dim "&") $(t_host "$(fn_get_http_client_name)")$(t_dim ")")"
sleep 0.2
); }
fn_get_os() { (
# Ex:
# GNU/Linux
# Android
# Linux (often Alpine, musl)
# Darwin
b_os="$(uname -o 2> /dev/null || echo '')"
b_sys="$(uname -s)"
if test -z "${b_os}" || test "${b_os}" = "${b_sys}"; then
# ex: 'Darwin' (and plain, non-GNU 'Linux')
echo "${b_sys}"
return 0
fi
if echo "${b_os}" | grep -q "${b_sys}"; then
# ex: 'GNU/Linux'
echo "${b_os}"
return 0
fi
# ex: 'Android/Linux'
echo "${b_os}/${b_sys}"
); }
fn_get_libc() { (
# Ex:
# musl
# libc
if ldd /bin/ls 2> /dev/null | grep -q 'musl' 2> /dev/null; then
echo 'musl'
elif fn_get_os | grep -q 'GNU|Linux'; then
echo 'gnu'
else
echo 'libc'
fi
); }
fn_get_http_client_name() { (
# Ex:
# curl
# curl+wget
b_client=""
if command -v curl > /dev/null; then
b_client="curl"
fi
if command -v wget > /dev/null; then
if test -z "${b_client}"; then
b_client="wget"
else
b_client="curl+wget"
fi
fi
echo "${b_client}"
); }
#########################################
# #
# For Making the Display Nice #
# #
#########################################
# Term Types
t_cmd() { (fn_printf '\e[2m\e[35m%s\e[39m\e[22m' "${1}"); }
t_host() { (fn_printf '\e[2m\e[33m%s\e[39m\e[22m' "${1}"); }
t_link() { (fn_printf '\e[1m\e[36m%s\e[39m\e[22m' "${1}"); }
t_path() { (fn_printf '\e[2m\e[32m%s\e[39m\e[22m' "${1}"); }
t_pkg() { (fn_printf '\e[1m\e[32m%s\e[39m\e[22m' "${1}"); }
t_task() { (fn_printf '\e[36m%s\e[39m' "${1}"); }
t_url() { (fn_printf '\e[2m%s\e[22m' "${1}"); }
# Levels
t_info() { (fn_printf '\e[1m\e[36m%s\e[39m\e[22m' "${1}"); }
t_attn() { (fn_printf '\e[1m\e[33m%s\e[39m\e[22m' "${1}"); }
t_warn() { (fn_printf '\e[1m\e[33m%s\e[39m\e[22m' "${1}"); }
t_err() { (fn_printf '\e[31m%s\e[39m' "${1}"); }
# Styles
t_bold() { (fn_printf '\e[1m%s\e[22m' "${1}"); }
t_dim() { (fn_printf '\e[2m%s\e[22m' "${1}"); }
t_em() { (fn_printf '\e[3m%s\e[23m' "${1}"); }
t_under() { (fn_printf '\e[4m%s\e[24m' "${1}"); }
# FG Colors
t_cyan() { (fn_printf '\e[36m%s\e[39m' "${1}"); }
t_green() { (fn_printf '\e[32m%s\e[39m' "${1}"); }
t_magenta() { (fn_printf '\e[35m%s\e[39m' "${1}"); }
t_yellow() { (fn_printf '\e[33m%s\e[39m' "${1}"); }
fn_printf() { (
a_style="${1}"
a_text="${2}"
if fn_is_tty; then
#shellcheck disable=SC2059
printf -- "${a_style}" "${a_text}"
else
printf -- '%s' "${a_text}"
fi
); }
fn_sub_home() { (
my_rel=${HOME}
my_abs=${1}
echo "${my_abs}" | sed "s:^${my_rel}:~:"
); }
###################################
# #
# Detect HTTP Client #
# #
###################################
fn_wget() { (
# Doc:
# Downloads the file at the given url to the given path
a_url="${1}"
a_path="${2}"
cmd_wget="wget -c -q --user-agent"
if fn_is_tty; then
cmd_wget="wget -c -q --show-progress --user-agent"
fi
# busybox wget doesn't support --show-progress
# See <https://github.com/webinstall/webi-installers/pull/772>
if readlink "$(command -v wget)" | grep -q busybox; then
cmd_wget="wget --user-agent"
fi
b_triple_ua="$(fn_get_target_triple_user_agent)"
b_agent="webi/wget ${b_triple_ua}"
if command -v curl > /dev/null; then
b_agent="webi/wget+curl ${b_triple_ua}"
fi
if ! $cmd_wget "${b_agent}" "${a_url}" -O "${a_path}"; then
echo >&2 " $(t_err "failed to download (wget)") '$(t_url "${a_url}")'"
echo >&2 " $cmd_wget '${b_agent}' '${a_url}' -O '${a_path}'"
echo >&2 " $(wget -V)"
return 1
fi
); }
fn_curl() { (
# Doc:
# Downloads the file at the given url to the given path
a_url="${1}"
a_path="${2}"
cmd_curl="curl -f -sSL -#"
if fn_is_tty; then
cmd_curl="curl -f -sSL"
fi
b_triple_ua="$(fn_get_target_triple_user_agent)"
b_agent="webi/curl ${b_triple_ua}"
if command -v wget > /dev/null; then
b_agent="webi/curl+wget ${b_triple_ua}"
fi
if ! $cmd_curl -A "${b_agent}" "${a_url}" -o "${a_path}"; then
echo >&2 " $(t_err "failed to download (curl)") '$(t_url "${a_url}")'"
echo >&2 " $cmd_curl -A '${b_agent}' '${a_url}' -o '${a_path}'"
echo >&2 " $(curl -V)"
return 1
fi
); }
fn_get_target_triple_user_agent() { (
# Ex:
# x86_64/unknown GNU/Linux/5.15.107-2-pve gnu
# arm64/unknown Darwin/22.6.0 libc
echo "$(uname -m)/unknown $(fn_get_os)/$(uname -r) $(fn_get_libc)"
); }
fn_download_to_path() { (
a_url="${1}"
a_path="${2}"
mkdir -p "$(dirname "${a_path}")"
if command -v curl > /dev/null; then
fn_curl "${a_url}" "${a_path}.part"
elif command -v wget > /dev/null; then
fn_wget "${a_url}" "${a_path}.part"
else
echo >&2 " $(t_err "failed to detect HTTP client (curl, wget)")"
return 1
fi
mv "${a_path}.part" "${a_path}"
); }
##############################################
# #
# Install or Update Webi and Install Package #
# #
##############################################
webi_bootstrap() { (
a_path="${1}"
echo ""
echo "$(t_task 'Bootstrapping') $(t_pkg 'Webi')"
b_path_rel="$(fn_sub_home "${a_path}")"
b_checksum=""
if test -r "${a_path}"; then
b_checksum="$(fn_checksum "${a_path}")"
fi
if test "$b_checksum" = "${WEBI_CHECKSUM}"; then
echo " $(t_dim 'Found') $(t_path "${b_path_rel}")"
sleep 0.1
return 0
fi
b_webi_file_url="${WEBI_HOST}/packages/webi/webi.sh"
b_tmp=''
if test -r "${a_path}"; then
b_ts="$(date -u '+%s')"
b_tmp="${a_path}.${b_ts}.bak"
mv "${a_path}" "${b_tmp}"
echo " Updating $(t_path "${b_path_rel}")"
fi
echo " Downloading $(t_url "${b_webi_file_url}")"
echo " to $(t_path "${b_path_rel}")"
fn_download_to_path "${b_webi_file_url}" "${a_path}"
chmod u+x "${a_path}"
if test -r "${b_tmp}"; then
rm -f "${b_tmp}"
fi
); }
fn_checksum() {
a_filepath="${1}"
cmd_shasum='sha1sum'
if command -v shasum > /dev/null; then
cmd_shasum='shasum'
fi
$cmd_shasum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
}
##############################################
# #
# Detect TTY and run main #
# #
##############################################
fn_is_tty() {
if test "${WEBI_TTY}" = 'tty'; then
return 0
fi
return 1
}
fn_detect_tty() { (
# stdin will NOT be a tty if it's being piped
# stdout & stderr WILL be a tty even when piped
# they are not a tty if being captured or redirected
# 'set -i' is NOT available in sh
if test -t 1 && test -t 2; then
return 0
fi
return 1
); }
main() { (
set -e
set -u
WEBI_TTY="${WEBI_TTY:-}"
if test -z "${WEBI_TTY}"; then
if fn_detect_tty; then
WEBI_TTY="tty"
fi
export WEBI_TTY
fi
if test -z "${WEBI_WELCOME:-}"; then
fn_show_welcome
fi
export WEBI_WELCOME='shown'
# note: we may support custom locations in the future
export WEBI_HOME="${HOME}/.local"
b_home="$(fn_sub_home "${WEBI_HOME}")"
b_webi_path="${WEBI_HOME}/bin/webi"
b_webi_path_rel="${b_home}/bin/webi"
WEBI_CURRENT="${WEBI_CURRENT:-}"
if test "${WEBI_CURRENT}" != "${WEBI_CHECKSUM}"; then
webi_bootstrap "${b_webi_path}"
export WEBI_CURRENT="${WEBI_CHECKSUM}"
fi
echo " Running $(t_cmd "${b_webi_path_rel} ${WEBI_PKG}")"
echo ""
"${b_webi_path}" "${WEBI_PKG}"
); }
main

View File

@@ -1,19 +0,0 @@
#!/bin/sh
set -e
set -u
# For installing from the extracted package tmp directory
pkg_install() {
(
cd "$WEBI_TMP"
if command -v rsync 2> /dev/null | grep -q rsync; then
rsync -Krl ./xmpl*/ "$pkg_src/" 2> /dev/null
else
cp -Hr ./xmpl*/* "$pkg_src/" 2> /dev/null
cp -Hr ./xmpl*/.* "$pkg_src/" 2> /dev/null
fi
rm -rf ./xmpl*
)
}

View File

@@ -1,7 +1,7 @@
'use strict';
var fs = require('fs');
var marked = require('marked');
var marked = require('marked').marked;
var frontmatter = '---';
var keyValRe = /(\w+): (.*)/;
@@ -19,7 +19,7 @@ function parseYamlish(txt) {
}
function unblock() {
cfg[block] = marked(cfg[block]);
cfg[block] = marked.parse(cfg[block]);
block = false;
}
@@ -57,7 +57,7 @@ function parseYamlish(txt) {
var m = line.match(keyValRe);
if (!m) {
throw new Error(
'invalid key format for: ' + JSON.stringify(line) + ' ' + i
'invalid key format for: ' + JSON.stringify(line) + ' ' + i,
);
}
if ('|' === m[2]) {
@@ -68,9 +68,9 @@ function parseYamlish(txt) {
});
if (block) {
cfg[block] = marked(cfg[block]);
cfg[block] = marked.parse(cfg[block]);
}
cfg.examples = marked(lines.slice(last).join('\n'));
cfg.examples = marked.parse(lines.slice(last).join('\n'));
return cfg;
}
@@ -79,6 +79,6 @@ module.exports.parse = parseYamlish;
if (require.main === module) {
console.info(
parseYamlish(fs.readFileSync(__dirname + '/../node/README.md', 'utf8'))
parseYamlish(fs.readFileSync(__dirname + '/../node/README.md', 'utf8')),
);
}

232
_webi/installers.js Normal file
View File

@@ -0,0 +1,232 @@
'use strict';
var Installers = module.exports;
var Crypto = require('crypto');
var Fs = require('node:fs/promises');
var path = require('node:path');
var reInstallTpl = /\s*#?\s*{{ installer }}/;
function padScript(txt) {
return txt.replace(/^/g, ' ');
}
var BAD_SH_RE = /[<>'"`$\\]/;
Installers.renderBash = async function (
pkgdir,
rel,
{ baseurl, pkg, tag, ver, os = '', arch = '', libc = '', formats, latest },
) {
if (!Array.isArray(formats)) {
formats = [];
}
if (!tag) {
tag = '';
}
let installTxt = await Fs.readFile(path.join(pkgdir, 'install.sh'), 'utf8');
installTxt = padScript(installTxt);
var vers = rel.version.split('.');
var v = {
major: vers.shift() || '',
minor: vers.shift() || '',
patch: vers.join('.').replace(/[+\-].*/, ''),
build: vers
.join('.')
.replace(/[^+\-]*/, '')
.replace(/^-/, ''),
};
var pkgFile = rel.filename || rel.name;
let tplTxt = await Fs.readFile(
path.join(__dirname, 'package-install.tpl.sh'),
'utf8',
);
// ex: 'node@lts' or 'node'
var webiPkg = pkg;
if (ver) {
webiPkg += `@${ver}`;
}
let releaseParams = new URLSearchParams({
os: rel.os,
arch: rel.arch,
libc: rel.libc,
formats: formats.join(','),
pretty: true,
});
let releaseSearch = releaseParams.toString();
releaseSearch = releaseSearch.replace(/%2C/g, ',');
let releaseUrl = `/api/releases/${pkg}@${tag}.tab?${releaseSearch}`;
let releaseCsv = [
rel.version,
rel.lts,
rel.channel,
rel.date,
rel.os,
rel.arch,
rel.ext,
'-',
rel.download,
rel.name,
rel.comment || '',
]
.join(',')
.replace(/'/g, '');
let webiChecksum = await Installers.getWebiShChecksum();
let envReplacements = [
['WEBI_CHECKSUM', webiChecksum],
['WEBI_PKG', webiPkg],
['WEBI_HOST', baseurl],
['WEBI_OS', os],
['WEBI_ARCH', arch],
['WEBI_LIBC', libc],
['WEBI_TAG', tag],
['WEBI_RELEASES', `${baseurl}${releaseUrl}`],
['WEBI_CSV', releaseCsv],
['WEBI_VERSION', rel.version],
['WEBI_MAJOR', v.major],
['WEBI_MINOR', v.minor],
['WEBI_PATCH', v.patch],
['WEBI_BUILD', v.build],
['WEBI_GIT_BRANCH', rel.git_branch || rel.git_tag],
['WEBI_GIT_TAG', rel.git_tag], // TODO replace with branch
['WEBI_LTS', rel.lts],
['WEBI_CHANNEL', rel.channel],
['WEBI_EXT', rel.ext.replace(/tar.*/, 'tar')],
['WEBI_FORMATS', formats.join(',')],
['WEBI_PKG_URL', rel.download],
['WEBI_PKG_PATHNAME', pkgFile],
['WEBI_PKG_FILE', pkgFile], // TODO replace with pathname
['PKG_NAME', pkg],
['PKG_OSES', (rel.oses || []).join(' ')],
['PKG_ARCHES', (rel.arches || []).join(' ')],
['PKG_LIBCS', (rel.libcs || []).join(' ')],
['PKG_FORMATS', (rel.formats || []).join(' ')],
['PKG_LATEST', latest],
];
for (let env of envReplacements) {
let name = env[0];
let value = env[1];
// Ex:
// #export WEBI_FOO=xyz => export WEBI_FOO='123'
// export WEBI_FOO= => export WEBI_FOO='123'
let envRe = new RegExp(
`^[ \\t]*#?[ \\t]*(export[ \\t])?[ \\t]*(${name})=.*`,
'm',
);
if (BAD_SH_RE.test(value)) {
throw new Error(`key '${name}' has invalid value '${value}'`);
}
tplTxt = tplTxt.replace(envRe, `$1$2='${value}'`);
}
tplTxt = tplTxt
.replace(/CHEATSHEET_URL/g, `${baseurl}/${pkg}`)
// $', $0, ... $9, $`, $&, and $_ all have special meaning
// (see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
// However, it can be escaped with $$ (which must be escaped with $$)
.replace(reInstallTpl, '\n' + installTxt.replace(/\$/g, '$$$$'));
return tplTxt;
};
Installers.renderPowerShell = async function (
pkgdir,
rel,
{ baseurl, pkg, tag, ver, os, arch, libc = '', formats },
) {
if (!Array.isArray(formats)) {
formats = [];
}
if (!tag) {
tag = '';
}
let installTxt = await Fs.readFile(path.join(pkgdir, 'install.ps1'), 'utf8');
installTxt = padScript(installTxt);
/*
var vers = rel.version.split('.');
var v = {
major: vers.shift() || '',
minor: vers.shift() || '',
patch: vers.join('.').replace(/[+\-].*$/, ''),
build: vers
.join('.')
.replace(/[^+\-]*()/, '')
.replace(/^-/, '')
};
*/
let tplTxt = await Fs.readFile(
path.join(__dirname, 'package-install.tpl.ps1'),
'utf8',
);
var pkgver = pkg + '@' + ver;
return (
tplTxt
.replace(
/^(#)?\$Env:WEBI_LIBC\s*=.*/im,
"$Env:WEBI_LIBC = '" + libc + "'",
)
.replace(
/^(#)?\$Env:WEBI_HOST\s*=.*/im,
"$Env:WEBI_HOST = '" + baseurl + "'",
)
.replace(
/^(#)?\$Env:WEBI_PKG\s*=.*/im,
"$Env:WEBI_PKG = '" + pkgver + "'",
)
.replace(/^(#)?\$Env:PKG_NAME\s*=.*/im, "$Env:PKG_NAME = '" + pkg + "'")
.replace(
/^(#)?\$Env:WEBI_VERSION\s*=.*/im,
"$Env:WEBI_VERSION = '" + rel.version + "'",
)
.replace(
/^(#)?\$Env:WEBI_GIT_TAG\s*=.*/im,
"$Env:WEBI_GIT_TAG = '" + rel.git_tag + "'",
)
.replace(
/^(#)?\$Env:WEBI_PKG_URL\s*=.*/im,
"$Env:WEBI_PKG_URL = '" + rel.download + "'",
)
// TODO replace WEBI_PKG_FILE (which is sometimes a dir)
.replace(
/^(#)?\$Env:WEBI_PKG_PATHNAME\s*=.*/im,
"$Env:WEBI_PKG_PATHNAME = '" + rel.name + "'",
)
// TODO deprecate
.replace(
/^(#)?\$Env:WEBI_PKG_FILE\s*=.*/im,
"$Env:WEBI_PKG_FILE = '" + rel.name + "'",
)
.replace(reInstallTpl, '\n' + installTxt)
);
};
var _webiShMeta = {
stale: 10 * 1000,
updated_at: 0,
checksum: '',
mtime: 0,
};
Installers.getWebiShChecksum = async function () {
let now = Date.now();
let ago = now - _webiShMeta.updated_at;
if (ago <= _webiShMeta.stale) {
return _webiShMeta.checksum;
}
let webiPath = path.join(__dirname, '../webi/webi.sh');
let stat = await Fs.stat(webiPath);
if (stat.mtimeMs === _webiShMeta.mtime) {
return _webiShMeta.checksum;
}
let webiBuf = await Fs.readFile(webiPath, null);
let webiHash = Crypto.createHash('sha1').update(webiBuf).digest('hex');
let webiChecksum = webiHash.slice(0, 8);
_webiShMeta.mtime = stat.mtimeMs;
_webiShMeta.updated_at = now;
_webiShMeta.checksum = webiChecksum;
return _webiShMeta.checksum;
};

338
_webi/lint-builds.js Normal file
View File

@@ -0,0 +1,338 @@
#!/usr/bin/env node
'use strict';
let Fs = require('node:fs/promises');
let Path = require('node:path');
let BuildsCacher = require('./builds-cacher.js');
let HostTargets = require('./build-classifier/host-targets.js');
let Parallel = require('./parallel.js');
var INSTALLERS_DIR = Path.join(__dirname, '..');
var CACHE_DIR = Path.join(__dirname, '../_cache');
let UserAgentsMap = require('./build-classifier/uas.json');
let uas = Object.keys(UserAgentsMap);
let uaTargetsMap = {};
for (let ua of uas) {
let terms = ua.split(/[\s\/]+/g);
let target = {};
void HostTargets.termsToTarget(target, terms);
if (!target) {
continue;
}
if (target.errors.length) {
throw target.errors[0];
}
if (!target.os) {
// TODO make target null, or create error for this
console.warn(`no os for terms: ${terms}`);
//throw new Error(`terms: ${terms}`);
continue;
}
if (!target.arch) {
// TODO make target null, or create error for this
console.warn(`no arch for terms: ${terms}`);
//throw new Error(`terms: ${terms}`);
continue;
}
if (!target.libc) {
// TODO make target null, or create error for this
console.warn(`no libc for terms: ${terms}`);
//throw new Error(`terms: ${terms}`);
continue;
}
let triplet = `${target.os}-${target.arch}-${target.libc}`;
uaTargetsMap[triplet] = target;
}
let uaTargets = [];
let triplets = Object.keys(uaTargetsMap);
for (let triplet of triplets) {
let target = uaTargetsMap[triplet];
uaTargets.push(target);
}
function showDirs(dirs) {
{
let errors = Object.keys(dirs.errors);
console.error('');
console.error(`Errors: ${errors.length}`);
for (let name of errors) {
let err = dirs.errors[name];
console.error(`${name}/: ${err.message}`);
}
}
{
let hidden = Object.keys(dirs.hidden);
console.debug('');
console.debug(`Hidden: ${hidden.length}`);
for (let name of hidden) {
let kind = dirs.hidden[name];
if (kind === '!directory') {
console.debug(` ${name}`);
} else {
console.debug(` ${name}/`);
}
}
}
{
let alias = Object.keys(dirs.alias);
console.debug('');
console.debug(`Alias: ${alias.length}`);
for (let name of alias) {
let kind = dirs.alias[name];
if (kind === 'symlink') {
console.debug(` ${name} => ...`);
} else {
console.debug(` ${name}/`);
}
}
}
{
let invalids = Object.keys(dirs.invalid);
console.warn('');
console.warn(`Invalid: ${invalids.length}`);
for (let name of invalids) {
console.warn(` ${name}/`);
}
}
{
let selfhosted = Object.keys(dirs.selfhosted);
console.info('');
console.info(`Self-Hosted: ${selfhosted.length}`);
for (let name of selfhosted) {
console.info(` ${name}/`);
}
}
{
let valids = Object.keys(dirs.valid);
console.info('');
console.info(`Found: ${valids.length}`);
for (let name of valids) {
console.info(` ${name}/`);
}
}
}
let bc = BuildsCacher.create({
caches: CACHE_DIR,
installers: INSTALLERS_DIR,
});
async function main() {
/* jshint maxcomplexity: 25 */
// TODO
// node ./_webi/lint-builds.js caddy@beta 'x86_64/unknown Darwin libc'
//
//let [projName, userAgent] = process.argv.slice(2);
let projName = process.argv[2];
// create test case for zoxide, goreleaser, go, yq, caddy, rg
let dirs = await bc.getProjectsByType();
if (!projName) {
showDirs(dirs);
console.info('');
}
bc.freshenRandomPackage(600 * 1000);
let rows = [];
let triples = [];
let valids = Object.keys(dirs.valid);
if (projName) {
if (!valids.includes(projName)) {
throw new Error(`'${projName}' is not a valid installable project`);
}
valids = [projName];
}
//valids = ['atomicparsley', 'caddy', 'macos'];
//valids = ['atomicparsley'];
console.info('');
console.info(`Fetching project release assets`);
let parallel = 25;
let projects = [];
await Parallel.run(parallel, valids, getAll);
async function getAll(name, i) {
console.info(` ${name}`);
let projInfo = await bc.getPackages({
//Releases: Releases,
name: name,
date: new Date(),
});
projects[i] = projInfo;
}
console.info(`Classifying build assets for...`);
for (let projInfo of projects) {
console.info(` ${projInfo.name}`);
let nStr = projInfo.releases.length.toString();
let n = nStr.padStart(5, ' ');
let row = `##### ${n}\t${projInfo.name}\tv`;
rows.push(row);
// ignore known, non-package extensions
for (let build of projInfo.releases) {
let target = bc.classify(projInfo, build);
if (!target) {
// non-build file
continue;
}
if (target.error) {
let e = target.error;
if (e.code === 'E_BUILD_NO_PATTERN') {
console.warn(`>>> ${e.message} <<<`);
console.warn(projInfo);
console.warn(build);
console.warn(`^^^ ${e.message} ^^^`);
}
throw e;
}
if (target.unknownTerms?.length) {
let msg = `${projInfo.name}: unrecognized term(s) '${target.unknownTerms}' in '${build.download}'`;
let err = new Error(msg);
throw err;
}
triples.push(target.triplet);
// if (!build.version) {
// throw new Error(`no version for ${pkg.name} ${build.name}`);
// }
// // For debug printing versions
// console.error(build.version);
rows.push(`${target.triplet}\t${projInfo.name}\t${build.version}`);
}
}
console.info(`Fetching builds for`);
for (let projInfo of projects) {
console.info('');
console.info('');
console.info(` ${projInfo.name}`);
for (let target of uaTargets) {
let libc = target.libc || 'libc';
let hostTriplet = `${target.os}-${target.arch}-${libc}`;
console.info('');
console.info(` target: ${hostTriplet}`);
let match = bc.findMatchingPackages(projInfo, target, {
ver: '',
});
if (!match) {
console.info(
` project: ${projInfo.name}: missing build for os '${target.os}'`,
);
continue;
}
if (!match.releases) {
console.info(
` project: ${projInfo.name}: missing build for os '${target.os}-${target.arch}-${libc}'`,
);
} else if (match.triplet === hostTriplet) {
let releaseNames = Object.keys(match.releases);
console.info(` selected ${releaseNames.length}`);
} else {
let releaseNames = Object.keys(match.releases);
console.info(
` selected ${releaseNames.length} (${match.triplet} fallback)`,
);
}
}
}
let tsv = rows.join('\n');
console.info('');
console.info('#rows', rows.length);
await Fs.writeFile('builds.tsv', tsv, 'utf8');
console.info('');
console.info('Triplets Detected:');
let triplets = Object.keys(bc._triplets);
if (triplets.length) {
triplets.sort();
console.info(' ', triplets.join('\n '));
} else {
console.info(' (none)');
}
console.info('');
console.info('New / Unknown Terms:');
let unknowns = Object.keys(bc.unknownTerms);
if (unknowns.length) {
unknowns.sort();
console.warn(' ', unknowns.join('\n '));
} else {
console.info(' (none)');
}
console.info('');
console.info('Unused Terms:');
let unuseds = Object.keys(bc.orphanTerms);
if (unuseds.length) {
unuseds.sort();
console.warn(' ', unuseds.join('\n '));
} else {
console.info(' (none)');
}
console.info('');
console.info('Formats:');
if (bc.formats.length) {
let formats = bc.formats.slice();
formats.sort();
if (!formats[0]) {
formats[0] = '(bin)';
}
console.warn(' ', formats.join('\n '));
} else {
console.info(' (none)');
}
// sort -u -k1 builds.tsv | rg -v '^#|^https?:' | rg -i arm
// cut -f1 builds.tsv | sort -u -k1 | rg -v '^#|^https?:' | rg -i arm
}
if (module === require.main) {
let times = [];
let now = Date.now();
main()
.then(async function () {
let then = Date.now();
let delta = then - now;
times.push(delta);
now = then;
await main();
then = Date.now();
delta = then - now;
times.push(delta);
})
.then(function () {
console.info('');
console.info('Run times');
for (let delta of times) {
let s = delta / 1000;
console.info(` ${s}`);
}
function forceExit() {
console.warn(`warn: dangling event loop reference`);
process.exit(0);
}
let exitTimeout = setTimeout(forceExit, 250);
exitTimeout.unref();
})
.catch(function (err) {
console.error(err.stack || err);
process.exit(1);
});
}

View File

@@ -7,20 +7,21 @@ var osMap = {
freebsd: /(\b|_)(freebsd)/i,
windows: /(\b|_)(win|microsoft|msft)/i,
sunos: /(\b|_)(sun)/i,
aix: /(\b|_)(aix)/i
aix: /(\b|_)(aix)/i,
};
var maps = {
oses: {},
arches: {},
formats: {}
libcs: {},
formats: {},
};
Object.keys(osMap).forEach(function (name) {
maps.oses[name] = true;
});
var formats = ['zip', 'xz', 'tar', 'pkg', 'msi', 'git', 'exe', 'dmg'];
var formats = ['zip', 'xz', 'tar', 'pkg', 'msi', 'git', 'exe', 'dmg', 'git'];
formats.forEach(function (name) {
maps.formats[name] = true;
});
@@ -28,18 +29,18 @@ formats.forEach(function (name) {
// evaluation order matters
// (i.e. otherwise x86 and x64 can cross match)
var arches = [
// arm 7 cannot be confused with arm64
'armv7l',
// amd64 is more likely than arm64
'amd64',
// arm6 has the same prefix as arm64
'armv6l',
// arm64 is more likely than arm6, and should be the default
// arm64/aarch64 has very high specificity, so it comes first
'arm64',
// arm 7 is also generic aarch/arm/arm32
'armv7l',
// arm6 can run on armv7
'armv6l',
// amd64 is more likely and less often specified than arm64
'amd64',
'x86',
'ppc64le',
'ppc64',
's390x'
's390x',
];
// Used for detecting system arch from package download url, for example:
//
@@ -49,57 +50,69 @@ var arches = [
// https://git.com/org/foo/releases/v0.7.9/foo-x86_64-linux-musl.tar.gz
//
var archMap = {
armv7l: /(\b|_)(armv?7l?)/i,
arm64: /(\b|_)(aarch64|arm64)/i,
armv7l: /(\b|_)(arm32|arm[_\-]?v?7l?)/i,
armv6l: /(\b|_)(arm|aarch32|arm[_\-]?v?6l?)(\b|_)/i,
//amd64: /(amd.?64|x64|[_\-]64)/i,
amd64:
/(\b|_|amd|(dar)?win(dows)?|mac(os)?|linux|osx|x)64([_\-]?bit)?(\b|_)/i,
//x86: /(86)(\b|_)/i,
armv6l: /(\b|_)(aarch32|armv?6l?)(\b|_)/i,
arm64: /(\b|_)((aarch|arm)64|arm)/i,
x86: /(\b|_|amd|(dar)?win(dows)?|mac(os)?|linux|osx|x)(86|32)([_\-]?bit)(\b|_)/i,
ppc64le: /(\b|_)(ppc64le)/i,
ppc64: /(\b|_)(ppc64)(\b|_)/i,
s390x: /(\b|_)(s390x)/i
s390x: /(\b|_)(s390x)/i,
};
arches.forEach(function (name) {
maps.arches[name] = true;
});
var libcs = ['none', 'musl', 'gnu', 'msvc', 'libc'];
libcs.forEach(function (name) {
maps.libcs[name] = true;
});
function normalize(all) {
/* jshint maxcomplexity:50 */
/* jshint maxdepth:10 */
var supported = {
oses: {},
arches: {},
formats: {}
libcs: {},
formats: {},
};
all.releases.forEach(function (rel) {
for (let rel of all.releases) {
rel.version = rel.version.replace(/^v/i, '');
if (!rel.name) {
rel.name = rel.download.replace(/.*\//, '');
}
if (!rel.os) {
rel.os =
Object.keys(osMap).find(function (regKey) {
return osMap[regKey].test(rel.name || rel.download);
}) || 'unknown';
}
// Hacky-doo for musl
// TODO some sort of glibc vs musl tag?
if (!rel._musl) {
if (/(\b|\.|_|-)(musl)(\b|\.|_|-)/.test(rel.download)) {
rel._musl = true;
rel.os = 'unknown';
let osNames = Object.keys(osMap);
for (let osName of osNames) {
let relName = rel.name || rel.download;
let osRegExp = osMap[osName];
let matches = osRegExp.test(relName);
if (matches) {
rel.os = osName;
break;
}
}
}
supported.oses[rel.os] = true;
if (!rel.arch) {
arches.some(function (regKey) {
var arch = (rel.name || rel.download).match(archMap[regKey]) && regKey;
if (arch) {
for (let arch of arches) {
let name = rel.name || rel.download;
let isArch = name.match(archMap[arch]);
if (isArch) {
rel.arch = arch;
return true;
break;
}
});
}
}
if (!rel.arch) {
if ('macos' === rel.os) {
@@ -108,6 +121,60 @@ function normalize(all) {
}
supported.arches[rel.arch] = true;
// note: depends on rel.os
if (!rel.libc) {
let isMusl;
let isMsvc;
let isStatic;
let isGnu;
// extra blocks to prevent copy pasta errors
{
let muslRe = /(\b|_)(musl)(\b|_)/i;
isMusl = muslRe.test(rel.download) || muslRe.test(rel.name);
}
{
let msvcRe = /(\b|_)(msvc)(\b|_)/i;
isMsvc = msvcRe.test(rel.download) || msvcRe.test(rel.name);
}
{
let staticRe = /(\b|_)(static)(\b|_)/i;
isStatic = staticRe.test(rel.download) || staticRe.test(rel.name);
}
{
let gnuRe = /(\b|_)(gnu|glibc|libc)(\b|_)/i;
isGnu = gnuRe.test(rel.download) || gnuRe.test(rel.name);
}
if (isMusl) {
// we specifically tag things that need musl++ in their own releases
rel.libc = 'none';
} else if (isStatic) {
rel.libc = 'none';
} else if (isGnu) {
rel.libc = 'gnu';
if (rel.os === 'windows') {
// windows gnu is static
rel.libc = 'none';
} else if (rel.os === 'darwin') {
// if glibc is required on macos, it'll be static
rel.libc = 'none';
}
} else if (isMsvc) {
rel.libc = 'msvc';
} else {
// The default is no requirement for any particular libc
// (Go, Zig, POSIX Shell, JS, etc)
// and hopefully we never have to worry about mingw and friends
rel.libc = 'none';
}
}
supported.libcs[rel.libc] = true;
var tarExt;
if (!rel.ext) {
// pkg-v1.0.tar.gz => ['gz', 'tar', '0', 'pkg-v1']
@@ -135,10 +202,28 @@ function normalize(all) {
}
supported.formats[tarExt || rel.ext] = true;
if (!rel.channel) {
// basically like this: (+.-_)(beta|rc)(0-9)(+.-_)
// matches:
// - v1.0-beta
// - v1.0-beta1.1
// - v1.0-beta-11
// won't match:
// - v1.0beta
// - v1.0-beta1b
let isBetaRe = /(\b|_)(preview|rc|beta|alpha)(\d+)(\b|_)/;
let isBeta = isBetaRe.test(rel.name);
if (isBeta) {
rel.channel = 'beta';
} else {
rel.channel = 'stable';
}
}
if (all.download) {
rel.download = all.download.replace(/{{ download }}/, rel.download);
}
});
}
all.oses = Object.keys(supported.oses).filter(function (name) {
return maps.oses[name];
@@ -146,6 +231,9 @@ function normalize(all) {
all.arches = Object.keys(supported.arches).filter(function (name) {
return maps.arches[name];
});
all.libcs = Object.keys(supported.libcs).filter(function (name) {
return maps.libcs[name];
});
all.formats = Object.keys(supported.formats).filter(function (name) {
return maps.formats[name];
});
@@ -166,4 +254,5 @@ module.exports._debug = function (all) {
// NOT in order of priority (which would be tar, xz, zip, ...)
module.exports.formats = formats;
module.exports.arches = arches;
module.exports.libcs = libcs;
module.exports.formatsMap = maps.formats;

View File

@@ -0,0 +1,155 @@
#!/usr/bin/env pwsh
#350 check if windows user run as admin
# this allows us to call ps1 files, which allows us to have spaces in filenames
# ('powershell "$Env:USERPROFILE\test.ps1" foo' will fail if it has a space in
# the path but '& "$Env:USERPROFILE\test.ps1" foo' will work even with a space)
Set-ExecutionPolicy -Scope Process Bypass
# If a command returns an error, halt the script.
$ErrorActionPreference = 'Stop'
# Ignore progress events from cmdlets so Invoke-WebRequest is not painfully slow
$ProgressPreference = 'SilentlyContinue'
$Env:WEBI_HOST = 'https://webinstall.dev'
#$Env:WEBI_PKG = 'node@lts'
#$Env:PKG_NAME = node
#$Env:WEBI_VERSION = v12.16.2
#$Env:WEBI_GIT_TAG = 12.16.2
#$Env:WEBI_PKG_URL = "https://.../node-....zip"
#$Env:WEBI_PKG_FILE = "node-v12.16.2-win-x64.zip"
#$Env:WEBI_PKG_PATHNAME = "node-v12.16.2-win-x64.zip"
# Switch to userprofile
Push-Location $Env:USERPROFILE
# Make paths
New-Item -Path "$Env:USERPROFILE\Downloads" -ItemType Directory -Force | Out-Null
New-Item -Path "$Env:USERPROFILE\.local\bin" -ItemType Directory -Force | Out-Null
New-Item -Path "$Env:USERPROFILE\.local\opt" -ItemType Directory -Force | Out-Null
# {{ baseurl }}
# {{ version }}
# See
# - <https://superuser.com/q/1264444>
# - <https://stackoverflow.com/a/60572643/151312>
$Esc = [char]27
$TTask = "${Esc}[36m"
$TName = "${Esc}[1m${Esc}[32m"
$TUrl = "${Esc}[2m"
$TPath = "${Esc}[2m${Esc}[32m"
$TCmd = "${Esc}[2m${Esc}[35m"
$TDim = "${Esc}[2m"
$TReset = "${Esc}[0m"
function Invoke-DownloadUrl {
Param (
[string]$URL,
[string]$Params,
[string]$Path,
[switch]$Force
)
IF (Test-Path -Path "$Path") {
IF (-Not $Force.IsPresent) {
Write-Host " ${TDim}Found${TReset} $Path"
return
}
Write-Host " Updating ${TDim}${Path}${TDim}"
}
$TmpPath = "${Path}.part"
Remove-Item -Path $TmpPath -Force -ErrorAction Ignore
Write-Host " Downloading ${TDim}from${TReset}"
Write-Host " ${TDim}${URL}${TReset}"
IF ($Params.Length -ne 0) {
Write-Host " ?$Params"
$URL = "${URL}?${Params}"
}
curl.exe '-#' -f -sS -A $Env:WEBI_UA -L $URL | Out-File $TmpPath
Remove-Item -Path $Path -Force -ErrorAction Ignore
Move-Item $TmpPath $Path
Write-Host " Saved ${TPath}${Path}${TReset}"
}
function Get-UserAgent {
# This is the canonical CPU arch when the process is emulated
$my_arch = "$Env:PROCESSOR_ARCHITEW6432"
IF ($my_arch -eq $null -or $my_arch -eq "") {
# This is the canonical CPU arch when the process is native
$my_arch = "$Env:PROCESSOR_ARCHITECTURE"
}
IF ($my_arch -eq "AMD64") {
# Because PowerShell is sometimes AMD64 on Windows 10 ARM
# See https://oofhours.com/2020/02/04/powershell-on-windows-10-arm64/
$my_os_arch = wmic os get osarchitecture
# Using -clike because of the trailing newline
IF ($my_os_arch -clike "ARM 64*") {
$my_arch = "ARM64"
}
}
"PowerShell+curl Windows/10+ $my_arch msvc"
}
function webi_path_add($pathname) {
# C:\Users\me => C:/Users/me
$my_home = $Env:UserProfile
$my_home = $my_home.replace('\\', '/')
$my_home_re = [regex]::escape($my_home)
# ~/bin => %USERPROFILE%/bin
$pathname = $pathname.replace('~/', "$Env:UserProfile/")
# C:\Users\me\bin => %USERPROFILE%/bin
$my_pathname = $pathname.replace('\\', '/')
$my_pathname = $my_pathname -ireplace $my_home_re, "%USERPROFILE%"
$all_user_paths = [Environment]::GetEnvironmentVariable("Path", "User")
$user_paths = "${all_user_paths}".Trim(';').Split(';')
$exists_in_path = $false
foreach ($user_path in $user_paths) {
# C:\Users\me\bin => %USERPROFILE%/bin
$my_user_path = $user_path.replace('\\', '/')
$my_user_path = $my_user_path -ireplace $my_home_re, "%USERPROFILE%"
if ($my_user_path -ieq $my_pathname) {
$exists_in_path = $true
}
}
if (-Not $exists_in_path) {
$all_user_paths = "${pathname};${all_user_paths}".Trim(';')
[Environment]::SetEnvironmentVariable("Path", $all_user_paths, "User")
$null = Sync-EnvPath
}
}
function Sync-EnvPath {
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User").Trim(';')
$MachinePath = [Environment]::GetEnvironmentVariable("Path", "Machine").Trim(';')
$Env:Path = "${UserPath};${MachinePath}"
[Environment]::SetEnvironmentVariable("Path", $Env:Path)
$Env:Path
}
$Env:WEBI_UA = Get-UserAgent
#$has_local_bin = echo "$Env:PATH" | Select-String -Pattern '\.local.bin'
#if (!$has_local_bin)
#{
webi_path_add ~/.local/bin
#}
# {{ installer }}
webi_path_add ~/.local/bin
# Done
Pop-Location

View File

@@ -0,0 +1,985 @@
#!/bin/sh
__bootstrap_webi() {
#PKG_NAME=
#WEBI_OS=
#WEBI_ARCH=
#WEBI_LIBC=
#WEBI_RELEASES=
#WEBI_CSV=
#WEBI_TAG=
#WEBI_VERSION=
#WEBI_MAJOR=
#WEBI_MINOR=
#WEBI_PATCH=
# TODO not sure if BUILD is the best name for this
#WEBI_BUILD=
#WEBI_GIT_TAG=
#WEBI_LTS=
#WEBI_CHANNEL=
#WEBI_EXT=
#WEBI_FORMATS=
#WEBI_PKG_URL=
#WEBI_PKG_FILE=
#WEBI_PKG_PATHNAME=
#PKG_OSES=
#PKG_ARCHES=
#PKG_LIBCS=
#PKG_FORMATS=
#PKG_LATEST=
WEBI_PKG_DOWNLOAD=""
WEBI_DOWNLOAD_DIR="${HOME}/Downloads"
if command -v xdg-user-dir > /dev/null; then
WEBI_DOWNLOAD_DIR="$(xdg-user-dir DOWNLOAD)"
if [ "${WEBI_DOWNLOAD_DIR}" = "${HOME}" ]; then
WEBI_DOWNLOAD_DIR="${HOME}/Downloads"
fi
fi
WEBI_PKG_PATH="${WEBI_DOWNLOAD_DIR}/webi/${PKG_NAME:-error}/${WEBI_VERSION:-latest}"
# get the special formatted version
# (i.e. "go is go1.14" while node is "node v12.10.8")
my_versioned_name=""
_webi_canonical_name() {
if [ -n "$my_versioned_name" ]; then
echo "$my_versioned_name"
return 0
fi
if command -v pkg_format_cmd_version > /dev/null; then
my_versioned_name="'$(pkg_format_cmd_version "$WEBI_VERSION")'"
else
my_versioned_name="'$pkg_cmd_name v$WEBI_VERSION'"
fi
echo "$my_versioned_name"
}
# Update symlinks as per $HOME/.local/opt and $HOME/.local/bin install paths.
webi_link() {
if command -v pkg_link > /dev/null; then
pkg_link
return 0
fi
if test -n "${WEBI_SINGLE}"; then
rm -rf "$pkg_dst_cmd"
ln -s "$pkg_src_cmd" "$pkg_dst_cmd"
else
# 'pkg_dst' will default to $HOME/.local/opt/<pkg>
# 'pkg_src' will be the installed version,
# such as to $HOME/.local/opt/<pkg>-<version>
rm -rf "$pkg_dst"
ln -s "$pkg_src" "$pkg_dst"
fi
}
# detect if this program is already installed
# or if an installed version may cause conflict
webi_check_installed() {
# Test for existing version
set +e
my_path="$PATH"
PATH="$(dirname "$pkg_dst_cmd"):$PATH"
export PATH
my_current_cmd="$(command -v "$pkg_cmd_name")"
set -e
if [ -n "$my_current_cmd" ]; then
my_canonical_name="$(_webi_canonical_name)"
if [ "$my_current_cmd" != "$pkg_dst_cmd" ]; then
echo >&2 " $(t_err "WARN: possible PATH conflict between $my_canonical_name and currently installed version")"
echo >&2 " $(t_path "${pkg_dst_cmd}") (new)"
echo >&2 " $(t_path "${my_current_cmd}") (existing)"
#my_current_version=false
fi
# 'readlink' can't read links in paths on macOS 🤦
# but that's okay, 'cmp -s' is good enough for us
if cmp -s "${pkg_src_cmd}" "${my_current_cmd}"; then
echo " $(t_pkg "${my_canonical_name}") already installed:"
my_dst_rel="$(fn_sub_home "${pkg_dst}")"
printf " %s" "$(t_link "${my_dst_rel}")"
if [ "${pkg_src_cmd}" != "${my_current_cmd}" ]; then
my_src_rel="$(fn_sub_home "${pkg_src}")"
printf " => %s" "$(t_path "${my_src_rel}")"
fi
echo ""
exit 0
fi
if [ -x "$pkg_src_cmd" ]; then
webi_link
echo " Switched to ${my_canonical_name}:"
my_src_rel="$(fn_sub_home "${pkg_src}")"
my_dst_rel="$(fn_sub_home "${pkg_dst}")"
echo " $(t_link "${my_dst_rel}") => $(t_path "${my_src_rel}")"
exit 0
fi
fi
export PATH="$my_path"
}
webi_check_available() {
if test "$WEBI_CHANNEL" != "error"; then
return 0
fi
{
echo ""
echo " $(t_err "Error: no '${PKG_NAME:-"Unknown Package"}@${WEBI_TAG:-"Unknown Tag"}' release for '${WEBI_OS:-"Unknown OS"}' (${WEBI_LIBC:-"Unknown Libc"}) on '${WEBI_ARCH:-"Unknown CPU"}' as one of '${WEBI_FORMATS:-"Unknown File Type"}'")"
echo ""
echo " Latest Version: ${PKG_LATEST}"
echo " CPUs: $PKG_ARCHES"
echo " OSes: $PKG_OSES"
echo " libcs: $PKG_LIBCS"
echo " Package Formats: $PKG_FORMATS"
echo " (check that the package name and version are correct)"
echo ""
my_release_url="$(echo "$WEBI_RELEASES" | sed 's:?.*::')"
my_release_params="$(echo "$WEBI_RELEASES" | sed 's:.*?:?:')"
echo " Double check at ${my_release_url}"
echo " ${my_release_params}"
echo ""
} >&2
exit 1
}
# detect if file is downloaded, and how to download it
webi_download() {
my_url="${1}"
my_dl="${2}"
my_dl_name="${3:-${PKG_NAME}}"
my_dl_rel="$(fn_sub_home "${my_dl}")"
WEBI_PKG_DOWNLOAD="${my_dl}"
export WEBI_PKG_DOWNLOAD
if [ -e "${my_dl}" ]; then
echo " $(t_dim 'Found') $(t_path "${my_dl_rel}")"
return 0
fi
echo " Downloading $(t_pkg "${my_dl_name}") from"
echo " $(t_url "${my_url}")"
fn_download_to_path "${my_url}" "${my_dl}"
echo " $(t_dim 'Saved as') $(t_path "${my_dl_rel}")"
}
webi_git_clone() { (
my_url="${1}"
my_dl="${2}"
my_dl_rel="$(fn_sub_home "${my_dl}")"
if [ -e "${my_dl}" ]; then
echo " $(t_dim 'Found') $(t_path "${my_dl_rel}")"
cp -RPp "${my_dl}" "${WEBI_TMP}/${WEBI_PKG_FILE}/"
return 0
fi
echo " Cloning $(t_url "${my_url}")"
cmd_git="git clone --config advice.detachedHead=false --quiet --depth=1 --single-branch"
rm -rf "${my_dl}.part"
if ! $cmd_git "${my_url}" --branch "${WEBI_GIT_TAG}" "${my_dl}.part"; then
echo >&2 " $(t_err "failed to git clone ${WEBI_PKG_URL}")"
exit 1
fi
mv "${my_dl}.part" "${my_dl}"
cp -RPp "${my_dl}" "${WEBI_TMP}/${WEBI_PKG_FILE}/"
); }
# detect which archives can be used
webi_extract() { (
cd "$WEBI_TMP"
my_dl_rel="$(
fn_sub_home "${WEBI_PKG_PATH}/${WEBI_PKG_FILE}"
)"
if [ "tar" = "$WEBI_EXT" ]; then
echo " Extracting $(t_path "${my_dl_rel}")"
tar xf "${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
elif [ "zip" = "$WEBI_EXT" ]; then
echo " Extracting $(t_path "${my_dl_rel}")"
unzip "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" > __unzip__.log
elif [ "exe" = "$WEBI_EXT" ]; then
echo " Moving $(t_path "${my_dl_rel}")"
echo " to $(t_path "$(fn_sub_home "$(pwd)")")"
mv "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" .
elif [ "git" = "$WEBI_EXT" ]; then
echo " Moving $(t_path "${my_dl_rel}")"
mv "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" .
elif [ "xz" = "$WEBI_EXT" ]; then
echo " Inflating $(t_path "${my_dl_rel}")"
unxz -c "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" > "$(basename "$WEBI_PKG_FILE")"
else
echo " $(t_err 'Failed to extract') $(t_path "${WEBI_PKG_PATH}/$WEBI_PKG_FILE")"
exit 1
fi
); }
# use 'pathman' to update $HOME/.config/envman/PATH.env
webi_path_add() {
my_path="${1}"
fn_envman_init
# \v was chosen as it is extremely unlikely for a filename
# \1 could be an even better choice, but needs more testing.
# (currently tested working on: linux & mac)
# "\0001" should also work
my_delim="$(
printf '\v'
)"
my_path_expanded="$(
echo "${my_path}" |
sed -e "s${my_delim}\$HOME${my_delim}$HOME${my_delim}g" \
-e "s${my_delim}\${HOME}${my_delim}$HOME${my_delim}g" \
-e "s${my_delim}^~/${my_delim}$HOME/${my_delim}g"
)"
# A gift for @adamcstephens.
# See https://github.com/webinstall/webi-installers/issues/322
case "${PATH}" in
# matches whether the first, a middle, the last, or the only PATH entry
"${my_path_expanded}":* | \
*:"${my_path_expanded}":* | \
*:"${my_path_expanded}" | \
"${my_path_expanded}")
if fn_is_defined_in_all_shells "${my_path}"; then
return 0
fi
;;
*) ;;
esac
my_path_export="$(
echo "${my_path}" |
sed -e "s${my_delim}${HOME}${my_delim}\$HOME${my_delim}g" \
-e "s${my_delim}\${HOME}${my_delim}\$HOME${my_delim}g" \
-e "s${my_delim}^~/${my_delim}\$HOME/${my_delim}g"
)"
my_export="export PATH=\"$my_path_export:\$PATH\""
touch -a ~/.config/envman/PATH.env
if grep -q -F "${my_export}" ~/.config/envman/PATH.env; then
return 0
fi
echo "${my_export}" >> ~/.config/envman/PATH.env
mkdir -p "$_webi_tmp"
my_path_tilde="$(
echo "${my_path}" |
sed -e "s${my_delim}${HOME}${my_delim}~${my_delim}g"
)"
if ! test -f "$_webi_tmp/.PATH.env" ||
! grep -q -F "${my_path_tilde}" "$_webi_tmp/.PATH.env"; then
echo "${my_path_tilde}" >> "$_webi_tmp/.PATH.env"
fi
}
fn_is_defined_in_all_shells() {
my_path="${1}"
my_path_expanded="$(
echo "${my_path}" |
sed -e "s${my_delim}\$HOME|${my_delim}$HOME${my_delim}g" \
-e "s${my_delim}\${HOME}${my_delim}$HOME${my_delim}g" \
-e "s${my_delim}^~/${my_delim}$HOME/${my_delim}g"
)"
my_paths="$(
echo "${my_path_expanded}"
# $HOME/foo
echo "${my_path_expanded}" |
sed "s${my_delim}${HOME}${my_delim}\$HOME${my_delim}g"
# ${HOME}/foo
echo "${my_path_expanded}" |
sed "s${my_delim}${HOME}${my_delim}\${HOME}${my_delim}g"
echo "${my_path}"
)"
my_confs="$(
echo "${HOME}/.profile"
echo "${HOME}/.bashrc"
echo "${HOME}/.zshrc"
echo "${HOME}/.config/fish/config.fish"
)"
for my_conf in $my_confs; do
if ! test -e "${my_conf}"; then
continue
fi
if ! grep -q -F "${my_paths}" "${my_conf}"; then
return 1
fi
done
}
# group common pre-install tasks as default
webi_pre_install() {
webi_check_installed
webi_check_available
if test "git" = "${WEBI_EXT}"; then
webi_git_clone \
"${WEBI_PKG_URL}" \
"${WEBI_PKG_PATH}/${WEBI_PKG_FILE}"
return 0
fi
webi_download \
"${WEBI_PKG_URL}" \
"${WEBI_PKG_PATH}/${WEBI_PKG_FILE}"
webi_extract
}
# move commands from the extracted archive directory
# to $HOME/.local/opt or $HOME/.local/bin
webi_install() {
b_src=''
if test -n "${WEBI_SINGLE}"; then
b_src="${pkg_src_cmd}"
mkdir -p "$(dirname "$pkg_src_cmd")"
else
echo " Removing $(t_path "${pkg_src}")"
rm -rf "$pkg_src"
b_src="${pkg_src}"
fi
echo " Moving $(t_path "${pkg_cmd_name}")"
echo " to $(t_path "$(fn_sub_home "${b_src}")")"
mv ./"${pkg_cmd_name}"* "${b_src}"
}
# run post-install functions - just updating PATH by default
webi_post_install() {
if test -n "${pkg_no_exec}"; then
return 0
fi
webi_path_add "$(dirname "$pkg_dst_cmd")"
}
_webi_enable_exec() {
if command -v spctl > /dev/null && command -v xattr > /dev/null; then
# note: some packages contain files that cannot be affected by xattr
xattr -r -d com.apple.quarantine "$pkg_src" || true
return 0
fi
}
_webi_done_message() {
my_dst_rel="$(fn_sub_home "${pkg_dst_cmd}")"
my_canonical_name="$(_webi_canonical_name)"
echo ""
echo " Installed $(t_pkg "${my_canonical_name}") as $(t_link "${my_dst_rel}")"
}
##
## Set up tmp, download, and install directories
##
WEBI_TMP=${WEBI_TMP:-"$(mktemp -d -t webinstall-"${WEBI_PKG-}".XXXXXXXX)"}
export _webi_tmp="${_webi_tmp:-"$HOME/.local/opt/webi-tmp.d"}"
mkdir -p "${WEBI_PKG_PATH}"
mkdir -p "$HOME/.local/bin"
mkdir -p "$HOME/.local/opt"
if test -e ~/.local/bin; then
echo " $(t_dim 'Found') $(t_path ' ~/.local/bin')"
else
echo " Creating$(t_path ' ~/.local/bin')"
mkdir -p "$HOME/.local/bin"
fi
##
##
## BEGIN custom override functions from <package>/install.sh
##
##
WEBI_SINGLE=
__init_installer() {
# the installer will be injected here
# {{ installer }}
return 0
}
__init_installer
##
##
## END custom override functions
##
##
# run everything with defaults or overrides as needed
if command -v pkg_install > /dev/null ||
command -v pkg_link > /dev/null ||
command -v pkg_post_install > /dev/null ||
command -v pkg_done_message > /dev/null ||
command -v pkg_format_cmd_version > /dev/null ||
[ -n "${WEBI_SINGLE-}" ] ||
[ -n "${pkg_cmd_name-}" ] ||
[ -n "${pkg_dst_cmd-}" ] ||
[ -n "${pkg_dst_dir-}" ] ||
[ -n "${pkg_dst-}" ] ||
[ -n "${pkg_src_cmd-}" ] ||
[ -n "${pkg_src_dir-}" ] ||
[ -n "${pkg_src-}" ]; then
pkg_cmd_name="${pkg_cmd_name:-$PKG_NAME}"
pkg_no_exec="${pkg_no_exec:-}"
if [ -n "${pkg_no_exec}" ]; then
pkg_dst_cmd="${pkg_dst}"
pkg_src="${pkg_dst}"
pkg_src_cmd="${pkg_dst}"
elif [ -n "${WEBI_SINGLE}" ]; then
pkg_dst_cmd="${pkg_dst_cmd:-$HOME/.local/bin/$pkg_cmd_name}"
pkg_dst="$pkg_dst_cmd"
#pkg_src_cmd="${pkg_src_cmd:-$HOME/.local/opt/$pkg_cmd_name-v$WEBI_VERSION/bin/$pkg_cmd_name-v$WEBI_VERSION}"
pkg_src_cmd="${pkg_src_cmd:-$HOME/.local/opt/$pkg_cmd_name-v$WEBI_VERSION/bin/$pkg_cmd_name}"
pkg_src="$pkg_src_cmd"
else
pkg_dst="${pkg_dst:-$HOME/.local/opt/$pkg_cmd_name}"
pkg_dst_cmd="${pkg_dst_cmd:-$pkg_dst/bin/$pkg_cmd_name}"
pkg_src="${pkg_src:-$HOME/.local/opt/$pkg_cmd_name-v$WEBI_VERSION}"
pkg_src_cmd="${pkg_src_cmd:-$pkg_src/bin/$pkg_cmd_name}"
fi
# shellcheck disable=SC2034 # used in ${WEBI_PKG}/install.sh
pkg_src_bin="$(dirname "$pkg_src_cmd")"
# shellcheck disable=SC2034 # used in ${WEBI_PKG}/install.sh
pkg_dst_bin="$(dirname "$pkg_dst_cmd")"
if command -v pkg_pre_install > /dev/null; then pkg_pre_install; else webi_pre_install; fi
(
cd "$WEBI_TMP"
my_src_rel="$(
fn_sub_home "${pkg_src_cmd}"
)"
if test -e "${pkg_src_cmd}"; then
echo " $(t_dim 'Found') $(t_path "${my_src_rel}") $(t_dim '(remove to force reinstall)')"
else
echo " Installing to $(t_path "${my_src_rel}")"
if command -v pkg_install > /dev/null; then pkg_install; else webi_install; fi
chmod a+x "$pkg_src"
chmod a+x "$pkg_src_cmd"
fi
)
if test -z "${pkg_no_exec}"; then
webi_link
_webi_enable_exec
fi
(
cd "$WEBI_TMP"
if command -v pkg_post_install > /dev/null; then pkg_post_install; else webi_post_install; fi
)
(
cd "$WEBI_TMP"
if command -v pkg_done_message > /dev/null; then pkg_done_message; else _webi_done_message; fi
)
echo ""
fi
webi_path_add "$HOME/.local/bin"
if [ -z "${_WEBI_CHILD-}" ] && [ -f "$_webi_tmp/.PATH.env" ]; then
if test -s "$_webi_tmp/.PATH.env"; then
# shellcheck disable=SC2088 # ~ should not expand here
echo " Edit $(t_path '~/.config/envman/PATH.env') to add:"
sort -u "$_webi_tmp/.PATH.env" | while read -r my_new_path; do
echo " $(t_path "${my_new_path}")"
done
echo ""
rm -f "$_webi_tmp/.PATH.env"
echo ">>> $(t_info 'ACTION REQUIRED') <<<"
echo " Copy, paste & run the following command:"
echo " $(t_attn 'source ~/.config/envman/PATH.env')"
echo " (newly opened terminal windows will update automatically)"
echo ""
fi
fi
rm -rf "$WEBI_TMP"
}
#########################################
# #
# Display Debug Info in Case of Failure #
# #
#########################################
fn_show_welcome_back() { (
if test -n "${WEBI_WELCOME:-}"; then
return 0
fi
echo ""
# invert t_task and t_pkg for top-level welcome message
printf -- ">>> %s %s <<<\n" \
"$(t_pkg 'Welcome to') $(t_task 'Webi')$(t_pkg '!')" \
"$(t_dim "- modern tools, instant installs.")"
echo " We expect your experience to be $(t_em 'absolutely perfect')!"
echo ""
echo " $(t_attn 'Success')? Star it! $(t_url 'https://github.com/webinstall/webi-installers')"
echo " $(t_attn 'Problem')? Report it: $(t_url 'https://github.com/webinstall/webi-installers/issues')"
echo " $(t_dim "(your system is") $(t_host "$(fn_get_os)")/$(t_host "$(uname -m)") $(t_dim "with") $(t_host "$(fn_get_libc)") $(t_dim "&") $(t_host "$(fn_get_http_client_name)")$(t_dim ")")"
sleep 0.2
); }
fn_get_os() { (
# Ex:
# GNU/Linux
# Android
# Linux (often Alpine, musl)
# Darwin
b_os="$(uname -o 2> /dev/null || echo '')"
b_sys="$(uname -s)"
if test -z "${b_os}" || test "${b_os}" = "${b_sys}"; then
# ex: 'Darwin' (and plain, non-GNU 'Linux')
echo "${b_sys}"
return 0
fi
if echo "${b_os}" | grep -q "${b_sys}"; then
# ex: 'GNU/Linux'
echo "${b_os}"
return 0
fi
# ex: 'Android/Linux'
echo "${b_os}/${b_sys}"
); }
fn_get_libc() { (
# Ex:
# musl
# libc
if ldd /bin/ls 2> /dev/null | grep -q 'musl' 2> /dev/null; then
echo 'musl'
elif fn_get_os | grep -q 'GNU|Linux'; then
echo 'gnu'
else
echo 'libc'
fi
); }
fn_get_http_client_name() { (
# Ex:
# curl
# curl+wget
b_client=""
if command -v curl > /dev/null; then
b_client="curl"
fi
if command -v wget > /dev/null; then
if test -z "${b_client}"; then
b_client="wget"
else
b_client="curl+wget"
fi
fi
echo "${b_client}"
); }
#########################################
# #
# For Making the Display Nice #
# #
#########################################
# Term Types
t_cmd() { (fn_printf '\e[2m\e[35m%s\e[39m\e[22m' "${1}"); }
t_host() { (fn_printf '\e[2m\e[33m%s\e[39m\e[22m' "${1}"); }
t_link() { (fn_printf '\e[1m\e[36m%s\e[39m\e[22m' "${1}"); }
t_path() { (fn_printf '\e[2m\e[32m%s\e[39m\e[22m' "${1}"); }
t_pkg() { (fn_printf '\e[1m\e[32m%s\e[39m\e[22m' "${1}"); }
t_task() { (fn_printf '\e[36m%s\e[39m' "${1}"); }
t_url() { (fn_printf '\e[2m%s\e[22m' "${1}"); }
# Levels
t_info() { (fn_printf '\e[1m\e[36m%s\e[39m\e[22m' "${1}"); }
t_attn() { (fn_printf '\e[1m\e[33m%s\e[39m\e[22m' "${1}"); }
t_warn() { (fn_printf '\e[1m\e[33m%s\e[39m\e[22m' "${1}"); }
t_err() { (fn_printf '\e[31m%s\e[39m' "${1}"); }
# Styles
t_bold() { (fn_printf '\e[1m%s\e[22m' "${1}"); }
t_dim() { (fn_printf '\e[2m%s\e[22m' "${1}"); }
t_em() { (fn_printf '\e[3m%s\e[23m' "${1}"); }
t_under() { (fn_printf '\e[4m%s\e[24m' "${1}"); }
# FG Colors
t_cyan() { (fn_printf '\e[36m%s\e[39m' "${1}"); }
t_green() { (fn_printf '\e[32m%s\e[39m' "${1}"); }
t_magenta() { (fn_printf '\e[35m%s\e[39m' "${1}"); }
t_yellow() { (fn_printf '\e[33m%s\e[39m' "${1}"); }
fn_printf() { (
a_style="${1}"
a_text="${2}"
if fn_is_tty; then
#shellcheck disable=SC2059
printf -- "${a_style}" "${a_text}"
else
printf -- '%s' "${a_text}"
fi
); }
fn_sub_home() { (
my_rel=${HOME}
my_abs=${1}
echo "${my_abs}" | sed "s:^${my_rel}:~:"
); }
###################################
# #
# Detect HTTP Client #
# #
###################################
fn_wget() { (
# Doc:
# Downloads the file at the given url to the given path
a_url="${1}"
a_path="${2}"
cmd_wget="wget -c -q --user-agent"
if fn_is_tty; then
cmd_wget="wget -c -q --show-progress --user-agent"
fi
# busybox wget doesn't support --show-progress
# See <https://github.com/webinstall/webi-installers/pull/772>
if readlink "$(command -v wget)" | grep -q busybox; then
cmd_wget="wget --user-agent"
fi
b_triple_ua="$(fn_get_target_triple_user_agent)"
b_agent="webi/wget ${b_triple_ua}"
if command -v curl > /dev/null; then
b_agent="webi/wget+curl ${b_triple_ua}"
fi
if ! $cmd_wget "${b_agent}" "${a_url}" -O "${a_path}"; then
echo >&2 " $(t_err "failed to download (wget)") '$(t_url "${a_url}")'"
echo >&2 " $cmd_wget '${b_agent}' '${a_url}' -O '${a_path}'"
echo >&2 " $(wget -V)"
return 1
fi
); }
fn_curl() { (
# Doc:
# Downloads the file at the given url to the given path
a_url="${1}"
a_path="${2}"
cmd_curl="curl -f -sSL -#"
if fn_is_tty; then
cmd_curl="curl -f -sSL"
fi
b_triple_ua="$(fn_get_target_triple_user_agent)"
b_agent="webi/curl ${b_triple_ua}"
if command -v wget > /dev/null; then
b_agent="webi/curl+wget ${b_triple_ua}"
fi
if ! $cmd_curl -A "${b_agent}" "${a_url}" -o "${a_path}"; then
echo >&2 " $(t_err "failed to download (curl)") '$(t_url "${a_url}")'"
echo >&2 " $cmd_curl -A '${b_agent}' '${a_url}' -o '${a_path}'"
echo >&2 " $(curl -V)"
return 1
fi
); }
fn_get_target_triple_user_agent() { (
# Ex:
# x86_64/unknown GNU/Linux/5.15.107-2-pve gnu
# arm64/unknown Darwin/22.6.0 libc
echo "$(uname -m)/unknown $(fn_get_os)/$(uname -r) $(fn_get_libc)"
); }
fn_download_to_path() { (
a_url="${1}"
a_path="${2}"
mkdir -p "$(dirname "${a_path}")"
if command -v curl > /dev/null; then
fn_curl "${a_url}" "${a_path}.part"
elif command -v wget > /dev/null; then
fn_wget "${a_url}" "${a_path}.part"
else
echo >&2 " $(t_err "failed to detect HTTP client (curl, wget)")"
return 1
fi
mv "${a_path}.part" "${a_path}"
); }
##############################################
# #
# Install or Update Webi and Install Package #
# #
##############################################
webi_upgrade() { (
a_path="${1}"
b_path_rel="$(fn_sub_home "${a_path}")"
b_checksum=""
if test -r "${a_path}"; then
b_checksum="$(fn_checksum "${a_path}")"
fi
if test "$b_checksum" = "${WEBI_CHECKSUM}"; then
sleep 0.1
return 0
fi
b_webi_file_url="${WEBI_HOST}/packages/webi/webi.sh"
b_tmp=''
if test -r "${a_path}"; then
b_ts="$(date -u '+%s')"
b_tmp="${a_path}.${b_ts}.bak"
mv "${a_path}" "${b_tmp}"
echo ""
echo "$(t_task 'Updating') $(t_pkg 'Webi')"
fi
echo " Downloading $(t_url "${b_webi_file_url}")"
echo " to $(t_path "${b_path_rel}")"
fn_download_to_path "${b_webi_file_url}" "${a_path}"
chmod u+x "${a_path}"
if test -r "${b_tmp}"; then
rm -f "${b_tmp}"
fi
); }
fn_checksum() {
a_filepath="${1}"
cmd_shasum='sha1sum'
if command -v shasum > /dev/null; then
cmd_shasum='shasum'
fi
$cmd_shasum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
}
##############################################
# #
# Detect TTY and run main #
# #
##############################################
fn_is_tty() {
if test "${WEBI_TTY}" = 'tty'; then
return 0
fi
return 1
}
fn_detect_tty() { (
# stdin will NOT be a tty if it's being piped
# stdout & stderr WILL be a tty even when piped
# they are not a tty if being captured or redirected
# 'set -i' is NOT available in sh
if test -t 1 && test -t 2; then
return 0
fi
return 1
); }
main() { (
set -e
set -u
#set -x
export WEBI_HOST=
export WEBI_CHECKSUM=
export WEBI_PKG=
WEBI_TTY="${WEBI_TTY:-}"
if test -z "${WEBI_TTY}"; then
if fn_detect_tty; then
WEBI_TTY="tty"
fi
export WEBI_TTY
fi
if test -z "${WEBI_WELCOME:-}"; then
fn_show_welcome_back
fi
export WEBI_WELCOME='shown'
# note: we may support custom locations in the future
export WEBI_HOME="${HOME}/.local"
b_webi_path="${WEBI_HOME}/bin/webi"
WEBI_CURRENT="${WEBI_CURRENT:-}"
if test "${WEBI_CURRENT}" != "${WEBI_CHECKSUM}"; then
webi_upgrade "${b_webi_path}"
export WEBI_CURRENT="${WEBI_CHECKSUM}"
fi
echo "$(t_task 'Installing') $(t_pkg "${WEBI_PKG}") $(t_task '...')"
__bootstrap_webi
); }
##############################################
# #
# envman helper functions #
# #
##############################################
fn_envman_init() { (
if ! test -r ~/.config/envman/; then
echo " Initializing ~/.config/envman/"
mkdir -p ~/.config/envman/
fi
# Note: the variables $BASH, $ZSH_NAME, etc are always empty
# because the active shell is always sh when this script runs
fn_envman_init_load_sh
fn_envman_init_shell sh '.profile' '.ash_history'
fn_envman_init_shell bash '.bashrc' '.bash_history'
fn_envman_init_shell zsh '.zshrc' '.zsh_sessions'
if command -v fish > /dev/null; then
fn_envman_init_load_fish
fn_envman_init_fish
fi
); }
fn_envman_init_load_sh() { (
touch -a ~/.config/envman/load.sh
if grep -q -F 'ENVMAN_LOAD' ~/.config/envman/load.sh; then
return 0
fi
cat << LOAD_SH > ~/.config/envman/load.sh
# Generated for envman. Do not edit.
# shellcheck disable=SC1090
touch -a ~/.config/envman/PATH.env
touch -a ~/.config/envman/ENV.env
touch -a ~/.config/envman/alias.env
touch -a ~/.config/envman/function.sh
# ENV first because we may use it in PATH
test -z "\${ENVMAN_LOAD:-}" && . ~/.config/envman/ENV.env
test -z "\${ENVMAN_LOAD:-}" && . ~/.config/envman/PATH.env
export ENVMAN_LOAD='loaded'
# function first because we may use it in alias
test -z "\${g_envman_load_sh:-}" && . ~/.config/envman/function.sh
test -z "\${g_envman_load_sh:-}" && . ~/.config/envman/alias.env
g_envman_load_sh='loaded'
LOAD_SH
); }
fn_envman_init_shell() { (
a_shell="${1}"
a_rc="${2}"
a_history="${3:-_history_file_doesnt_exist}"
a_login_shell="$(basename "${SHELL:-}")"
if ! command -v "${a_shell}" > /dev/null; then
return 0
fi
# .bashrc and .zshrc no longer exist by default on macOS
if ! test -e ~/"${a_rc}" && ! test -e ~/"${a_history}"; then
if test "${a_login_shell}" != "${a_shell}"; then
return 0
fi
fi
touch -a ~/"${a_rc}"
if grep -q -F '/.config/envman/load.sh' ~/"${a_rc}"; then
return 0
fi
# shellcheck disable=SC2088 # ~ should not expand here
echo >&2 " Edit $(t_path "~/${a_rc}") to $(t_cmd "source ~/.config/envman/load.sh")"
{
echo ''
echo '# Generated for envman. Do not edit.'
#shellcheck disable=SC2016 # vars should not expand here
echo '[ -s "$HOME/.config/envman/load.sh" ] && source "$HOME/.config/envman/load.sh"'
} >> ~/"${a_rc}"
); }
fn_envman_init_load_fish() { (
mkdir -p ~/.config/envman/
touch -a ~/.config/envman/load.fish
if grep -q -F 'ENVMAN_LOAD' ~/.config/envman/load.fish; then
return 0
fi
echo >&2 " Create ~/.config/envman/load.fish"
cat << EOF > ~/.config/envman/load.fish
# Generated for envman. Do not edit.
touch -a ~/.config/envman/PATH.env
touch -a ~/.config/envman/ENV.env
touch -a ~/.config/envman/alias.env
touch -a ~/.config/envman/function.fish
not set -q ENVMAN_LOAD; and source ~/.config/envman/ENV.env
not set -q ENVMAN_LOAD; and source ~/.config/envman/PATH.env
set -x ENVMAN_LOAD 'loaded'
not set -q g_envman_load_fish; and source ~/.config/envman/function.fish
not set -q g_envman_load_fish; and source ~/.config/envman/alias.env
set -g g_envman_load_fish 'loaded'
EOF
); }
fn_envman_init_fish() {
mkdir -p ~/.config/fish/
touch -a ~/.config/fish/config.fish
if grep -q -F '/.config/envman/load.fish' ~/.config/fish/config.fish; then
return 0
fi
# shellcheck disable=SC2088 # ~ should not expand here
echo >&2 " Edit $(t_path "~/.config/fish/config.fish") to $(t_cmd "source ~/.config/envman/load.fish")"
cat << EOF >> ~/.config/fish/config.fish
# Generated for envman. Do not edit.
test -s ~/.config/envman/load.fish; and source ~/.config/envman/load.fish
EOF
}
main

View File

@@ -1,114 +0,0 @@
'use strict';
var frontmarker = require('./frontmarker.js');
var shmatter = require('shmatter');
var fs = require('fs');
var path = require('path');
var pkgs = module.exports;
pkgs.create = function (Pkgs, basepath) {
if (!Pkgs) {
Pkgs = {};
}
if (!basepath) {
basepath = path.join(__dirname, '../');
}
Pkgs.all = function () {
return fs.promises.readdir(basepath).then(function (nodes) {
var items = [];
return nodes
.reduce(function (p, node) {
return p.then(function () {
return pkgs.get(node).then(function (meta) {
if (meta && '_' !== node[0]) {
meta.name = node;
items.push(meta);
}
});
});
}, Promise.resolve())
.then(function () {
return items;
});
});
};
Pkgs.get = function (node) {
return fs.promises.access(path.join(basepath, node)).then(function () {
return Pkgs._get(node);
});
};
Pkgs._get = function (node) {
var yash = path.join(basepath, node, 'package.yash');
var curlbash = path.join(basepath, node, 'install.sh');
var readme = path.join(basepath, node, 'README.md');
var winstall = path.join(basepath, node, 'install.ps1');
return Promise.all([
fs.promises
.readFile(readme, 'utf-8')
.then(function (txt) {
// TODO
return frontmarker.parse(txt);
})
.catch(function (e) {
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to read '" + node + "/README.md'");
console.error(e);
}
}),
fs.promises
.readFile(yash, 'utf-8')
.then(function (txt) {
return shmatter.parse(txt);
})
.catch(function (e) {
// no yash package description
yash = '';
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to parse '" + node + "/package.yash'");
console.error(e);
}
return fs.promises.readFile(curlbash, 'utf-8').then(function (txt) {
return shmatter.parse(txt);
});
})
.catch(function (e) {
// no *nix installer
curlbash = '';
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to parse '" + node + "/install.sh'");
console.error(e);
}
}),
fs.promises.access(winstall).catch(function (e) {
// no winstaller
winstall = '';
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to read '" + node + "/install.ps1'");
console.error(e);
}
})
]).then(function (items) {
var meta = items[0] || items[1];
if (!meta) {
// doesn't exist
return;
}
meta.windows = !!winstall;
meta.bash = !!curlbash;
return meta;
});
};
return Pkgs;
};
pkgs.create(pkgs);
if (module === require.main) {
pkgs.all().then(function (data) {
console.info('package info:');
console.info(data);
});
}

1
_webi/packages.js Symbolic link
View File

@@ -0,0 +1 @@
projects.js

44
_webi/parallel.js Normal file
View File

@@ -0,0 +1,44 @@
'use strict';
var Parallel = module.exports;
Parallel.run = async function (limit, arr, fn) {
let index = 0;
let actives = [];
let results = [];
limit = Math.min(limit, arr.length);
function launch() {
let _index = index;
let p = fn(arr[_index], _index, arr);
// some tasks may be synchronous
// so we must push before removing
actives.push(p);
p.then(function _resolve(result) {
let i = actives.indexOf(p);
actives.splice(i, 1);
results[_index] = result;
});
index += 1;
}
// start tasks in parallel, up to limit
for (; actives.length < limit; ) {
launch();
}
// keep the task queue full
for (; index < arr.length; ) {
// wait for one task to complete
await Promise.race(actives);
// add one task again
launch();
}
// wait for all remaining tasks
await Promise.all(actives);
return results;
};

96
_webi/projects.js Normal file
View File

@@ -0,0 +1,96 @@
'use strict';
var frontmarker = require('./frontmarker.js');
var fs = require('fs');
var path = require('path');
var pkgs = module.exports;
pkgs.create = function (Pkgs, basepath) {
if (!Pkgs) {
Pkgs = {};
}
if (!basepath) {
basepath = path.join(__dirname, '../');
}
Pkgs.all = function () {
return fs.promises.readdir(basepath).then(function (nodes) {
var items = [];
return nodes
.reduce(function (p, node) {
return p.then(function () {
return pkgs.get(node).then(function (meta) {
if (meta && '_' !== node[0]) {
meta.name = node;
items.push(meta);
}
});
});
}, Promise.resolve())
.then(function () {
return items;
});
});
};
Pkgs.get = function (node) {
return fs.promises.access(path.join(basepath, node)).then(function () {
return Pkgs._get(node);
});
};
Pkgs._get = function (node) {
var curlbash = path.join(basepath, node, 'install.sh');
var readme = path.join(basepath, node, 'README.md');
var winstall = path.join(basepath, node, 'install.ps1');
return Promise.all([
fs.promises
.readFile(readme, 'utf-8')
.then(function (txt) {
// TODO
return frontmarker.parse(txt);
})
.catch(function (e) {
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to read '" + node + "/README.md'");
console.error(e);
}
}),
fs.promises.access(curlbash).catch(function (e) {
// no *nix installer
curlbash = '';
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to parse '" + node + "/install.sh'");
console.error(e);
}
}),
fs.promises.access(winstall).catch(function (e) {
// no winstaller
winstall = '';
if ('ENOENT' !== e.code && 'ENOTDIR' !== e.code) {
console.error("failed to read '" + node + "/install.ps1'");
console.error(e);
}
}),
]).then(function (items) {
var meta = items[0] || items[1];
if (!meta) {
// doesn't exist
return;
}
meta.windows = !!winstall;
meta.bash = !!curlbash;
return meta;
});
};
return Pkgs;
};
pkgs.create(pkgs);
if (module === require.main) {
pkgs.all().then(function (data) {
console.info('package info:');
console.info(data);
});
}

View File

@@ -1,255 +0,0 @@
'use strict';
var fs = require('fs');
var path = require('path');
var request = require('@root/request');
var _normalize = require('../_webi/normalize.js');
var reInstallTpl = /\s*#?\s*{{ installer }}/;
var Releases = module.exports;
Releases.get = async function (pkgdir) {
var get;
try {
get = require(path.join(pkgdir, 'releases.js'));
} catch (e) {
throw new Error('no releases.js for', pkgdir.split(/[\/\\]+/).pop());
}
return get(request).then(function (all) {
return _normalize(all);
});
};
function padScript(txt) {
return txt.replace(/^/g, ' ');
}
Releases.renderBash = function (
pkgdir,
rel,
{ baseurl, pkg, tag, ver, os = '', arch = '', formats }
) {
if (!Array.isArray(formats)) {
formats = [];
}
if (!tag) {
tag = '';
}
return fs.promises
.readFile(path.join(pkgdir, 'install.sh'), 'utf8')
.then(function (installTxt) {
installTxt = padScript(installTxt);
var vers = rel.version.split('.');
var v = {
major: vers.shift() || '',
minor: vers.shift() || '',
patch: vers.join('.').replace(/[+\-].*/, ''),
build: vers
.join('.')
.replace(/[^+\-]*/, '')
.replace(/^-/, '')
};
var pkgFile = rel.filename || rel.name;
return fs.promises
.readFile(path.join(__dirname, 'template.sh'), 'utf8')
.then(function (tplTxt) {
// ex: 'node@lts' or 'node'
var webiPkg = pkg;
if (ver) {
webiPkg += `@${ver}`;
}
return (
tplTxt
.replace(/CHEATSHEET_URL/g, `${baseurl}/${pkg}`)
.replace(/^\s*#?WEBI_PKG=.*/m, `WEBI_PKG='${webiPkg}'`)
.replace(/^\s*#?WEBI_HOST=.*/m, `WEBI_HOST='${baseurl}'`)
.replace(/^\s*#?WEBI_OS=.*/m, `WEBI_OS='${os}'`)
.replace(/^\s*#?WEBI_ARCH=.*/m, `WEBI_ARCH='${arch}'`)
.replace(/^\s*#?WEBI_TAG=.*/m, `WEBI_TAG='${tag}'`)
.replace(
/^\s*#?WEBI_RELEASES=.*/m,
"WEBI_RELEASES='" +
baseurl +
'/api/releases/' +
pkg +
'@' +
tag +
'.tab?os=' +
rel.os +
'&arch=' +
rel.arch +
'&formats=' +
formats.join(',') +
'&pretty=true' +
"'"
)
.replace(
/^\s*#?WEBI_CSV=.*/m,
"WEBI_CSV='" +
[
rel.version,
rel.lts,
rel.channel,
rel.date,
rel.os,
rel.arch,
rel.ext,
'-',
rel.download,
rel.name,
rel.comment || ''
]
.join(',')
.replace(/'/g, '') +
"'"
)
.replace(
/^\s*#?WEBI_VERSION=.*/m,
'WEBI_VERSION=' + JSON.stringify(rel.version)
)
.replace(/^\s*#?WEBI_MAJOR=.*/m, 'WEBI_MAJOR=' + v.major)
.replace(/^\s*#?WEBI_MINOR=.*/m, 'WEBI_MINOR=' + v.minor)
.replace(/^\s*#?WEBI_PATCH=.*/m, 'WEBI_PATCH=' + v.patch)
.replace(/^\s*#?WEBI_BUILD=.*/m, 'WEBI_BUILD=' + v.build)
.replace(/^\s*#?WEBI_LTS=.*/m, 'WEBI_LTS=' + rel.lts)
.replace(/^\s*#?WEBI_CHANNEL=.*/m, 'WEBI_CHANNEL=' + rel.channel)
.replace(
/^\s*#?WEBI_EXT=.*/m,
'WEBI_EXT=' + rel.ext.replace(/tar.*/, 'tar')
)
.replace(
/^\s*#?WEBI_FORMATS=.*/m,
"WEBI_FORMATS='" + formats.join(',') + "'"
)
.replace(
/^\s*#?WEBI_PKG_URL=.*/m,
"WEBI_PKG_URL='" + rel.download + "'"
)
.replace(
/^\s*#?WEBI_PKG_FILE=.*/m,
"WEBI_PKG_FILE='" + pkgFile + "'"
)
// PKG details
.replace(/^\s*#?PKG_NAME=.*/m, "PKG_NAME='" + pkg + "'")
.replace(
/^\s*#?PKG_OSES=.*/m,
"PKG_OSES='" + ((rel && rel.oses) || []).join(',') + "'"
)
.replace(
/^\s*#?PKG_ARCHES=.*/m,
"PKG_ARCHES='" + ((rel && rel.arches) || []).join(',') + "'"
)
.replace(
/^\s*#?PKG_FORMATS=.*/m,
"PKG_FORMATS='" + ((rel && rel.formats) || []).join(',') + "'"
)
// $', $0, ... $9, $`, $&, and $_ all have special meaning
// (see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
// However, it can be escaped with $$ (which must be escaped with $$)
.replace(reInstallTpl, '\n' + installTxt.replace(/\$/g, '$$$$'))
);
});
});
};
Releases.renderBatch = function (
pkgdir,
rel,
{ baseurl, pkg, tag, ver, os, arch, formats }
) {
if (!Array.isArray(formats)) {
formats = [];
}
if (!tag) {
tag = '';
}
return fs.promises
.readFile(path.join(pkgdir, 'install.bat'), 'utf8')
.then(function (installTxt) {
installTxt = padScript(installTxt);
/*
var vers = rel.version.split('.');
var v = {
major: vers.shift() || '',
minor: vers.shift() || '',
patch: vers.join('.').replace(/[+\-].*$/, ''),
build: vers
.join('.')
.replace(/[^+\-]*()/, '')
.replace(/^-/, '')
};
*/
return fs.promises
.readFile(path.join(__dirname, 'template.bat'), 'utf8')
.then(function (tplTxt) {
return tplTxt
.replace(
/^(REM )?WEBI_PKG=.*/im,
"WEBI_PKG='" + pkg + '@' + ver + "'"
)
.replace(reInstallTpl, '\n' + installTxt);
});
});
};
Releases.renderPowerShell = function (
pkgdir,
rel,
{ baseurl, pkg, tag, ver, os, arch, formats }
) {
if (!Array.isArray(formats)) {
formats = [];
}
if (!tag) {
tag = '';
}
return fs.promises
.readFile(path.join(pkgdir, 'install.ps1'), 'utf8')
.then(function (installTxt) {
installTxt = padScript(installTxt);
/*
var vers = rel.version.split('.');
var v = {
major: vers.shift() || '',
minor: vers.shift() || '',
patch: vers.join('.').replace(/[+\-].*$/, ''),
build: vers
.join('.')
.replace(/[^+\-]*()/, '')
.replace(/^-/, '')
};
*/
return fs.promises
.readFile(path.join(__dirname, 'template.ps1'), 'utf8')
.then(function (tplTxt) {
var pkgver = pkg + '@' + ver;
return tplTxt
.replace(
/^(#)?\$Env:WEBI_HOST\s*=.*/im,
"$Env:WEBI_HOST = '" + baseurl + "'"
)
.replace(
/^(#)?\$Env:WEBI_PKG\s*=.*/im,
"$Env:WEBI_PKG = '" + pkgver + "'"
)
.replace(
/^(#)?\$Env:PKG_NAME\s*=.*/im,
"$Env:PKG_NAME = '" + pkg + "'"
)
.replace(
/^(#)?\$Env:WEBI_VERSION\s*=.*/im,
"$Env:WEBI_VERSION = '" + rel.version + "'"
)
.replace(
/^(#)?\$Env:WEBI_PKG_URL\s*=.*/im,
"$Env:WEBI_PKG_URL = '" + rel.download + "'"
)
.replace(
/^(#)?\$Env:WEBI_PKG_FILE\s*=.*/im,
"$Env:WEBI_PKG_FILE = '" + rel.name + "'"
)
.replace(reInstallTpl, '\n' + installTxt);
});
});
};

View File

@@ -1,92 +1,288 @@
'use strict';
var path = require('path');
var InstallerServer = module.exports;
var uaDetect = require('./ua-detect.js');
var packages = require('./packages.js');
var Releases = require('./releases.js');
let Fs = require('fs/promises');
let Path = require('path');
// handlers caching and transformation, probably should be broken down
var getReleases = require('./transform-releases.js');
let HostTargets = require('./build-classifier/host-targets.js');
let Builds = require('./builds.js');
let Installers = require('./installers.js');
var installersDir = path.join(__dirname, '..');
module.exports = async function serveInstaller(
InstallerServer.INSTALLERS_DIR = Path.join(__dirname, '..');
InstallerServer.serveInstaller = async function (
baseurl,
ua,
pkg,
tag,
ext,
formats
formats,
libc,
) {
// TODO put some of this in a middleware? or common function?
let unameAgent = ua;
let projectName = pkg;
let [rel, tmplParams] = await InstallerServer.helper({
unameAgent,
projectName,
tag,
formats,
libc,
});
Object.assign(tmplParams, {
baseurl,
});
var ver = tag.replace(/^v/, '');
var lts;
var channel;
var pkgdir = Path.join(InstallerServer.INSTALLERS_DIR, projectName);
if ('ps1' === ext) {
return Installers.renderPowerShell(pkgdir, rel, tmplParams);
}
return Installers.renderBash(pkgdir, rel, tmplParams);
};
switch (ver) {
case 'latest':
ver = '';
channel = 'stable';
break;
case 'lts':
lts = true;
channel = 'stable';
ver = '';
break;
case 'stable':
channel = 'stable';
ver = '';
break;
case 'beta':
channel = 'beta';
ver = '';
break;
case 'dev':
channel = 'dev';
ver = '';
break;
// TODO put some of this in a middleware? or common function?
// TODO maybe move package/version/lts/channel detection into getReleases
InstallerServer.helper = async function ({
unameAgent,
projectName,
tag,
formats,
libc,
}) {
console.log(`dbg: Installer User-Agent: ${unameAgent}`);
let releaseTarget = toReleaseTarget(tag);
let hostFormats = formats;
let terms = unameAgent.split(/[\s\/]+/g);
let hostTarget = {};
try {
void HostTargets.termsToTarget(hostTarget, terms);
} catch (e) {
// if we can't guarantee the results...
// "in the face of ambiguity, refuse the temptation to guess"
throw e;
}
console.log(`dbg: Installer Host Target:`);
console.log(hostTarget);
if (!hostTarget.os) {
throw new Error(`OS could not be identified by User-Agent '${unameAgent}'`);
}
// TODO maybe move package/version/lts/channel detection into getReleases
var myOs = uaDetect.os(ua);
var myArch = uaDetect.arch(ua);
return packages.get(pkg).then(function (cfg) {
return getReleases({
pkg: cfg.alias || pkg,
ver,
os: myOs,
arch: myArch,
lts,
channel,
formats,
limit: 1
}).then(function (rels) {
var rel = rels.releases[0];
var pkgdir = path.join(installersDir, pkg);
var opts = {
baseurl,
pkg: cfg.alias || pkg,
ver,
tag,
os: myOs,
arch: myArch,
lts,
channel,
formats,
limit: 1
};
rel.oses = rels.oses;
rel.arches = rels.arches;
rel.formats = rels.formats;
if ('bat' === ext) {
return Releases.renderBatch(pkgdir, rel, opts);
} else if ('ps1' === ext) {
return Releases.renderPowerShell(pkgdir, rel, opts);
} else {
return Releases.renderBash(pkgdir, rel, opts);
}
});
console.log(`dbg: Get Project Installer Type for '${projectName}':`);
let proj = await Builds.getProjectType(projectName);
console.log(proj);
let validTypes = ['alias', 'selfhosted', 'valid'];
if (!validTypes.includes(proj.type)) {
let msg = `'${projectName}' doesn't have an installer: '${proj.type}': '${proj.detail}'`;
let err = new Error(msg);
err.code = 'ENOENT';
throw err;
}
if (proj.type === 'alias') {
projectName = proj.detail;
}
let tmplParams = {
pkg: projectName,
tag: tag,
os: hostTarget.os,
arch: hostTarget.arch,
libc: hostTarget.libc,
formats: hostFormats,
limit: 1,
};
Object.assign(tmplParams, releaseTarget);
console.log('tmplParams', tmplParams);
let errPackage = {
name: 'doesntexist.ext',
version: '0.0.0',
lts: '-',
channel: 'error',
date: '1970-01-01',
os: hostTarget.os || '-',
arch: hostTarget.arch || '-',
libc: hostTarget.libc || '-',
ext: 'err',
download: 'https://example.com/doesntexist.ext',
comment:
'No matches found. Could be bad or missing version info' +
',' +
"Check query parameters. Should be something like '/api/releases/{package}@{version}.tab?os={macos|linux|windows|-}&arch={amd64|x86|aarch64|arm64|armv7l|-}&libc={musl|gnu|msvc|libc|static}&limit=10'",
};
if (proj.type === 'selfhosted') {
return [errPackage, tmplParams];
}
let projInfo = await Builds.getPackage({
name: projectName,
date: new Date(),
});
let latest = projInfo.versions[0];
Object.assign(tmplParams, { latest });
//console.log('projInfo', projInfo);
let buildTargetInfo = {
triplets: projInfo.triplets,
oses: projInfo.oses,
arches: projInfo.arches,
libcs: projInfo.libcs,
formats: projInfo.formats,
};
let hasOs = projInfo.oses.includes(hostTarget.os);
if (!hasOs) {
hasOs = projInfo.oses.includes('ANYOS');
}
if (!hasOs) {
let pkg1 = Object.assign(buildTargetInfo, errPackage);
return [pkg1, tmplParams];
}
let targetRelease = Builds.findMatchingPackages(
projInfo,
hostTarget,
releaseTarget,
);
// { triplet: `${os}-${arch}-${libc}`, packages: targetPackages
// , latest: projInfo.versions[0], versions: matchInfo
// }
if (!targetRelease?.packages) {
let pkg1 = Object.assign(buildTargetInfo, errPackage);
return [pkg1, tmplParams];
}
let buildPkg = Builds.selectPackage(targetRelease.packages, hostFormats);
let ext = buildPkg.ext || '.exe';
if (ext.startsWith('.')) {
ext = ext.slice(1);
}
let version = targetRelease.version;
if (version.startsWith('v')) {
version = version.slice(1);
}
buildPkg = Object.assign(buildTargetInfo, buildPkg, { ext, version });
console.log('dbg: buildPkg', buildPkg);
console.log('dbg: tmplParams', tmplParams);
return [buildPkg, tmplParams];
};
let channelNames = [
'stable',
// 'hotfix',
'latest',
'rc',
'preview',
'pre',
'dev',
'beta',
'alpha',
];
function toReleaseTarget(tag) {
tag = tag.replace(/^v/, '');
let releaseTarget = {
channel: '',
lts: false,
version: '',
};
if (tag === 'lts') {
releaseTarget.lts = true;
releaseTarget.channel = 'stable';
} else if (channelNames.includes(tag)) {
releaseTarget.channel = tag;
} else {
releaseTarget.version = tag;
}
return releaseTarget;
}
var CURL_PIPE_PS1_BOOT = Path.join(__dirname, 'curl-pipe-bootstrap.tpl.ps1');
var CURL_PIPE_SH_BOOT = Path.join(__dirname, 'curl-pipe-bootstrap.tpl.sh');
var BAD_SH_RE = /[<>'"`$\\]/;
InstallerServer.getPosixCurlPipeBootstrap = async function ({
baseurl,
pkg,
ver,
}) {
let bootTxt = await Fs.readFile(CURL_PIPE_SH_BOOT, 'utf8');
var webiPkg = [pkg, ver].filter(Boolean).join('@');
var webiChecksum = await Installers.getWebiShChecksum();
var envReplacements = [
['WEBI_PKG', webiPkg],
['WEBI_HOST', baseurl],
['WEBI_CHECKSUM', webiChecksum],
];
for (let env of envReplacements) {
let name = env[0];
let value = env[1];
let envRe = new RegExp(
`^[ \\t]*#?[ \\t]*(export[ \\t])?[ \\t]*(${name})=.*`,
'm',
);
if (BAD_SH_RE.test(value)) {
throw new Error(`key '${name}' has invalid value '${value}'`);
}
bootTxt = bootTxt.replace(envRe, `$1$2='${value}'`);
}
// TODO init config here
//bootTxt.replace(/CHEATSHEET_URL/g, `${Config.cheatUrl}/${pkg}`);
return bootTxt;
};
InstallerServer.getPwshCurlPipeBootstrap = async function ({
baseurl,
pkg,
ver,
exename,
}) {
let bootTxt = await Fs.readFile(CURL_PIPE_PS1_BOOT, 'utf8');
var webiPkg = [pkg, ver].filter(Boolean).join('@');
//var webiChecksum = await InstallerServer.getWebiPs1Checksum();
var envReplacements = [
['Env:WEBI_PKG', webiPkg],
['Env:WEBI_HOST', baseurl],
//['Env:WEBI_CHECKSUM', webiChecksum],
['baseurl', baseurl],
['exename', exename],
['version', ver],
];
for (let env of envReplacements) {
let name = env[0];
let value = env[1];
if (BAD_SH_RE.test(value)) {
throw new Error(`key '${name}' has invalid value '${value}'`);
}
let tplRe = new RegExp(`{{ (${name}) }}`, 'g');
bootTxt = bootTxt.replace(tplRe, `${value}`);
let setRe = new RegExp(
`(#[ \\t]*)?(\\$${name})[ \\t]*=[ \\t]['"].*['"][ \\t]`,
'im',
);
bootTxt = bootTxt.replace(setRe, `$$${name} = '${value}'`);
}
// TODO init config here
//bootTxt.replace(/CHEATSHEET_URL/g, `${Config.cheatUrl}/${pkg}`);
return bootTxt;
};

View File

@@ -1,5 +0,0 @@
REM REM debug
REM WEBI_PKG=
{{ installer }}

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env pwsh
#350 check if windows user run as admin
function Confirm-IsElevated {
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object System.Security.Principal.WindowsPrincipal($id)
if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
{ Write-Output $true }
else
{ Write-Output $false }
}
if (Confirm-IsElevated)
{ throw "Webi MUST NOT be run with elevated privileges. Please run again as a normal user, NOT as administrator." }
# this allows us to call ps1 files, which allows us to have spaces in filenames
# ('powershell "$Env:USERPROFILE\test.ps1" foo' will fail if it has a space in
# the path but '& "$Env:USERPROFILE\test.ps1" foo' will work even with a space)
Set-ExecutionPolicy -Scope Process Bypass
# If a command returns an error, halt the script.
$ErrorActionPreference = 'Stop'
# Ignore progress events from cmdlets so Invoke-WebRequest is not painfully slow
$ProgressPreference = 'SilentlyContinue'
$Env:WEBI_HOST = 'https://webinstall.dev'
#$Env:WEBI_PKG = 'node@lts'
#$Env:PKG_NAME = node
#$Env:WEBI_VERSION = v12.16.2
#$Env:WEBI_PKG_URL = "https://.../node-....zip"
#$Env:WEBI_PKG_FILE = "node-v12.16.2-win-x64.zip"
# Switch to userprofile
pushd $Env:USERPROFILE
# Make paths
New-Item -Path Downloads -ItemType Directory -Force | out-null
New-Item -Path .local\bin -ItemType Directory -Force | out-null
New-Item -Path .local\opt -ItemType Directory -Force | out-null
# {{ baseurl }}
# {{ version }}
function webi_add_path
{
Write-Host ''
Write-Host '*****************************' -ForegroundColor red -BackgroundColor white
Write-Host '* IMPORTANT - READ ME *' -ForegroundColor red -BackgroundColor white
Write-Host '*****************************' -ForegroundColor red -BackgroundColor white
Write-Host ''
& "$Env:USERPROFILE\.local\bin\pathman.exe" add "$args[0]"
# Note: not all of these work as expected, so we use the unix-style, which is most consistent
#& "$Env:USERPROFILE\.local\bin\pathman.exe" add ~/.local/bin
#& "$Env:USERPROFILE\.local\bin\pathman.exe" add "$Env:USERPROFILE\.local\bin"
#& "$Env:USERPROFILE\.local\bin\pathman.exe" add %USERPROFILE%\.local\bin
}
# Run pathman to set up the folder
& "$Env:USERPROFILE\.local\bin\pathman.exe" add ~/.local/bin
{{ installer }}
# Done
popd

View File

@@ -1,445 +0,0 @@
#!/bin/sh
__bootstrap_webi() {
set -e
set -u
#set -x
#WEBI_PKG=
#PKG_NAME=
# TODO should this be BASEURL instead?
#WEBI_OS=
#WEBI_ARCH=
#WEBI_HOST=
#WEBI_RELEASES=
#WEBI_CSV=
#WEBI_TAG=
#WEBI_VERSION=
#WEBI_MAJOR=
#WEBI_MINOR=
#WEBI_PATCH=
# TODO not sure if BUILD is the best name for this
#WEBI_BUILD=
#WEBI_LTS=
#WEBI_CHANNEL=
#WEBI_EXT=
#WEBI_FORMATS=
#WEBI_PKG_URL=
#WEBI_PKG_FILE=
#PKG_OSES=
#PKG_ARCHES=
#PKG_FORMATS=
WEBI_UA="$(uname -a)"
WEBI_PKG_DOWNLOAD=""
WEBI_PKG_PATH="${HOME}/Downloads/webi/${PKG_NAME:-error}/${WEBI_VERSION:-latest}"
export WEBI_HOST
##
## Set up tmp, download, and install directories
##
WEBI_TMP=${WEBI_TMP:-"$(mktemp -d -t webinstall-"${WEBI_PKG:-}".XXXXXXXX)"}
export _webi_tmp="${_webi_tmp:-"$HOME/.local/opt/webi-tmp.d"}"
mkdir -p "${WEBI_PKG_PATH}"
mkdir -p "$HOME/.local/bin"
mkdir -p "$HOME/.local/opt"
##
## Detect http client
##
set +e
WEBI_CURL="$(command -v curl)"
export WEBI_CURL
WEBI_WGET="$(command -v wget)"
export WEBI_WGET
set -e
# get the special formatted version (i.e. "go is go1.14" while node is "node v12.10.8")
my_versioned_name=""
_webi_canonical_name() {
if [ -n "$my_versioned_name" ]; then
echo "$my_versioned_name"
return 0
fi
if [ -n "$(command -v pkg_format_cmd_version)" ]; then
my_versioned_name="'$(pkg_format_cmd_version "$WEBI_VERSION")'"
else
my_versioned_name="'$pkg_cmd_name v$WEBI_VERSION'"
fi
echo "$my_versioned_name"
}
# update symlinks according to $HOME/.local/opt and $HOME/.local/bin install paths.
# shellcheck disable=2120
# webi_link may be used in the templated install script
webi_link() {
if [ -n "$(command -v pkg_link)" ]; then
pkg_link
return 0
fi
if [ -n "$WEBI_SINGLE" ] || [ "single" = "${1:-}" ]; then
rm -rf "$pkg_dst_cmd"
ln -s "$pkg_src_cmd" "$pkg_dst_cmd"
else
# 'pkg_dst' will default to $HOME/.local/opt/<pkg>
# 'pkg_src' will be the installed version, such as to $HOME/.local/opt/<pkg>-<version>
rm -rf "$pkg_dst"
ln -s "$pkg_src" "$pkg_dst"
fi
}
# detect if this program is already installed or if an installed version may cause conflict
webi_check() {
# Test for existing version
set +e
my_path="$PATH"
PATH="$(dirname "$pkg_dst_cmd"):$PATH"
export PATH
my_current_cmd="$(command -v "$pkg_cmd_name")"
set -e
if [ -n "$my_current_cmd" ]; then
my_canonical_name="$(_webi_canonical_name)"
if [ "$my_current_cmd" != "$pkg_dst_cmd" ]; then
echo >&2 "WARN: possible PATH conflict between $my_canonical_name and currently installed version"
echo >&2 " ${pkg_dst_cmd} (new)"
echo >&2 " ${my_current_cmd} (existing)"
#my_current_version=false
fi
# 'readlink' can't read links in paths on macOS 🤦
# but that's okay, 'cmp -s' is good enough for us
if cmp -s "${pkg_src_cmd}" "${my_current_cmd}"; then
echo "${my_canonical_name} already installed:"
printf " %s" "${pkg_dst}"
if [ "${pkg_src_cmd}" != "${my_current_cmd}" ]; then
printf " => %s" "${pkg_src}"
fi
echo ""
exit 0
fi
if [ -x "$pkg_src_cmd" ]; then
# shellcheck disable=2119
# this function takes no args
webi_link
echo "switched to $my_canonical_name:"
echo " ${pkg_dst} => ${pkg_src}"
exit 0
fi
fi
export PATH="$my_path"
}
is_interactive_shell() {
# $- shows shell flags (error,unset,interactive,etc)
case $- in
*i*)
# true
return 0
;;
*)
# false
return 1
;;
esac
}
# detect if file is downloaded, and how to download it
webi_download() {
# determine the url to download
if [ -n "${1:-}" ]; then
my_url="$1"
else
if [ "error" = "$WEBI_CHANNEL" ]; then
# TODO pass back requested OS / Arch / Version
echo >&2 "Error: no '$PKG_NAME' release for '${WEBI_OS:-}' on '$WEBI_ARCH' as one of '$WEBI_FORMATS' by the tag '${WEBI_TAG:-}'"
echo >&2 " '$PKG_NAME' is available for '$PKG_OSES' on '$PKG_ARCHES' as one of '$PKG_FORMATS'"
echo >&2 " (check that the package name and version are correct)"
echo >&2 ""
echo >&2 " Double check at $(echo "$WEBI_RELEASES" | sed 's:\?.*::')"
echo >&2 ""
exit 1
fi
my_url="$WEBI_PKG_URL"
fi
# determine the location to download to
if [ -n "${2:-}" ]; then
my_dl="$2"
else
my_dl="${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
fi
WEBI_PKG_DOWNLOAD="${my_dl}"
export WEBI_PKG_DOWNLOAD
if [ -e "$my_dl" ]; then
echo "Found $my_dl"
return 0
fi
echo "Downloading $PKG_NAME from"
echo "$my_url"
# It's only 2020, we can't expect to have reliable CLI tools
# to tell us the size of a file as part of a base system...
if [ -n "$WEBI_WGET" ]; then
# wget has resumable downloads
# TODO wget -c --content-disposition "$my_url"
set +e
my_show_progress=""
if is_interactive_shell; then
my_show_progress="--show-progress"
fi
if ! wget -q $my_show_progress --user-agent="wget $WEBI_UA" -c "$my_url" -O "$my_dl.part"; then
echo >&2 "failed to download from $WEBI_PKG_URL"
exit 1
fi
set -e
else
# Neither GNU nor BSD curl have sane resume download options, hence we don't bother
# TODO curl -fsSL --remote-name --remote-header-name --write-out "$my_url"
my_show_progress="-#"
if is_interactive_shell; then
my_show_progress=""
fi
# shellcheck disable=SC2086
# we want the flags to be split
curl -fSL $my_show_progress -H "User-Agent: curl $WEBI_UA" "$my_url" -o "$my_dl.part"
fi
mv "$my_dl.part" "$my_dl"
echo ""
echo "Saved as $my_dl"
}
# detect which archives can be used
webi_extract() {
(
cd "$WEBI_TMP"
if [ "tar" = "$WEBI_EXT" ]; then
echo "Extracting ${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
tar xf "${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
elif [ "zip" = "$WEBI_EXT" ]; then
echo "Extracting ${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
unzip "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" > __unzip__.log
elif [ "exe" = "$WEBI_EXT" ]; then
echo "Moving ${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
mv "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" .
elif [ "xz" = "$WEBI_EXT" ]; then
echo "Inflating ${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
unxz -c "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" > "$(basename "$WEBI_PKG_FILE")"
else
# do nothing
echo "Failed to extract ${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
exit 1
fi
)
}
# use 'pathman' to update $HOME/.config/envman/PATH.env
webi_path_add() {
# make sure that we don't recursively install pathman with webi
my_path="$PATH"
export PATH="$HOME/.local/bin:$PATH"
# install pathman if not already installed
if [ -z "$(command -v pathman)" ]; then
"$HOME/.local/bin/webi" pathman > /dev/null
fi
export PATH="$my_path"
# in case pathman was recently installed and the PATH not updated
mkdir -p "$_webi_tmp"
# 'true' to prevent "too few arguments" output
# when there are 0 lines of stdout
"$HOME/.local/bin/pathman" add "$1" |
grep "export" 2> /dev/null \
>> "$_webi_tmp/.PATH.env" ||
true
}
# group common pre-install tasks as default
webi_pre_install() {
webi_check
webi_download
webi_extract
}
# move commands from the extracted archive directory to $HOME/.local/opt or $HOME/.local/bin
# shellcheck disable=2120
# webi_install may be sourced and used elsewhere
webi_install() {
if [ -n "$WEBI_SINGLE" ] || [ "single" = "${1:-}" ]; then
mkdir -p "$(dirname "$pkg_src_cmd")"
mv ./"$pkg_cmd_name"* "$pkg_src_cmd"
else
rm -rf "$pkg_src"
mv ./"$pkg_cmd_name"* "$pkg_src"
fi
}
# run post-install functions - just updating PATH by default
webi_post_install() {
webi_path_add "$(dirname "$pkg_dst_cmd")"
}
_webi_enable_exec() {
if [ -n "$(command -v spctl)" ] && [ -n "$(command -v xattr)" ]; then
# note: some packages contain files that cannot be affected by xattr
xattr -r -d com.apple.quarantine "$pkg_src" || true
return 0
fi
# TODO need to test that the above actually worked
# (and proceed to this below if it did not)
if [ -n "$(command -v spctl)" ]; then
echo "Checking permission to execute '$pkg_cmd_name' on macOS 11+"
set +e
is_allowed="$(spctl -a "$pkg_src_cmd" 2>&1 | grep valid)"
set -e
if [ -z "$is_allowed" ]; then
echo ""
echo "##########################################"
echo "# IMPORTANT: Permission Grant Required #"
echo "##########################################"
echo ""
echo "Requesting permission to execute '$pkg_cmd_name' on macOS 10.14+"
echo ""
sleep 3
spctl --add "$pkg_src_cmd"
fi
fi
}
# a friendly message when all is well, showing the final install path in $HOME/.local
_webi_done_message() {
echo "Installed $(_webi_canonical_name) as $pkg_dst_cmd"
}
##
##
## BEGIN custom override functions from <package>/install.sh
##
##
WEBI_SINGLE=
if [ -z "${WEBI_WELCOME:-}" ]; then
echo ""
printf "Thanks for using webi to install '\e[32m%s\e[0m' on '\e[31m%s/%s\e[0m'.\n" "${WEBI_PKG:-}" "$(uname -s)" "$(uname -m)"
echo "Have a problem? Experience a bug? Please let us know:"
echo " https://github.com/webinstall/webi-installers/issues"
echo ""
printf "\e[31mLovin'\e[0m it? Say thanks with a \e[34mStar on GitHub\e[0m:\n"
printf " \e[32mhttps://github.com/webinstall/webi-installers\e[0m\n"
echo ""
fi
__init_installer() {
# do nothing - to satisfy parser prior to templating
printf ""
# {{ installer }}
}
__init_installer
##
##
## END custom override functions
##
##
# run everything with defaults or overrides as needed
if command -v pkg_install > /dev/null ||
command -v pkg_link > /dev/null ||
command -v pkg_post_install > /dev/null ||
command -v pkg_done_message > /dev/null ||
command -v pkg_format_cmd_version > /dev/null ||
[ -n "${WEBI_SINGLE:-}" ] ||
[ -n "${pkg_cmd_name:-}" ] ||
[ -n "${pkg_dst_cmd:-}" ] ||
[ -n "${pkg_dst_dir:-}" ] ||
[ -n "${pkg_dst:-}" ] ||
[ -n "${pkg_src_cmd:-}" ] ||
[ -n "${pkg_src_dir:-}" ] ||
[ -n "${pkg_src:-}" ]; then
pkg_cmd_name="${pkg_cmd_name:-$PKG_NAME}"
if [ -n "$WEBI_SINGLE" ]; then
pkg_dst_cmd="${pkg_dst_cmd:-$HOME/.local/bin/$pkg_cmd_name}"
pkg_dst="$pkg_dst_cmd" # "$(dirname "$(dirname $pkg_dst_cmd)")"
#pkg_src_cmd="${pkg_src_cmd:-$HOME/.local/opt/$pkg_cmd_name-v$WEBI_VERSION/bin/$pkg_cmd_name-v$WEBI_VERSION}"
pkg_src_cmd="${pkg_src_cmd:-$HOME/.local/opt/$pkg_cmd_name-v$WEBI_VERSION/bin/$pkg_cmd_name}"
pkg_src="$pkg_src_cmd" # "$(dirname "$(dirname $pkg_src_cmd)")"
else
pkg_dst="${pkg_dst:-$HOME/.local/opt/$pkg_cmd_name}"
pkg_dst_cmd="${pkg_dst_cmd:-$pkg_dst/bin/$pkg_cmd_name}"
pkg_src="${pkg_src:-$HOME/.local/opt/$pkg_cmd_name-v$WEBI_VERSION}"
pkg_src_cmd="${pkg_src_cmd:-$pkg_src/bin/$pkg_cmd_name}"
fi
# this script is templated and these are used elsewhere
# shellcheck disable=SC2034
pkg_src_bin="$(dirname "$pkg_src_cmd")"
# shellcheck disable=SC2034
pkg_dst_bin="$(dirname "$pkg_dst_cmd")"
if [ -n "$(command -v pkg_pre_install)" ]; then pkg_pre_install; else webi_pre_install; fi
(
cd "$WEBI_TMP"
echo "Installing to $pkg_src_cmd"
if [ -n "$(command -v pkg_install)" ]; then pkg_install; else webi_install; fi
chmod a+x "$pkg_src"
chmod a+x "$pkg_src_cmd"
)
webi_link
_webi_enable_exec
(
cd "$WEBI_TMP"
if [ -n "$(command -v pkg_post_install)" ]; then pkg_post_install; else webi_post_install; fi
)
(
cd "$WEBI_TMP"
if [ -n "$(command -v pkg_done_message)" ]; then pkg_done_message; else _webi_done_message; fi
)
echo ""
fi
webi_path_add "$HOME/.local/bin"
if [ -z "${_WEBI_CHILD:-}" ] && [ -f "$_webi_tmp/.PATH.env" ]; then
if [ -n "$(cat "$_webi_tmp/.PATH.env")" ]; then
printf 'PATH.env updated with:\n'
sort -u "$_webi_tmp/.PATH.env"
printf "\n"
rm -f "$_webi_tmp/.PATH.env"
printf "\e[31mTO FINISH\e[0m: copy, paste & run the following command:\n"
printf "\n"
printf " \e[34msource ~/.config/envman/PATH.env\e[0m\n"
printf " (newly opened terminal windows will update automatically)\n"
fi
fi
# cleanup the temp directory
rm -rf "$WEBI_TMP"
# See? No magic. Just downloading and moving files.
}
__bootstrap_webi

View File

@@ -5,7 +5,7 @@
//
var usage = [
'Usage: node _webi/test.js --debug <path-to-package>',
'Example: node _webi/test.js --debug ./node/'
'Example: node _webi/test.js --debug ./node/',
].join('\n');
var count = 3;
@@ -32,8 +32,10 @@ if (/\b-?-h(elp)?\b/.test(process.argv.join(' '))) {
var os = require('os');
var fs = require('fs');
var path = require('path');
var Releases = require('./releases.js');
var uaDetect = require('./ua-detect.js');
var Builds = require('./builds.js');
var Installers = require('./installers.js');
var ServeInstaller = require('./serve-installer.js');
var pkg = process.argv[2].split('@');
var pkgdir = pkg[0];
var pkgtag = pkg[1] || '';
@@ -42,6 +44,7 @@ var nodes = fs.readdirSync(pkgdir);
nodes.forEach(function (node) {
nodesMap[node] = true;
});
var baseurl = 'https://webinstall.dev';
var maxLen = 0;
console.info('');
@@ -62,31 +65,49 @@ console.info('Has the necessary files?');
});
console.info('');
Releases.get(path.join(process.cwd(), pkgdir)).then(function (all) {
let projName = pkgdir.split('/').filter(Boolean).pop();
Builds.getPackage({ name: projName }).then(async function (/*projInfo*/) {
var pkgname = path.basename(pkgdir.replace(/\/$/, ''));
var osrel = os.platform() + '-' + os.release();
var arch = os.arch();
var formats = ['exe', 'xz', 'tar', 'zip'];
var nodeOs = os.platform();
var nodeOsRelease = os.release();
var nodeArch = os.arch();
var nodeLibc = 'libc';
if (process.platform === 'linux') {
nodeLibc = 'gnu';
let isUnofficial =
process.config.variables.node_release_urlbase.includes('unofficial');
if (isUnofficial) {
nodeLibc = 'musl';
}
}
var formats = ['exe', 'xz', 'tar', 'zip', 'git'];
var rel = all.releases.filter(function (rel) {
return (
formats.filter(function (ext) {
return rel.ext.match(ext);
})[0] &&
'stable' === rel.channel &&
rel.os === uaDetect.os(osrel) &&
rel.arch === uaDetect.arch(arch) &&
(!pkgtag ||
rel.tag === pkgtag ||
new RegExp('^' + pkgtag).test(rel.version))
);
})[0];
rel.oses = all.oses;
rel.arches = all.arches;
rel.formats = all.formats;
let [rel, opts] = await ServeInstaller.helper({
unameAgent: `${nodeOs}/${nodeOsRelease} ${nodeArch}/unknown ${nodeLibc}`,
projectName: pkgname,
tag: pkgtag || '',
formats: formats,
libc: nodeLibc,
});
Object.assign(
{
ver: '',
lts: null,
channel: '',
os: '',
arch: '',
limit: 0,
},
opts,
{
baseurl,
},
);
if (!rel) {
console.error('Error: ❌ no release found for current os, arch, and tag');
console.error(
`Error: ❌ no release found for @${pkgtag}?os=${nodeOs}&arch=${nodeArch}&libc=${nodeLibc}&formats=${formats}`,
);
process.exit(1);
return;
}
@@ -97,24 +118,8 @@ Releases.get(path.join(process.cwd(), pkgdir)).then(function (all) {
console.info('');
return Promise.all([
Releases.renderBash(pkgdir, rel, {
baseurl: 'https://webinstall.dev',
pkg: pkgname,
tag: pkgtag || '',
ver: '',
os: osrel,
arch,
formats: formats
}).catch(function () {}),
Releases.renderPowerShell(pkgdir, rel, {
baseurl: 'https://webinstall.dev',
pkg: pkgname,
tag: pkgtag || '',
ver: '',
os: osrel,
arch,
formats: formats
}).catch(function () {})
Installers.renderBash(pkgdir, rel, opts).catch(function () {}),
Installers.renderPowerShell(pkgdir, rel, opts).catch(function () {}),
]).then(function (scripts) {
var bashTxt = scripts[0];
var ps1Txt = scripts[1];
@@ -125,7 +130,7 @@ Releases.get(path.join(process.cwd(), pkgdir)).then(function (all) {
bashTxt = (bashTxt || 'echo ERROR').replace(/#set -x/g, 'set -x');
ps1Txt = (ps1Txt || 'echo ERROR').replace(
/REM REM todo debug/g,
'REM todo debug'
'REM todo debug',
);
}
console.info('Do the scripts actually work?');
@@ -138,5 +143,9 @@ Releases.get(path.join(process.cwd(), pkgdir)).then(function (all) {
console.info('\tNEEDS MANUAL TEST: powershell.exe %s', ps1File);
}
console.info('');
setTimeout(function () {
console.warn(`[warn] dangling event loop handle`);
process.exit(0);
}, 300).unref();
});
});

View File

@@ -1,13 +1,34 @@
'use strict';
var Releases = module.exports;
var path = require('path');
var Releases = require('./releases.js');
var request = require('@root/request');
var _normalize = require('./normalize.js');
var cache = {};
var staleAge = 5 * 1000;
var expiredAge = 15 * 1000;
//var staleAge = 5 * 1000;
//var expiredAge = 15 * 1000;
var staleAge = 5 * 60 * 1000;
var expiredAge = 15 * 60 * 1000;
let installerDir = path.join(__dirname, '..');
Releases.get = async function (pkgdir) {
let get;
try {
get = require(path.join(pkgdir, 'releases.js'));
} catch (e) {
let err = new Error('no releases.js for', pkgdir.split(/[\/\\]+/).pop());
err.code = 'E_NO_RELEASE';
throw err;
}
let all = await get(request);
return _normalize(all);
};
// TODO needs a proper test, and more accurate (though perhaps far less simple) code
function createFormatsSorter(formats) {
return function sortByVerExt(a, b) {
@@ -50,13 +71,14 @@ function createFormatsSorter(formats) {
return 1;
}
// Hacky-doo for linux: prefer musl
if (a._musl && !b._musl) {
// rank builds that don't depend on any form of libc first
if (a.libc === 'none' && b.libc !== 'none') {
return -1;
}
if (!a._musl && b._musl) {
if (a.libc !== 'none' && b.libc === 'none') {
return 1;
}
return 0;
};
}
@@ -64,64 +86,102 @@ function createFormatsSorter(formats) {
async function getCachedReleases(pkg) {
// returns { download: '<template string>', releases: [{ version, date, os, arch, lts, channel, download}] }
function putCache() {
cache[pkg].promise = cache[pkg].promise.then(function () {
var age = Date.now() - cache[pkg].updatedAt;
if (age < staleAge) {
//console.debug('NOT STALE ANYMORE - updated in previous promise');
return cache[pkg].all;
}
//console.debug('DOWNLOADING NEW "%s" releases', pkg);
var pkgdir = path.join(installerDir, pkg);
return Releases.get(pkgdir)
.then(function (all) {
//console.debug('DOWNLOADED NEW "%s" releases', pkg);
cache[pkg].updatedAt = Date.now();
cache[pkg].all = all;
})
.catch(function (e) {
console.error(
'Error fetching releases for "%s": %s',
pkg,
e.toString()
);
cache[pkg].all = { download: '', releases: [] };
})
.then(function () {
return cache[pkg].all;
});
});
async function chainCachePromise(fn) {
cache[pkg].promise = cache[pkg].promise.then(fn);
return cache[pkg].promise;
}
var p;
async function sleep(ms) {
return await new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
});
}
async function putCache() {
var age = Date.now() - cache[pkg].updatedAt;
if (age < staleAge) {
//console.debug('NOT STALE ANYMORE - updated in previous promise');
return cache[pkg].all;
}
//console.debug('DOWNLOADING NEW "%s" releases', pkg);
var pkgdir = path.join(installerDir, pkg);
// workaround for request timeout seeming to not work
let complete = false;
await Promise.race([
Releases.get(pkgdir)
.catch(function (err) {
if ('E_NO_RELEASE' === err.code) {
let all = { _error: 'E_NO_RELEASE', download: '', releases: [] };
return all;
}
throw err;
})
.catch(function (err) {
let hasReleases = cache[pkg].all?.releases?.length > 1;
if (!hasReleases) {
throw err;
}
console.error(`Error: the BOOGEYMAN got us!`);
console.error(err.stack);
return cache[pkg].all;
})
.then(function (all) {
// Note: it is possible for slightly older data
// to replace slightly newer data, but this is better
// than being in a cycle where release updates _always_
// take longer than expected.
//console.debug('DOWNLOADED NEW "%s" releases', pkg);
cache[pkg].updatedAt = Date.now();
cache[pkg].all = all;
complete = true;
}),
sleep(15000).then(function () {
if (complete) {
return;
}
console.error(`request timeout waiting for '${pkg}' release info`);
}),
]);
return cache[pkg].all;
}
if (!cache[pkg]) {
cache[pkg] = {
updatedAt: 0,
all: null,
promise: Promise.resolve()
all: { download: '', releases: [] },
promise: Promise.resolve(),
};
}
var bgRenewal;
var age = Date.now() - cache[pkg].updatedAt;
if (age >= expiredAge) {
//console.debug("EXPIRED - waiting");
p = putCache();
} else if (age >= staleAge) {
//console.debug("STALE - background update");
putCache();
p = Promise.resolve(cache[pkg].all);
} else {
//console.debug("FRESH");
p = Promise.resolve(cache[pkg].all);
var fresh = age < staleAge;
if (!fresh) {
bgRenewal = chainCachePromise(putCache);
}
return p;
var tooStale = age > expiredAge;
if (!tooStale) {
return await cache[pkg].all;
}
return await Promise.race([
bgRenewal,
sleep(5000).then(function () {
return cache[pkg].all;
}),
]);
}
async function filterReleases(
all,
{ ver, os, arch, lts, channel, formats, limit }
{ ver, os, arch, libc, lts, channel, formats, limit },
) {
// When multiple formats are downloadable (i.e. .zip and .pkg)
// sort the most compatible format first
@@ -130,40 +190,87 @@ async function filterReleases(
var sortByVerExt = createFormatsSorter(rformats);
var reVer = new RegExp('^' + ver + '\\b');
var sortedRels = all.releases
.filter(function (rel) {
if (
(os && rel.os !== os) ||
// Hacky-doo for linux musl
(arch && rel.arch !== arch) ||
(lts && !rel.lts) ||
(channel && rel.channel !== channel) ||
// to match 'tar.gz' and 'tar.xz' with just 'tar'
(formats.length &&
!formats.some(function (ext) {
return rel.ext.match(ext);
})) ||
(ver && !rel.version.match(reVer))
) {
function selectMatches(rel) {
if (os) {
if (rel.os !== '*') {
if (rel.os !== os) {
return false;
}
}
}
if (arch) {
if (rel.arch !== '*') {
if (rel.arch !== arch) {
return false;
}
}
}
if (rel.libc !== 'none') {
let releaseRequiresMusl = rel.libc === 'musl';
// goal: handle non-glibc (Alpine / Docker / musl)
let osHasMusl = libc === 'musl';
if (osHasMusl) {
// goal: fail if dependent on libc
let releaseRequiresLibc = rel.libc === 'gnu';
if (releaseRequiresLibc) {
return false;
}
} else if (releaseRequiresMusl) {
// goal: don't use musl++ on glibc (Ubuntu, GNU, etc)
return false;
}
return true;
})
.sort(sortByVerExt);
}
if (lts) {
if (!rel.lts) {
return false;
}
}
if (channel) {
if (rel.channel !== channel) {
return false;
}
}
// to match 'tar.gz' and 'tar.xz' with just 'tar'
function hasExt(ext) {
return rel.ext.match(ext);
}
if (formats.length) {
if (!formats.some(hasExt)) {
return false;
}
}
if (ver) {
if (!rel.version.match(reVer)) {
return false;
}
}
return true;
}
var sortedRels = all.releases.filter(selectMatches).sort(sortByVerExt);
//console.log(sortedRels.slice(0, 4));
return sortedRels.slice(0, limit || 1000);
}
module.exports = function getReleases({
Releases.getReleases = function ({
_count,
pkg,
ver,
os,
arch,
libc,
lts,
channel,
formats,
limit
limit,
}) {
if (!_count) {
_count = 0;
@@ -173,102 +280,133 @@ module.exports = function getReleases({
ver,
os,
arch,
libc,
lts,
channel,
formats,
limit
limit,
})
.catch(function (err) {
if ('MODULE_NOT_FOUND' === err.code) {
return null;
}
console.error(
'TODO: lib/release.js: check type of error, such as MODULE_NOT_FOUND'
'TODO: lib/release.js: check type of error, such as MODULE_NOT_FOUND',
);
console.error(err);
})
.then(function (releases) {
if (!releases.length) {
// Apple Silicon M1 hack-y do workaround fix
if (releases.length) {
return {
oses: all.oses,
arches: all.arches,
libcs: all.libcs,
formats: all.formats,
releases: releases,
};
}
if (_count < 1) {
// Apple Silicon M1 hacky-do workaround fix
if ('macos' === os && 'arm64' === arch) {
return getReleases({
return Releases.getReleases({
pkg,
ver,
os,
arch: 'amd64',
libc,
lts,
channel,
formats,
limit
limit,
});
}
// Raspberry Pi 3+ on Raspbian x86 (not Ubuntu arm64)
if (!_count && 'linux' === os && 'armv7l' === arch) {
return getReleases({
_count: _count + 1,
// Windows ARM hacky-do workaround fix
if ('windows' === os && 'arm64' === arch) {
return Releases.getReleases({
pkg,
ver,
os,
arch: 'arm64',
arch: 'amd64',
libc,
lts,
channel,
formats,
limit
limit,
});
}
// Raspberry Pi 3+ on Ubuntu arm64 (via Bionic?)
// (this may be the same as the prior search, that's okay)
if ('linux' === os && 'arm64' === arch) {
return getReleases({
return Releases.getReleases({
_count: _count + 1,
pkg,
ver,
os,
arch: 'armv7l',
libc,
lts,
channel,
formats,
limit
limit,
});
}
// Raspberry Pi 3+ on Ubuntu arm64 (via Bionic?)
// armv7 can run armv6
if ('linux' === os && 'armv7l' === arch) {
return getReleases({
return Releases.getReleases({
_count: _count + 1,
pkg,
ver,
os,
arch: 'armv6l',
libc,
lts,
channel,
formats,
limit
limit,
});
}
releases = [
{
name: 'doesntexist.ext',
version: '0.0.0',
lts: '-',
channel: 'error',
date: '1970-01-01',
os: os || '-',
arch: arch || '-',
_musl: undefined,
ext: 'err',
download: 'https://example.com/doesntexist.ext',
comment:
'No matches found. Could be bad or missing version info' +
',' +
"Check query parameters. Should be something like '/api/releases/{package}@{version}.tab?os={macos|linux|windows|-}&arch={amd64|x86|aarch64|arm64|armv7l|-}&limit=100'"
}
];
}
if (_count < 2) {
// Raspberry Pi 3+ on Raspbian arm7 (not Ubuntu arm64)
// hail mary
if ('linux' === os && 'armv7l' === arch) {
return Releases.getReleases({
_count: _count + 1,
pkg,
ver,
os,
arch: 'arm64',
libc,
lts,
channel,
formats,
limit,
});
}
}
releases = [
{
name: 'doesntexist.ext',
version: '0.0.0',
lts: '-',
channel: 'error',
date: '1970-01-01',
os: os || '-',
arch: arch || '-',
libc: libc || '-',
ext: 'err',
download: 'https://example.com/doesntexist.ext',
comment:
'No matches found. Could be bad or missing version info' +
',' +
"Check query parameters. Should be something like '/api/releases/{package}@{version}.tab?os={macos|linux|windows|-}&arch={amd64|x86|aarch64|arm64|armv7l|-}&libc={musl|gnu|msvc|libc|static}&limit=10'",
},
];
return {
oses: all.oses,
arches: all.arches,
libcs: all.libcs,
formats: all.formats,
releases: releases
releases: releases,
};
});
});
@@ -282,9 +420,10 @@ if (require.main === module) {
os: 'macos',
arch: 'amd64',
lts: true,
libc: 'libc',
channel: 'stable',
formats: ['tar', 'exe', 'zip', 'xz', 'dmg', 'pkg'],
limit: 10
limit: 10,
})
.then(function (all) {
console.info(JSON.stringify(all));

View File

@@ -16,11 +16,11 @@ function getRequest(req) {
}
return {
unix: 'curl -fsSA "$(uname -a)" ' + url,
unix: 'curl -fsSA "$(uname -srm)" ' + url,
windows: 'curl.exe -fsSA "MS $Env:PROCESSOR_ARCHITECTURE" ' + url,
ua: ua,
os: uaDetect.os(ua),
arch: uaDetect.arch(ua)
arch: uaDetect.arch(ua),
};
}
@@ -56,16 +56,23 @@ function getArch(ua) {
return '-';
}
// quick hack for Apple Silicon M1
// Quick hack for Apple Silicon M1
//
// Note: we now use `uname -srm` which does not have the native arch
// info included with `uname -v` and `uname -a`.
//
// Native: Darwin boomer.local 20.2.0 Darwin Kernel Version 20.2.0: Wed Dec 2 20:40:21 PST 2020; root:xnu-7195.60.75~1/RELEASE_ARM64_T8101 arm64
// Resetta: Darwin boomer.local 20.2.0 Darwin Kernel Version 20.2.0: Wed Dec 2 20:40:21 PST 2020; root:xnu-7195.60.75~1/RELEASE_ARM64_T8101 x86_64
ua = ua.replace(/xnu-.*RELEASE_[^\s]*/, '');
if (/aarch64|arm64|arm8|armv8/i.test(ua)) {
return 'arm64';
} else if (/aarch|arm7|armv7/i.test(ua)) {
} else if (/aarch|arm7|armv7|arm32/i.test(ua)) {
return 'armv7l';
} else if (/arm6|armv6/i.test(ua)) {
} else if (/arm6|armv6|arm(\b|_)/i.test(ua)) {
return 'armv6l';
} else if (/ppc64le/i.test(ua)) {
return 'ppc64le';
} else if (/ppc64/i.test(ua)) {
return 'ppc64';
} else if (/mips64/i.test(ua)) {
@@ -84,6 +91,63 @@ function getArch(ua) {
}
}
function getLibc(ua) {
if ('-' === ua) {
return '-';
}
// How to see a bunch of target host quadruples:
// go tool dist list
// rustup target list
// zig targets | jq -r '.libc[]' | sort -
// Generally, we prefer 'musl' builds because they DO work on glibc systems (Ubuntu),
// but 'glibc' builds will NOT work on musl systems (Alpine / Docker).
//
// However, there are a few instances (ex: Node.js), where the 'musl' builds
// DO NOT work on glibc systems.
{
let muslRe = /(\b|_)(musl)(\b|_)/i;
if (muslRe.test(ua)) {
return 'musl';
}
}
{
let msvcRe = /(\b|_)(msvc|windows|microsoft)(\b|_)/i;
if (msvcRe.test(ua)) {
return 'msvc';
}
}
{
let gnuRe = /(\b|_)(gnu|glibc|linux)(\b|_)/i;
if (gnuRe.test(ua)) {
return 'gnu';
}
}
{
let libcRe = /(\b|_)(libc)(\b|_)/i;
if (libcRe.test(ua)) {
return 'libc';
}
}
{
let darwinRe = /(\b|_)(darwin)(\b|_)/i;
if (darwinRe.test(ua)) {
// not sure whether this should be "darwin" or "libc" or "none"
// https://opensource.apple.com/source/Libc/
return 'libc';
}
}
return 'libc';
}
uaDetect.os = getOs;
uaDetect.arch = getArch;
uaDetect.libc = getLibc;
uaDetect.request = getRequest;

View File

@@ -1,126 +0,0 @@
#!/usr/bin/env pwsh
# this allows us to call ps1 files, which allows us to have spaces in filenames
# ('powershell "$Env:USERPROFILE\test.ps1" foo' will fail if it has a space in
# the path but '& "$Env:USERPROFILE\test.ps1" foo' will work even with a space)
Set-ExecutionPolicy -Scope Process Bypass
# If a command returns an error, halt the script.
$ErrorActionPreference = 'Stop'
# Ignore progress events from cmdlets so Invoke-WebRequest is not painfully slow
$ProgressPreference = 'SilentlyContinue'
# This is the canonical CPU arch when the process is emulated
$my_arch = "$Env:PROCESSOR_ARCHITEW6432"
IF ($my_arch -eq $null -or $my_arch -eq "") {
# This is the canonical CPU arch when the process is native
$my_arch = "$Env:PROCESSOR_ARCHITECTURE"
}
# TODO API should know to prefer x86 for windows when arm binary is not available
$Env:WEBI_UA = "Windows/10 $my_arch"
$exename = $args[0]
# Switch to userprofile
pushd $Env:USERPROFILE
# Make paths if needed
New-Item -Path .local\bin -ItemType Directory -Force | out-null
# TODO replace all xbin with opt\bin\
New-Item -Path .local\xbin -ItemType Directory -Force | out-null
# See note on Set-ExecutionPolicy above
Set-Content -Path .local\bin\webi.bat -Value "@echo off`r`npushd %USERPROFILE%`r`npowershell -ExecutionPolicy Bypass .local\bin\webi-pwsh.ps1 %1`r`npopd"
# Backwards-compat bugfix: remove old webi-pwsh.ps1 location
Remove-Item -Path .local\bin\webi.ps1 -Recurse -ErrorAction Ignore
if (!(Test-Path -Path .local\opt))
{
New-Item -Path .local\opt -ItemType Directory -Force | out-null
}
# TODO windows version of mktemp -d
if (!(Test-Path -Path .local\tmp))
{
New-Item -Path .local\tmp -ItemType Directory -Force | out-null
}
# TODO SetStrictMode
# TODO Test-Path variable:global:Env:WEBI_HOST ???
IF($Env:WEBI_HOST -eq $null -or $Env:WEBI_HOST -eq "")
{
$Env:WEBI_HOST = "https://webinstall.dev"
}
if (!(Test-Path -Path .local\bin\pathman.exe))
{
& curl.exe -fsSL -A "$Env:WEBI_UA" "$Env:WEBI_HOST/packages/pathman/install.ps1" -o .\.local\tmp\pathman-setup.ps1
powershell .\.local\tmp\pathman-setup.ps1
# TODO del .\.local\tmp\pathman-setup.bat
}
# Run pathman to set up the folder
# (using unix style path because... cmd vs powershell vs whatever)
$has_local_bin = echo "$Env:PATH" | Select-String -Pattern '\.local.bin'
if (!$has_local_bin)
{
Write-Host ''
Write-Host '**********************************' -ForegroundColor red -BackgroundColor white
Write-Host '* IMPORTANT -- READ ME *' -ForegroundColor red -BackgroundColor white
Write-Host '* (run the PATH command below) *' -ForegroundColor red -BackgroundColor white
Write-Host '**********************************' -ForegroundColor red -BackgroundColor white
Write-Host ''
& "$Env:USERPROFILE\.local\bin\pathman.exe" add ~/.local/bin
}
# {{ baseurl }}
# {{ version }}
$my_version = 'v1.1.15'
## show help if no params given or help flags are used
if ($exename -eq $null -or $exename -eq "-h" -or $exename -eq "--help" -or $exename -eq "help" -or $exename -eq "/?") {
Write-Host "webi " -ForegroundColor Green -NoNewline; Write-Host "$my_version " -ForegroundColor Red -NoNewline; Write-Host "Copyright 2020+ AJ ONeal"
Write-Host " https://webinstall.dev/webi" -ForegroundColor blue
echo ""
echo "SUMMARY"
echo " Webi is the best way to install the modern developer tools you love."
echo " It's fast, easy-to-remember, and conflict free."
echo ""
echo "USAGE"
echo " webi <thing1>[@version] [thing2] ..."
echo ""
echo "UNINSTALL"
echo " Almost everything that is installed with webi is scoped to"
echo " ~/.local/opt/<thing1>, so you can remove it like so:"
echo ""
echo " rmdir /s %USERPROFILE%\.local\opt\<thing1>"
echo " del %USERPROFILE%\.local\bin\<thing1>"
echo ""
echo " Some packages have special uninstall instructions, check"
echo " https://webinstall.dev/<thing1> to be sure."
echo ""
echo "FAQ"
Write-Host " See " -NoNewline; Write-Host "https://webinstall.dev/faq" -ForegroundColor blue
echo ""
echo "ALWAYS REMEMBER"
echo " Friends don't let friends use brew for simple, modern tools that don't need it."
exit 0
}
if ($exename -eq "-V" -or $exename -eq "--version" -or $exename -eq "version" -or $exename -eq "/v") {
Write-Host "webi " -ForegroundColor Green -NoNewline; Write-Host "$my_version " -ForegroundColor Red -NoNewline; Write-Host "Copyright 2020+ AJ ONeal"
Write-Host " https://webinstall.dev/webi" -ForegroundColor blue
exit 0
}
# Fetch <whatever>.ps1
# TODO detect formats
$PKG_URL = "$Env:WEBI_HOST/api/installers/$exename.ps1?formats=zip,exe,tar"
echo "Downloading $PKG_URL"
# Invoke-WebRequest -UserAgent "Windows amd64" "$PKG_URL" -OutFile ".\.local\tmp\$exename.install.ps1"
& curl.exe -fsSL -A "$Env:WEBI_UA" "$PKG_URL" -o .\.local\tmp\$exename.install.ps1
# Run <whatever>.ps1
powershell .\.local\tmp\$exename.install.ps1
# Done
popd

View File

@@ -1,38 +0,0 @@
@echo off
pushd "%userprofile%" || goto :error
IF NOT EXIST .local (
mkdir .local || goto :error
)
IF NOT EXIST .local\bin (
mkdir .local\bin || goto :error
)
IF NOT EXIST .local\opt (
mkdir .local\opt || goto :error
)
pushd .local\bin || goto :error
if NOT EXIST pathman.exe (
echo updating PATH management
powershell $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://webinstall.dev/packages/pathman/install.bat -OutFile pathman-setup.bat || goto :error
call .\pathman-setup.bat || goto :error
del pathman-setup.bat || goto :error
rem TODO there's rumor of a windows tool called 'pathman' that does the same thing?
)
popd || goto :error
.\.local\bin\pathman add ".local\bin" || goto :error
echo downloading and installing %1
powershell $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://webinstall.dev/packages/%1/install.ps1 -OutFile %1-webinstall.bat || goto :error
rem TODO only add if it's not in there already
PATH .local\bin;%PATH%
call %1-webinstall.bat || goto :error
del %1-webinstall.bat || goto :error
popd
goto :EOF
:error
echo Failed with error #%errorlevel%.
exit /b %errorlevel%

View File

@@ -1 +0,0 @@
powershell -ExecutionPolicy Bypass .\.local\bin\webi-pwsh.ps1 %1

125
aliasman/README.md Normal file
View File

@@ -0,0 +1,125 @@
---
title: aliasman
homepage: https://github.com/BeyondCodeBootcamp/aliasman
tagline: |
aliasman: A cross-shell (POSIX-compliant) alias manager for bash, zsh, and fish
---
To update or switch versions, run `webi aliasman@stable` (or `@v1.0.0`, `@beta`,
etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.config/envman/alias.env
~/.local/bin/aliasman
```
## Cheat Sheet
> `aliasman` helps you love your *alias*es again! 🥸 \
> Set 'em once, use 'em everywhere! \
> (and all in just one dotfile, for an on-the-go friendly config)
```sh
aliasman <alias-name> <command-name> [args, pipes, redirs, etc]
```
### What are Aliases?
An _alias_ is just shorthand for a shell function.
Take a long command like this:
```sh
git commit -m "feat: new feature"
```
And turn it into a short command, like this:
```sh
gc "feat: new feature"
```
(that would be `aliasman gc 'git commit -m'`)
### Imagine the possibilities!
1. What if you could quickly create a _command_, `ll`, \
that does the work of `ls -lAhF`!?
2. Set an _alias_ to do just that!
```sh
aliasman ll 'ls -lAhF'
```
3. Reload your alias config (or open a _new terminal_)
```sh
source ~/.config/envman/alias.env
```
4. Use it!
```sh
ll
```
```text
drwxr-xr-x aj wheel 416 B Thu Feb 9 02:08:39 2023 📂 .git/
.rwxr-xr-x aj staff 6.2 KB Thu Feb 9 01:36:30 2023 💻 aliasman*
.rw-r--r-- aj wheel 16 KB Wed Feb 8 21:51:06 2023 🔑 LICENSE
.rw-r--r-- aj wheel 1.4 KB Thu Feb 9 01:47:13 2023 📄 README.md
```
### Common aliases
Use *alias*es to make other tools you find around webi even _more_ convenient
⚡️ (and powerful 💪).
```sh
aliasman curl 'curlie'
aliasman diffy 'diff -y --suppress-common-lines'
aliasman gc 'git commit -m'
aliasman gri 'git rebase -i'
aliasman la 'lsd -AF'
aliasman ll 'lsd -lAhF'
aliasman ls 'lsd -F'
aliasman rgi 'rg -i'
aliasman tree 'lsd -F --tree --group-dirs=last'
# random password generator
aliasman rnd 'xxd -l24 -ps /dev/urandom'
```
### How to replace an alias
Just run the command again!
```sh
aliasman ll 'lsd -l'
aliasman ll 'lsd -lAhF'
```
### How to delete an alias
With `--delete`!
```sh
aliasman --delete ll
```
### How to see an alias
Just supply the name!
```sh
aliasman rnd
```
```text
alias rnd='xxd -l24 -ps /dev/urandom'
```

41
aliasman/install.sh Normal file
View File

@@ -0,0 +1,41 @@
#!/bin/sh
set -e
set -u
__init_aliasman() {
######################
# Install aliasman #
######################
# Every package should define these 6 variables
pkg_cmd_name="aliasman"
pkg_dst_cmd="$HOME/.local/bin/aliasman"
pkg_dst="$pkg_dst_cmd"
pkg_src_cmd="$HOME/.local/opt/aliasman-v$WEBI_VERSION/bin/aliasman"
pkg_src_dir="$HOME/.local/opt/aliasman-v$WEBI_VERSION"
pkg_src="$pkg_src_cmd"
# pkg_install must be defined by every package
pkg_install() {
# ~/.local/opt/aliasman-v1.0.0/bin
mkdir -p "$(dirname "$pkg_src_cmd")"
# mv ./*aliasman*/aliasman ~/.local/opt/aliasman-v1.0.0/bin/aliasman
mv ./*aliasman*/aliasman "$pkg_src_cmd"
}
# pkg_get_current_version is recommended, but (soon) not required
pkg_get_current_version() {
# 'aliasman version' has output in this format:
# aliasman v1.0.0 (2023-01-15)
# Copyright 2023 AJ ONeal
# This trims it down to just the version number:
# 1.0.0
aliasman version | head -n 1 | cut -d ' ' -f 2 | sed 's:^v::'
}
}
__init_aliasman

30
aliasman/releases.js Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
var githubSource = require('../_common/github-source.js');
var owner = 'BeyondCodeBootcamp';
var repo = 'aliasman';
module.exports = function (request) {
let arches = [
'amd64',
'arm64',
'armv6l',
'armv7l',
'ppc64le',
'ppc64',
's390x',
'x86',
];
let oses = ['freebsd', 'linux', 'macos', 'posix'];
return githubSource(request, owner, repo, oses, arches).then(function (all) {
all._names = ['aliasman', 'legacy'];
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -7,6 +7,16 @@ tagline: |
To update or switch versions, run `webi arc@stable` (or `@v3.5`, `@beta`, etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/arc
```
## Cheat Sheet
> Archiver (`arc`) is a powerful and flexible library meets an elegant CLI in
@@ -18,42 +28,42 @@ create a top-level directory if one does not exist.
### List
```txt
```text
# arc ls <archive file>
arc ls example.zip
```
### Unarchive (whole)
```txt
```text
# arc unarchive <archive file>
arc unarchive example.zip
```
### Extract (partial)
```txt
```text
# arc extract <archive file> <archived path> <extracted path>
arc extract example.zip example/foo ~/Downloads/foo
```
### Archive (recursive)
```txt
```text
# arc archive <archive file> <files or folders ...>
arc archive example.zip ./README.md ./bin ./src
```
### Compress (single file)
```txt
```text
# arc compress <single file> <format>
arc compress ./example.tar xz
```
### Decompress (single file)
```txt
```text
# arc decompress <archive file>
arc decompress ./example.tar.xz
```

View File

@@ -15,41 +15,39 @@ $pkg_src_bin = "$Env:USERPROFILE\.local\opt\archiver-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\archiver-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | out-null
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"))
{
echo "Downloading archiver from $Env:WEBI_PKG_URL to $pkg_download"
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading archiver from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& move "$pkg_download.part" "$pkg_download"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd"))
{
echo "Installing archiver"
IF (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing archiver"
# TODO: create package-specific temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\arc-*" -Recurse -ErrorAction Ignore
Remove-Item -Path ".\arc.exe" -Recurse -ErrorAction Ignore
# Remove any leftover tmp cruft
Remove-Item -Path ".\arc-*" -Recurse -ErrorAction Ignore
Remove-Item -Path ".\arc.exe" -Recurse -ErrorAction Ignore
# Move single binary into root of temporary folder
& move "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE" "arc.exe"
# Move single binary into root of temporary folder
& Move-Item "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE" "arc.exe"
# Settle unpacked archive into place
echo "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | out-null
Move-Item -Path "arc.exe" -Destination "$pkg_src_bin"
# Settle unpacked archive into place
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Move-Item -Path "arc.exe" -Destination "$pkg_src_bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | out-null
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

View File

@@ -6,6 +6,7 @@ var repo = 'archiver';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
all._names = ['archiver', 'arc'];
return all;
});
};

View File

@@ -1,5 +1,5 @@
#!/bin/pwsh
echo "'archiver@$Env:WEBI_TAG' is an alias for 'arc@$Env:WEBI_VERSION'"
IF ($Env:WEBI_HOST -eq $null -or $Env:WEBI_HOST -eq "") { $Env:WEBI_HOST = "https://webinstall.dev" }
curl.exe -fsSL "$Env:WEBI_HOST/arc@$Env:WEBI_VERSION" | powershell
Write-Output "'archiver@$Env:WEBI_TAG' is an alias for 'arc@$Env:WEBI_VERSION'"
IF ($null -eq $Env:WEBI_HOST -or $Env:WEBI_HOST -eq "") { $Env:WEBI_HOST = "https://webinstall.dev" }
curl.exe -A MS -fsSL "$Env:WEBI_HOST/arc@$Env:WEBI_VERSION" | powershell

View File

@@ -3,9 +3,9 @@ set -e
set -u
__redirect_alias_arc() {
echo "'archiver@${WEBI_TAG:-stable}' is an alias for 'arc@${WEBI_VERSION:-}'"
echo "'archiver@${WEBI_TAG:-stable}' is an alias for 'arc@${WEBI_VERSION-}'"
WEBI_HOST=${WEBI_HOST:-"https://webinstall.dev"}
curl -fsSL "$WEBI_HOST/arc@${WEBI_VERSION:-}" | sh
curl -fsSL "$WEBI_HOST/arc@${WEBI_VERSION-}" | sh
}
__redirect_alias_arc

538
atomicparsley/README.md Normal file
View File

@@ -0,0 +1,538 @@
---
title: atomicparsley
homepage: https://github.com/wez/atomicparsley
tagline: |
AtomicParsley is a lightweight tool for reading, parsing and setting iTunes-style metadata.
---
To update or switch versions, run `webi atomicparsley@stable` (or `@v20221229`,
`@beta`, etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/AtomicParsley
```
**Windows Users**
```text
\Windows\System32\vcruntime140.dll
```
This will also attempt to install the
[Microsoft Visual C++ Redistributable](/vcruntime) via `webi vcruntime`. If it
fails and you get the error _`vcruntime140.dll` was not found_, you'll need to
[install it manually](https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170).
## Cheat Sheet
> `AtomicParsley` is an alternative to `ffmpeg` and `ffprobe` for viewing and
> changing metadata in MPEG-4 files with .3gp, .3g2, .mp4, .m4a, .m4b, .m4p,
> .m4r, and .m4v extensions. Because it's purpose-built for MP4 containers, it
> can do some things `ffmpeg` can't.
### How to Change Album Art
1. Save any existing cover art
```sh
AtomicParsley ./my-song.m4a --extractPixToPath ./ 'my-song'
```
2. Remove the artwork
```sh
AtomicParsley ./my-song.m4a --artwork REMOVE_ALL
```
3. **(macOS ONLY)** Add new artwork at the specified DPI (and other constraints)
```sh
export PIC_OPTIONS="DPI=72"
AtomicParsley ./my-podcast-audiobook.m4b --artwork ./my-season-1-cover.jpg
```
Only JPEG and PNG are supported. See `PIC_OPTIONS` down below for more options.
If you get an error, try exporting your file with a DPI to 72 (or up to 300 with
`PIC_OPTIONS` set) and a smaller resolution - perhaps 600x600 or 1500x1500 (what
old versions iTunes versions used).
### How to Remove Apple Account Info
```sh
AtomicParsley ./my-file.m4a \
--DeepScan \
--manualAtomRemove "moov.trak.mdia.minf.stbl.mp4a.pinf" \
--manualAtomRemove "moov.udta.meta.ilst.apID" \
--manualAtomRemove "moov.udta.meta.ilst.cnID" \
--manualAtomRemove "moov.udta.meta.ilst.purd" \
--manualAtomRemove "moov.udta.meta.ilst.sfID" \
--manualAtomRemove "moov.udta.meta.ilst.soal" \
--manualAtomRemove "moov.udta.meta.ilst.xid"
```
If you wanted to also remove information that indicates which Country or
Language the song was purchased in, or which album it was purchased from, there
are some additional IDs to consider:
| Metadata Tag | Description |
| ---------------------------------------- | -------------------------------------------- |
| moov.udta.meta.ilst.apID | Apple account email address |
| moov.udta.meta.ilst.ownr | Apple account username |
| moov.udta.meta.ilst.atID | Artist-track ID |
| moov.udta.meta.ilst.cnID | iTunes Catalog ID |
| moov.udta.meta.ilst.geID | Genre ID |
| moov.udta.meta.ilst.plID | Playlist ID (identifies album) |
| moov.udta.meta.ilst.sfID | iTunes store identifier (location/number) |
| moov.udta.meta.ilst.cprt | Copyright information |
| moov.udta.meta.ilst.flvr | Bitrate/video size related |
| moov.udta.meta.ilst.purd | Date purchased |
| moov.udta.meta.ilst.rtng | Explicit/Clean information |
| moov.udta.meta.ilst.soal | Album sort name |
| moov.udta.meta.ilst.stik | Media type information |
| moov.udta.meta.ilst.xid | Vendor xID |
| moov.udta.meta.ilst.----.name:[iTunMOVI] | Embedded plist contains filesize and flavor. |
| moov.trak.mdia.minf.stbl.stsd.mp4a.pinf | Purchase information related |
See <https://gist.github.com/riophae/f5694fd2952cb64982689b971ca6ec79>.
### Genre Lists
All values are **case sensitive**.
#### The "stik" List
These values are **case sensitive**:
0. `Home Video`
1. `Normal` (meaning music)
2. `Audiobook` (changes extension to .m4b)
3. `Whacked Bookmark`
4. `Music Video`
5. `Movie`
6. `Short Film`
7. `TV Show`
8. `Booklet`
#### Standard Music Genres
```txt
(1.) Blues
(2.) Classic Rock
(3.) Country
(4.) Dance
(5.) Disco
(6.) Funk
(7.) Grunge
(8.) Hip-Hop
(9.) Jazz
(10.) Metal
(11.) New Age
(12.) Oldies
(13.) Other
(14.) Pop
(15.) R&B
(16.) Rap
(17.) Reggae
(18.) Rock
(19.) Techno
(20.) Industrial
(21.) Alternative
(22.) Ska
(23.) Death Metal
(24.) Pranks
(25.) Soundtrack
(26.) Euro-Techno
(27.) Ambient
(28.) Trip-Hop
(29.) Vocal
(30.) Jazz+Funk
(31.) Fusion
(32.) Trance
(33.) Classical
(34.) Instrumental
(35.) Acid
(36.) House
(37.) Game
(38.) Sound Clip
(39.) Gospel
(40.) Noise
(41.) AlternRock
(42.) Bass
(43.) Soul
(44.) Punk
(45.) Space
(46.) Meditative
(47.) Instrumental Pop
(48.) Instrumental Rock
(49.) Ethnic
(50.) Gothic
(51.) Darkwave
(52.) Techno-Industrial
(53.) Electronic
(54.) Pop-Folk
(55.) Eurodance
(56.) Dream
(57.) Southern Rock
(58.) Comedy
(59.) Cult
(60.) Gangsta
(61.) Top 40
(62.) Christian Rap
(63.) Pop/Funk
(64.) Jungle
(65.) Native American
(66.) Cabaret
(67.) New Wave
(68.) Psychadelic
(69.) Rave
(70.) Showtunes
(71.) Trailer
(72.) Lo-Fi
(73.) Tribal
(74.) Acid Punk
(75.) Acid Jazz
(76.) Polka
(77.) Retro
(78.) Musical
(79.) Rock & Roll
(80.) Hard Rock
(81.) Folk
(82.) Folk/Rock
(83.) National Folk
(84.) Swing
(85.) Fast Fusion
(86.) Bebob
(87.) Latin
(88.) Revival
(89.) Celtic
(90.) Bluegrass
(91.) Avantgarde
(92.) Gothic Rock
(93.) Progressive Rock
(94.) Psychedelic Rock
(95.) Symphonic Rock
(96.) Slow Rock
(97.) Big Band
(98.) Chorus
(99.) Easy Listening
(100.) Acoustic
(101.) Humour
(102.) Speech
(103.) Chanson
(104.) Opera
(105.) Chamber Music
(106.) Sonata
(107.) Symphony
(108.) Booty Bass
(109.) Primus
(110.) Porn Groove
(111.) Satire
(112.) Slow Jam
(113.) Club
(114.) Tango
(115.) Samba
(116.) Folklore
(117.) Ballad
(118.) Power Ballad
(119.) Rhythmic Soul
(120.) Freestyle
(121.) Duet
(122.) Punk Rock
(123.) Drum Solo
(124.) A Capella
(125.) Euro-House
(126.) Dance Hall
```
#### iTunes Movie Genre IDs
```sh
AtomicParsley --genre-movie-id-list
```
```text
(4401) Action & Adventure
(4402) Anime
(4403) Classics
(4404) Comedy
(4405) Documentary
(4406) Drama
(4407) Foreign
(4408) Horror
(4409) Independent
(4410) Kids & Family
(4411) Musicals
(4412) Romance
(4413) Sci-Fi & Fantasy
(4414) Short Films
(4415) Special Interest
(4416) Thriller
(4417) Sports
(4418) Western
(4419) Urban
(4420) Holiday
(4421) Made for TV
(4422) Concert Films
(4423) Music Documentaries
(4424) Music Feature Films
(4425) Japanese Cinema
(4426) Jidaigeki
(4427) Tokusatsu
(4428) Korean Cinema
```
#### iTunes TV Genre IDs
```sh
AtomicParsley --genre-tv-id-list
```
```text
(4000) Comedy
(4001) Drama
(4002) Animation
(4003) Action & Adventure
(4004) Classic
(4005) Kids
(4005) Nonfiction
(4007) Reality TV
(4008) Sci-Fi & Fantasy
(4009) Sports
(4010) Teens
(4011) Latino TV
```
### Help
Note: Normally we don't include full help text, but since AtomicParsley has a
long history across various maintainers and repos, we feel it's appropriate to
do so in this case.
```text
AtomicParsley sets metadata into MPEG-4 files & derivatives supporting 3 tag
schemes: iTunes-style, 3GPP assets & ISO defined copyright notifications.
AtomicParsley quick help for setting iTunes-style metadata into MPEG-4 files.
General usage examples:
AtomicParsley /path/to.mp4 -T 1
AtomicParsley /path/to.mp4 -t +
AtomicParsley /path/to.mp4 --artist "Me" --artwork /path/to/art.jpg
Atomicparsley /path/to.mp4 --albumArtist "You" --podcastFlag true
Atomicparsley /path/to.mp4 --stik "TV Show" --advisory explicit
Getting information about the file & tags:
-T --test Test file for mpeg4-ishness & print atom tree
-t --textdata Prints tags embedded within the file
-E --extractPix Extracts pix to the same folder as the mpeg-4 file
Setting iTunes-style metadata tags
--artist (string) Set the artist tag
--title (string) Set the title tag
--album (string) Set the album tag
--genre (string) Genre tag (see --longhelp for more info)
--tracknum (num)[/tot] Track number (or track number/total tracks)
--disk (num)[/tot] Disk number (or disk number/total disks)
--comment (string) Set the comment tag
--year (num|UTC) Year tag (see --longhelp for "Release Date")
--lyrics (string) Set lyrics (not subject to 256 byte limit)
--lyricsFile (/path) Set lyrics to the content of a file
--composer (string) Set the composer tag
--copyright (string) Set the copyright tag
--grouping (string) Set the grouping tag
--artwork (/path) Set a piece of artwork (jpeg or png only)
--bpm (number) Set the tempo/bpm
--albumArtist (string) Set the album artist tag
--compilation (boolean) Set the compilation flag (true or false)
--hdvideo (number) Set the hdvideo flag to one of:
false or 0 for standard definition
true or 1 for 720p
2 for 1080p
--advisory (string*) Content advisory (*values: 'clean', 'explicit')
--stik (string*) Sets the iTunes "stik" atom (see --longhelp)
--description (string) Set the description tag
--longdesc (string) Set the long description tag
--storedesc (string) Set the store description tag
--TVNetwork (string) Set the TV Network name
--TVShowName (string) Set the TV Show name
--TVEpisode (string) Set the TV episode/production code
--TVSeasonNum (number) Set the TV Season number
--TVEpisodeNum (number) Set the TV Episode number
--podcastFlag (boolean) Set the podcast flag (true or false)
--category (string) Sets the podcast category
--keyword (string) Sets the podcast keyword
--podcastURL (URL) Set the podcast feed URL
--podcastGUID (URL) Set the episode's URL tag
--purchaseDate (UTC) Set time of purchase
--encodingTool (string) Set the name of the encoder
--encodedBy (string) Set the name of the Person/company who encoded the file
--apID (string) Set the Account Name
--cnID (number) Set the iTunes Catalog ID (see --longhelp)
--geID (number) Set the iTunes Genre ID (see --longhelp)
--xID (string) Set the vendor-supplied iTunes xID (see --longhelp)
--gapless (boolean) Set the gapless playback flag
--contentRating (string*) Set tv/mpaa rating (see -rDNS-help)
Deleting tags
Set the value to "": --artist "" --stik "" --bpm ""
To delete (all) artwork: --artwork REMOVE_ALL
manually removal: --manualAtomRemove "moov.udta.meta.ilst.ATOM"
More detailed iTunes help is available with AtomicParsley --longhelp
Setting reverse DNS forms for iTunes files: see --reverseDNS-help
Setting 3gp assets into 3GPP & derivative files: see --3gp-help
Setting copyright notices for all files: see --ISO-help
For file-level options & padding info: see --file-help
Setting custom private tag extensions: see --uuid-help
Setting ID3 tags onto mpeg-4 files: see --ID3-help
----------------------------------------------------------------------
AtomicParsley version: 20221229.172126.0 d813aa6e0304ed3ab6d92f1ae96cd52b586181ec (utf8)
Submit bug fixes to https://github.com/wez/atomicparsley
```
### `--longhelp`
```text
AtomicParsley help page for setting iTunes-style metadata into MPEG-4 files.
(3gp help available with AtomicParsley --3gp-help)
(ISO copyright help available with AtomicParsley --ISO-help)
(reverse DNS form help available with AtomicParsley --reverseDNS-help)
Usage: AtomicParsley [mp4FILE]... [OPTION]... [ARGUMENT]... [ [OPTION2]...[ARGUMENT2]...]
example: AtomicParsley /path/to.mp4 -e ~/Desktop/pix
example: AtomicParsley /path/to.mp4 --podcastURL "http://www.url.net" --tracknum 45/356
example: AtomicParsley /path/to.mp4 --copyright "℗ © 2006"
example: AtomicParsley /path/to.mp4 --year "2006-07-27T14:00:43Z" --purchaseDate timestamp
example: AtomicParsley /path/to.mp4 --sortOrder artist "Mighty Dub Cats, The
------------------------------------------------------------------------------------------------
Extract any pictures in user data "covr" atoms to separate files.
--extractPix , -E Extract to same folder (basename derived from file).
--extractPixToPath , -e (/path/basename) Extract to specific path (numbers added to basename).
example: --e ~/Desktop/SomeText
gives: SomeText_artwork_1.jpg SomeText_artwork_2.png
Note: extension comes from embedded image file format
------------------------------------------------------------------------------------------------
Tag setting options:
--artist , -a (str) Set the artist tag: "moov.udta.meta.ilst.©ART.data"
--title , -s (str) Set the title tag: "moov.udta.meta.ilst.©nam.data"
--album , -b (str) Set the album tag: "moov.udta.meta.ilst.©alb.data"
--genre , -g (str) Set the genre tag: "©gen" (custom) or "gnre" (standard).
see the standard list with "AtomicParsley --genre-list"
--tracknum , -k (num)[/tot] Set the track number (or track number & total tracks).
--disk , -d (num)[/tot] Set the disk number (or disk number & total disks).
--comment , -c (str) Set the comment tag: "moov.udta.meta.ilst.©cmt.data"
--year , -y (num|UTC) Set the year tag: "moov.udta.meta.ilst.©day.data"
set with UTC "2006-09-11T09:00:00Z" for Release Date
--lyrics , -l (str) Set the lyrics tag: "moov.udta.meta.ilst.©lyr.data"
--lyricsFile , (/path) Set the lyrics tag to the content of a file
--composer , -w (str) Set the composer tag: "moov.udta.meta.ilst.©wrt.data"
--copyright , -x (str) Set the copyright tag: "moov.udta.meta.ilst.cprt.data"
--grouping , -G (str) Set the grouping tag: "moov.udta.meta.ilst.©grp.data"
--artwork , -A (/path) Set a piece of artwork (jpeg or png) on "covr.data"
Note: multiple pieces are allowed with more --artwork args
--bpm , -B (num) Set the tempo/bpm tag: "moov.udta.meta.ilst.tmpo.data"
--albumArtist , -A (str) Set the album artist tag: "moov.udta.meta.ilst.aART.data"
--compilation , -C (bool) Sets the "cpil" atom (true or false to delete the atom)
--hdvideo , -V (bool) Sets the "hdvd" atom (true or false to delete the atom)
--advisory , -y (1of3) Sets the iTunes lyrics advisory ('remove', 'clean', 'explicit')
--stik , -S (1of7) Sets the iTunes "stik" atom (--stik "remove" to delete)
"Movie", "Normal", "TV Show" .... others:
see the full list with "AtomicParsley --stik-list"
or set in an integer value with --stik value=(num)
Note: --stik Audiobook will change file extension to '.m4b'
--description , -p (str) Sets the description on the "desc" atom
--Rating , (str) Sets the Rating on the "rate" atom
--longdesc , -j (str) Sets the long description on the "ldes" atom
--storedesc , (str) Sets the iTunes store description on the "sdes" atom
--TVNetwork , -n (str) Sets the TV Network name on the "tvnn" atom
--TVShowName , -H (str) Sets the TV Show name on the "tvsh" atom
--TVEpisode , -I (str) Sets the TV Episode on "tven":"209", but it is a string: "209 Part 1"
--TVSeasonNum , -U (num) Sets the TV Season number on the "tvsn" atom
--TVEpisodeNum , -N (num) Sets the TV Episode number on the "tves" atom
--podcastFlag , -f (bool) Sets the podcast flag (values are "true" or "false")
--category , -q (str) Sets the podcast category; typically a duplicate of its genre
--keyword , -K (str) Sets the podcast keyword; invisible to MacOSX Spotlight
--podcastURL , -L (URL) Set the podcast feed URL on the "purl" atom
--podcastGUID , -J (URL) Set the episode's URL tag on the "egid" atom
--purchaseDate , -D (UTC) Set Universal Coordinated Time of purchase on a "purd" atom
(use "timestamp" to set UTC to now; can be akin to id3v2 TDTG tag)
--encodingTool , (str) Set the name of the encoder on the "©too" atom
--encodedBy , (str) Set the name of the Person/company who encoded the file on the "©enc" atom
--apID , -Y (str) Set the name of the Account Name on the "apID" atom
--cnID , (num) Set iTunes Catalog ID, used for combining SD and HD encodes in iTunes on the "cnID" atom
To combine you must set "hdvd" atom on one file and must have same "stik" on both file
Must not use "stik" of value Home Video(0), use Movie(9)
iTunes Catalog numbers can be obtained by finding the item in the iTunes Store. Once item
is found in the iTunes Store right click on picture of item and select copy link. Paste this link
into a document or web browser to display the catalog number ID.
An example link for the video Street Kings is:
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewMovie?id=278743714&s=143441
Here you can see the cnID is 278743714
Alternatively you can use iMDB numbers, however these will not match the iTunes catalog.
--geID , (num) Set iTunes Genre ID. This does not necessarily have to match genre.
See --genre-movie-id-list and --genre-tv-id-list
--xID , (str) Set iTunes vendor-supplied xID, used to allow iTunes LPs and iTunes Extras to interact
with other content in your iTunes Library
--gapless , (bool) Sets the gapless playback flag for a track in a gapless album
--sortOrder (type) (str) Sets the sort order string for that type of tag.
(available types are: "name", "artist", "albumartist",
"album", "composer", "show")
NOTE: Except for artwork, only 1 of each tag is allowed; artwork allows multiple pieces.
NOTE: Tags that carry text(str) have a limit of 255 utf8 characters;
however lyrics and long descriptions have no limit.
------------------------------------------------------------------------------------------------
To delete a single atom, set the tag to null (except artwork):
--artist "" --lyrics ""
--artwork REMOVE_ALL
--metaEnema , -P Douches away every atom under "moov.udta.meta.ilst"
--foobar2000Enema , -2 Eliminates foobar2000's non-compliant so-out-o-spec tagging scheme
--manualAtomRemove "some.atom.path" where some.atom.path can be:
keys to using manualAtomRemove:
ilst.ATOM.data or ilst.ATOM target an iTunes-style metadata tag
ATOM:lang=foo target an atom with this language setting; like 3gp assets
ATOM.----.name:[foo] target a reverseDNS metadata tag; like iTunNORM
Note: these atoms show up with 'AP -t' as: Atom "----" [foo]
'foo' is actually carried on the 'name' atom
ATOM[x] target an atom with an index other than 1; like trak[2]
ATOM.uuid=hex-hex-hex-hex targt a uuid atom with the uuid of hex string representation
examples:
moov.udta.meta.ilst.----.name:[iTunNORM] moov.trak[3].cprt:lang=urd
moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740
------------------------------------------------------------------------------------------------
Environmental Variables (affecting picture placement) (macOS ONLY)
set PIC_OPTIONS in your shell to set these flags; preferences are separated by colons (:)
MaxDimensions=num (default: 0; unlimited); sets maximum pixel dimensions
DPI=num (default: 72); sets dpi
MaxKBytes=num (default: 0; unlimited); maximum kilobytes for file (jpeg only)
AddBothPix=bool (default: false); add original & converted pic (for archival purposes)
AllPixJPEG | AllPixPNG =bool (default: false); force conversion to a specific picture format
SquareUp (include to square images to largest dimension, allows an [ugly] 160x1200->1200x1200)
removeTempPix (include to delete temp pic files created when resizing images after tagging)
ForceHeight=num (must also specify width, below) force image pixel height
ForceWidth=num (must also specify height, above) force image pixel width
Examples: (bash-style)
export PIC_OPTIONS="MaxDimensions=400:DPI=72:MaxKBytes=100:AddBothPix=true:AllPixJPEG=true"
export PIC_OPTIONS="SquareUp:removeTempPix"
export PIC_OPTIONS="ForceHeight=999:ForceWidth=333:removeTempPix"
------------------------------------------------------------------------------------------------
```
```
```

60
atomicparsley/install.ps1 Normal file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env pwsh
#########################
# Install AtomicParsley #
#########################
# Every package should define these variables
$pkg_cmd_name = "AtomicParsley"
$pkg_dst_cmd = "$Env:USERPROFILE\.local\bin\AtomicParsley.exe"
$pkg_dst_bin = "$Env:USERPROFILE\.local\bin"
$pkg_dst = "$pkg_dst_cmd"
$pkg_src_cmd = "$Env:USERPROFILE\.local\opt\AtomicParsley-v$Env:WEBI_VERSION\bin\AtomicParsley.exe"
$pkg_src_bin = "$Env:USERPROFILE\.local\opt\AtomicParsley-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\AtomicParsley-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Checking for (or Installing) MSVC Runtime..."
& "$Env:USERPROFILE\.local\bin\webi-pwsh.ps1" vcruntime
Write-Output "Downloading AtomicParsley from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing AtomicParsley"
# TODO: create package-specific temp directory
# Enter tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\AtomicParsley-v*" -Recurse -ErrorAction Ignore
Remove-Item -Path ".\AtomicParsley.exe" -Recurse -ErrorAction Ignore
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Settle unpacked archive into place
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Move-Item -Path ".\AtomicParsley.exe" -Destination "$pkg_src_bin"
# Exit tmp
Pop-Location
}
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
New-Item "$pkg_dst_bin" -ItemType Directory -Force | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

41
atomicparsley/install.sh Normal file
View File

@@ -0,0 +1,41 @@
#!/bin/sh
set -e
set -u
__init_atomicparsley() {
#########################
# Install AtomicParsley #
#########################
WEBI_SINGLE=true
# Every package should define these 6 variables
pkg_cmd_name="AtomicParsley"
pkg_dst_cmd="$HOME/.local/bin/AtomicParsley"
pkg_dst="$pkg_dst_cmd"
pkg_src_cmd="$HOME/.local/opt/AtomicParsley-v$WEBI_VERSION/bin/AtomicParsley"
pkg_src_dir="$HOME/.local/opt/AtomicParsley-v$WEBI_VERSION"
pkg_src="$pkg_src_cmd"
# pkg_install must be defined by every package
pkg_install() {
# ~/.local/opt/AtomicParsley-v20221229.172126.0/bin
mkdir -p "$(dirname "$pkg_src_cmd")"
# mv ./AtomicParsley ~/.local/opt/AtomicParsley-v20221229.172126.0/bin/AtomicParsley
mv ./"$pkg_cmd_name" "$pkg_src_cmd"
}
pkg_get_current_version() {
# 'AtomicParsley --version' has output in this format:
# AtomicParsley version: 20221229.172126.0 d813aa6e0304ed3ab6d92f1ae96cd52b586181ec (utf8)
# This trims it down to just the version number:
# 20221229.172126.0
AtomicParsley --version 2> /dev/null | head -n 1 | cut -d' ' -f3
}
}
__init_atomicparsley

79
atomicparsley/releases.js Normal file
View File

@@ -0,0 +1,79 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'wez';
var repo = 'atomicparsley';
let targets = {
x86win: {
os: 'windows',
arch: 'x86',
libc: 'msvc',
},
x64win: {
os: 'windows',
arch: 'amd64',
// https://github.com/wez/atomicparsley/issues/6#issuecomment-1364523028
libc: 'msvc',
},
x64mac: {
os: 'macos',
arch: 'amd64',
},
x64lin: {
os: 'linux',
arch: 'amd64',
libc: 'gnu',
},
x64musl: {
os: 'linux',
arch: 'amd64',
libc: 'musl',
},
};
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
for (let rel of all.releases) {
let windows32 = rel.name.includes('WindowsX86.');
if (windows32) {
Object.assign(rel, targets.x86win);
continue;
}
let windows64 = rel.name.includes('Windows.');
if (windows64) {
Object.assign(rel, targets.x64win);
continue;
}
let macos64 = rel.name.includes('MacOS');
if (macos64) {
Object.assign(rel, targets.x64mac);
continue;
}
let musl64 = rel.name.includes('Alpine');
if (musl64) {
Object.assign(rel, targets.x64musl);
continue;
}
let lin64 = rel.name.includes('Linux.');
if (lin64) {
Object.assign(rel, targets.x64lin);
continue;
}
}
all._names = ['AtomicParsley', 'atomicparsley'];
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
console.info(JSON.stringify(all));
//console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -7,6 +7,17 @@ tagline: |
To update or switch versions, run `webi awless@stable` (or `@v2`, `@beta`, etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/awless
~/.aws/credentials
```
## Cheat Sheet
awless is modeled after popular command-line tools such as Git. Most commands

View File

@@ -16,22 +16,22 @@ $pkg_src_bin = "$Env:USERPROFILE\.local\opt\awless-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\awless-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | out-null
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
echo "Downloading awless from $Env:WEBI_PKG_URL to $pkg_download"
Write-Output "Downloading awless from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& move "$pkg_download.part" "$pkg_download"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
echo "Installing awless"
Write-Output "Installing awless"
# TODO: create package-specific temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\awless-v*" -Recurse -ErrorAction Ignore
@@ -39,19 +39,19 @@ IF (!(Test-Path -Path "$pkg_src_cmd")) {
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
echo "Unpacking $pkg_download"
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Settle unpacked archive into place
echo "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | out-null
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Move-Item -Path ".\awless.exe" -Destination "$pkg_src_bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | out-null
New-Item "$pkg_dst_bin" -ItemType Directory -Force | out-null
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
New-Item "$pkg_dst_bin" -ItemType Directory -Force | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

View File

@@ -7,23 +7,45 @@ tagline: |
To update or switch versions, run `webi bat@stable` (or `@v0.18`, `@beta`, etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.config/bat/config
~/.local/opt/bat/
```
**Windows Users**
```text
\Windows\System32\vcruntime140.dll
```
This will also attempt to install the
[Microsoft Visual C++ Redistributable](/vcruntime) via `webi vcruntime`. If it
fails and you get the error _`vcruntime140.dll` was not found_, you'll need to
[install it manually](https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170).
## Cheat Sheet
> `bat` is pretty much what `cat` would be if it were developed today's in the
> world of Markdown, git, etc.
### How to run on Windows
On Windows you'll get an error like this:
> execution cannot proceed run because vcruntime140.dll was not found
You need to download and install the
[Microsoft Visual C++ Redistributable](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)
### How to alias as `cat`
Update your `.bashrc`, `.zshrc`, or `.profile`
Use [aliasman](/aliasman):
```sh
aliasman cat 'bat --style=plain'
alias cat='bat --style=plain'
```
Or place this in `~/.config/envman/alias.env` and manually update your
`.bashrc`, `.zshrc`, `.profile`, and/or `~/.config/fish/config.fish` to source
it.
```sh
alias cat="bat --style=plain"
@@ -54,7 +76,7 @@ Edit the config file:
`~/.config/bat/config`:
```txt
```text
# no numbers or headers, just highlighting and such
--style="plain"
```

View File

@@ -3,41 +3,45 @@
$VERNAME = "$Env:PKG_NAME-v$Env:WEBI_VERSION.exe"
$EXENAME = "$Env:PKG_NAME.exe"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"))
{
echo "Downloading $Env:PKG_NAME from $Env:WEBI_PKG_URL to $Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading $Env:PKG_NAME from $Env:WEBI_PKG_URL to $Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part"
& move "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part" "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
& Move-Item "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part" "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
}
IF (!(Test-Path -Path "$Env:USERPROFILE\.local\xbin\$VERNAME"))
{
echo "Installing $Env:PKG_NAME"
# Fetch MSVC Runtime
Write-Output "Checking for MSVC Runtime..."
IF (-not (Test-Path "\Windows\System32\vcruntime140.dll")) {
& "$Env:USERPROFILE\.local\bin\webi-pwsh.ps1" vcruntime
}
IF (!(Test-Path -Path "$Env:USERPROFILE\.local\bin\$VERNAME")) {
Write-Output "Installing $Env:PKG_NAME"
# TODO: temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path "$Env:PKG_NAME-v*" -Recurse -ErrorAction Ignore
# Remove any leftover tmp cruft
Remove-Item -Path "$Env:PKG_NAME-v*" -Recurse -ErrorAction Ignore
# Unpack archive
# Windows BSD-tar handles zip. Imagine that.
echo "Unpacking $Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
& tar xf "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Move single binary into root of temporary folder
& move "bat-*\$EXENAME" "$VERNAME"
# Unpack archive
# Windows BSD-tar handles zip. Imagine that.
Write-Output "Unpacking $Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
& tar xf "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Move single binary into root of temporary folder
& Move-Item "bat-*\$EXENAME" "$VERNAME"
# Settle unpacked archive into place
echo "New Name: $VERNAME"
echo "New Location: $Env:USERPROFILE\.local\xbin\$VERNAME"
Move-Item -Path "$VERNAME" -Destination "$Env:USERPROFILE\.local\xbin"
# Settle unpacked archive into place
Write-Output "New Name: $VERNAME"
Write-Output "New Location: $Env:USERPROFILE\.local\bin\$VERNAME"
Move-Item -Path "$VERNAME" -Destination "$Env:USERPROFILE\.local\bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$Env:USERPROFILE\.local\bin\$EXENAME' from '$Env:USERPROFILE\.local\xbin\$VERNAME'"
Write-Output "Copying into '$Env:USERPROFILE\.local\bin\$EXENAME' from '$Env:USERPROFILE\.local\bin\$VERNAME'"
Remove-Item -Path "$Env:USERPROFILE\.local\bin\$EXENAME" -Recurse -ErrorAction Ignore
Copy-Item -Path "$Env:USERPROFILE\.local\xbin\$VERNAME" -Destination "$Env:USERPROFILE\.local\bin\$EXENAME" -Recurse
Copy-Item -Path "$Env:USERPROFILE\.local\bin\$VERNAME" -Destination "$Env:USERPROFILE\.local\bin\$EXENAME" -Recurse

View File

@@ -19,7 +19,7 @@ __init_bat() {
}
pkg_install() {
# ~/.local/xbin
# ~/.local/bin
mkdir -p "$pkg_src_bin"
# mv ./bat-*/bat ~/.local/opt/bat-v0.15.4/bin/bat
@@ -27,6 +27,11 @@ __init_bat() {
# chmod a+x ~/.local/opt/bat-v0.15.4/bin/bat
chmod a+x "$pkg_src_cmd"
if ! [ -e ~/.config/bat/config ]; then
mkdir -p ~/.config/bat/
touch ~/.config/bat/config
fi
}
}

83
beyond-shell/README.md Normal file
View File

@@ -0,0 +1,83 @@
---
title: beyond-shell
homepage: https://webinstall.dev/beyond-shell
tagline: |
meta package for Beyond Code workshops
---
To update, run the relevant installers individually. For example:
`webi node@lts`.
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/bin
~/.config/envman/alias.env
~/.config/envman/PATH.env
~/.iterm2/
~/.local/bin/
~/.local/opt/
~/.local/share/font/ (or ~/Library/Fonts)
~/.vim/pack/plugins/start/
~/.vim/plugins/
~/.vimrc
```
## Cheat Sheet
> Installs the format and linter tools and code you'll need for the Beyond Code
> Shell Scripting Workshop
This meta package will install the full set of plugins and settings we
recommended.
## Prerequisites for Windows
- https://webinstall.dev/wsl
- https://webinstall.dev/nerdfont
## Post Install for macOS
- https://webinstall.dev/iterm2-themes
- https://webinstall.dev/nerdfont
## What's installed?
- Formatters & Linters
- [`prettier`](/prettier)
- [`shellcheck`](/shellcheck)
- [`shfmt`](/shfmt)
- Vim Plugins & Config
- [vim-leader](/vim-leader)
- [vim-shell](/vim-shell)
- [vim-sensible](/vim-sensible)
- [vim-viminfo](/vim-viminfo)
- [vim-lastplace](/vim-lastplace)
- [vim-smartcase](/vim-smartcase)
- [vim-spell](/vim-spell)
- [vim-ale](/vim-ale)
- [vim-prettier](/vim-prettier)
- [vim-shfmt](/vim-shfmt)
- [vim-whitespace](/vim-whitespace)
- Commandline Tools
- [`aliasman`](/aliasman)
- [`bat`](/bat)
- [`curlie`](/curlie)
- [`jq`](/jq)
- [`pathman`](/pathman)
- [`ssh-pubkey`](/ssh-pubkey)
- [`webi`](/webi)
- Aliases
- `cat='bat --style=plain --pager=none'`
- `curl=curlie`
- `setalias='aliasman'`
- System
- [iterm2](/iterm2)
- [fish](/fish)
- [NerdFont](/nerdfont)
- git
- vim
- zip

415
beyond-shell/install.sh Normal file
View File

@@ -0,0 +1,415 @@
#!/bin/sh
set -e
set -u
webi_cmd="$HOME/.local/bin/webi"
aliasman_cmd="$HOME/.local/bin/aliasman"
pathman_cmd="$HOME/.local/bin/pathman"
my_date="$(
date -u '+%F_%H.%M.%S'
)"
my_log="${HOME}/.local/share/beyond-code/var/${my_date}.log"
fn_node_version() { (
my_node="${1:-}"
if test -n "${my_node}"; then
"${my_node}" --version | sed 's/v//'
fi
); }
fn_node_path() { (
if command -v node; then
return 0
fi
if test ! -e ~/.local/opt/node/bin/node; then
return 0
fi
echo ~/.local/opt/node/bin/node
); }
fn_node_install() { (
node_minimum="18"
my_path_actual="$(
fn_node_path
)"
my_node="$(
fn_node_path
)"
my_node_version="$(
fn_node_version "${my_node}"
)"
if test -n "${my_path_actual}"; then
my_bin_dir="$(
dirname "${my_node}"
)"
my_node_dir="$(
dirname "${my_bin_dir}"
)"
my_lib_dir="${my_node_dir}/lib/node_modules"
if test ! -w "${my_bin_dir}" ||
test ! -w "${my_node_dir}" ||
test ! -w "${my_lib_dir}"; then
printf "\e[31m[Warning]\e[0m \e[32mNode.js\e[0m at %s is not user-writable\n" "${my_path_actual}"
sleep 1
my_path_actual=""
fi
fi
if test -n "${my_path_actual}"; then
my_node_major="$(
echo "${my_node_version}" | cut -d'.' -f1
)"
if test -z "${my_node_major}"; then
my_path_actual=""
elif test "${my_node_major}" -lt "${node_minimum}"; then
printf "\e[31m[Warning]\e[0m \e[32mNode.js v%s\e[0m is too old\n" "${my_node_version}"
sleep 1
my_path_actual=""
fi
fi
if test -z "${my_path_actual}"; then
printf "\e[32m[Fixup]\e[0m Installing \e[32mNode.js\e[0m to \e[34m%s\e[0m\n" '~'/.local/opt/node/
sleep 1
"${webi_cmd}" node@lts >> "${my_log}"
if test -e ~/.local/opt/node/bin/node; then
"${pathman_cmd}" add ~/.local/opt/node/bin/ > /dev/null 2> /dev/null
fi
else
echo "[Info] Found ${my_path_actual}/node (v${my_node_version})"
fi
); }
fn_git_install() { (
if [ "Darwin" = "$(uname -s)" ]; then
needs_xcode="$(
/usr/bin/xcode-select -p > /dev/null 2> /dev/null || echo "true"
)"
if [ -n "${needs_xcode}" ]; then
echo ""
echo ""
printf "\e[31m[Error]\e[0m: Run this command to install XCode Command Line Tools first:\n"
echo ""
echo " xcode-select --install"
echo ""
echo "After the install, close this terminal, open a new one, and try again."
echo ""
exit 1
else
my_git="$(command -v git)"
echo "[Info] Found ${my_git}"
fi
return 0
fi
if ! command -v git > /dev/null; then
fn_apt_get git git
return 0
fi
my_git="$(command -v git)"
echo "[Info] Found ${my_git}"
); }
fn_apt_get() { (
my_cmd="${1:-}"
my_pkg="${2:-}"
printf "\e[31m[Warning]\e[0m \e[32m%s\e[0m not found\n" "${my_cmd}"
printf "\e[32m[Fixup]\e[0m install \e[34m%s\e[0m (may require password)\n" "${my_pkg}"
sleep 1
echo "sudo apt-get install ${my_pkg}"
# shellcheck disable=SC2030
export DEBIAN_FRONTEND=noninteractive
if ! sudo apt-get install -qq -y -o=Dpkg::Use-Pty=0 "${my_pkg}"; then
echo "failed to install ${my_cmd} on"
cat /etc/issue
fi
); }
fn_vim_install() { (
if command -v vim > /dev/null; then
my_vim="$(command -v vim)"
echo "[Info] Found ${my_vim}"
return 0
fi
fn_apt_get vim vim
); }
fn_zip_install() { (
if command -v zip > /dev/null; then
my_zip="$(command -v zip)"
echo "[Info] Found ${my_zip}"
return 0
fi
fn_apt_get zip zip
); }
fn_fish_install() { (
if ! command -v fish > /dev/null; then
if [ "Darwin" = "$(uname -s)" ]; then
printf "\e[31m[Warning]\e[0m \e[32mfish\e[0m not found\n"
printf "\e[32m[Fixup]\e[0m install \e[34mfish\e[0m\n"
sleep 1
"${webi_cmd}" fish > /dev/null
else
fn_apt_get fish fish
fi
fi
if ! test -f ~/.config/fish/config.fish; then
printf "\e[32m[Fixup]\e[0m create \e[34m%s\e[0m\n" '~'/.config/fish/config.fish
mkdir -p ~/.config/fish/
touch ~/.config/fish/config.fish
chmod 0600 ~/.config/fish/config.fish
fi
); }
fn_touch() { (
my_file_path="${1:-}"
my_file_name="${2:-}"
if test -f "${my_file_path}"; then
echo "[Info] Found ${my_file_name}"
return 0
fi
printf "\e[32m[Fixup]\e[0m create \e[34m%s\e[0m\n" "${my_file_name}"
my_base_path="$(
basename "${my_file_path}"
)"
if test ! -e "${my_base_path}"; then
mkdir -p "${my_base_path}"
chmod 0700 "${my_base_path}"
fi
touch "${my_file_path}"
chmod 0600 "${my_file_path}"
); }
fn_mkdir() { (
my_dir_name="${1:-}"
my_dir_path="${2:-}"
if test -d "${my_dir_path}"; then
echo "[Info] Found ${my_dir_name}"
return 0
fi
printf "\e[32m[Fixup]\e[0m create \e[34m%s\e[0m\n" "${my_dir_name}"
mkdir -p "${my_dir_path}"
chmod 0700 "${my_dir_path}"
); }
fn_path_bin() { (
if test -d ~/bin/; then
echo "[Info] Found ~/bin/"
return 0
fi
printf "\e[32m[Fixup]\e[0m create \e[34m%s\e[0m\n" '~'/bin/
mkdir -p ~/bin/
"${pathman_cmd}" add ~/bin/ > /dev/null 2> /dev/null
); }
fn_webi_bin() { (
my_cmd="$(
echo "${1:-}" | sed 's/@.*//'
)"
if test -e ~/.local/bin/"${my_cmd}"; then
echo "[Info] Found ~/.local/bin/${my_cmd}"
return 0
fi
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m to \e[34m%s\e[0m\n" "${my_cmd}" '~'"/.local/bin/${my_cmd}"
"${webi_cmd}" "${my_cmd}" > /dev/null 2> /dev/null
); }
fn_webi_opt() { (
my_cmd="${1:-}"
# shellcheck disable=SC2010
# we do want to use ls with grep
if ls ~/.local/opt/ | grep -q -E "^${my_cmd}(-|\$)"; then
return 0
fi
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m to \e[34m%s/\e[0m\n" "${my_cmd}" '~'"/.local/opt/${my_cmd}"
"${webi_cmd}" "${my_cmd}" > /dev/null
); }
fn_webi_vim_config() { (
my_cmd="${1:-}"
if test -e ~/.vim/plugins/"${my_cmd}.vim"; then
echo "[Info] Found ~/.vim/plugins/${my_cmd}.vim"
return 0
fi
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m to \e[34m%s.vim\e[0m\n" "vim-${my_cmd}" '~'"/.vim/plugins/${my_cmd}.vim"
"${webi_cmd}" "vim-${my_cmd}" > /dev/null
); }
fn_webi_vim_plugin_ale() { (
my_cmd="ale"
if test -e ~/.vim/pack/plugins/start/"${my_cmd}"; then
echo "[Info] Found ~/.vim/pack/plugins/start/${my_cmd}"
return 0
fi
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m to \e[34m%s\e[0m\n" "vim-${my_cmd}" '~'"/.vim/pack/plugins/start/${my_cmd}"
printf "\e[32m[Fixup]\e[0m and config at \e[34m%s.vim\e[0m\n" '~'"/.vim/plugins/${my_cmd}"
"${webi_cmd}" "vim-${my_cmd}" > /dev/null 2> /dev/null
); }
fn_webi_vim_plugin() { (
my_cmd="${1:-}"
if test -e ~/.vim/pack/plugins/start/"vim-${my_cmd}"; then
echo "[Info] Found ~/.vim/pack/plugins/start/vim-${my_cmd}"
return 0
fi
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m to \e[34m%s\e[0m\n" "vim-${my_cmd}" '~'"/.vim/pack/plugins/start/vim-${my_cmd}"
printf "\e[32m[Fixup]\e[0m and config at \e[34m%s.vim\e[0m\n" '~'"/.vim/plugins/${my_cmd}"
"${webi_cmd}" "vim-${my_cmd}" > /dev/null 2> /dev/null
); }
fn_webi_vimrc() { (
my_cmd="${1:-}"
if grep -q "${my_cmd}" ~/.vimrc; then
echo "[Info] Found vim-${my_cmd} config in ~/.vimrc"
return 0
fi
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m to \e[34m%s\e[0m\n" "vim-${my_cmd}" '~'/.vimrc
"${webi_cmd}" "vim-${my_cmd}" > /dev/null
); }
fn_setalias() { (
my_name="${1:-}"
my_cmd="${2:-}"
if grep -q "${my_name}" ~/.config/envman/alias.env; then
echo "[Info] Found '${my_name}' alias"
return 0
fi
printf "\e[32m%s\e[0m aliased to '\e[34m%s\e[0m'\n" "${my_name}" "${my_cmd}"
"${aliasman_cmd}" "${my_name}" "${my_cmd}" > /dev/null
); }
__install() {
#printf "\e[31mRED\e[0m\n"
#printf "\e[32mYELLOW\e[0m\n"
#printf "\e[34mBLUE\e[0m\n"
echo ""
printf "\e[34mLog\e[0m will be written to \e[32m%s\e[0m\n" '~'/.local/share/beyond-code/var/
sleep 2
echo ""
mkdir -p ~/.local/share/beyond-code/var/
mkdir -p ~/.local/opt/
{
printf "\e[32mwebi\e[0m at \e[32m%s\e[0m\n" '~'/.local/bin/webi
echo ""
if [ "Darwin" = "$(uname -s)" ]; then
if test -e /Applications/iTerm.app; then
echo "[Info] Found iTerm.app in /Applications/"
else
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m into \e[34m%s\e[0m\n" "iTerm.app" "/Applications/"
"${webi_cmd}" iterm2 > /dev/null
fi
fi
fn_git_install
fn_fish_install
fn_zip_install
if test -e ~/.local/share/fonts/'Droid Sans Mono for Powerline Nerd Font Complete.otf'; then
echo "[Info] Found NerdFont in ~/.local/share/fonts"
elif test -e ~/Library/Fonts/'Droid Sans Mono for Powerline Nerd Font Complete.otf'; then
echo "[Info] Found NerdFont in ~/Library/Fonts"
else
"${webi_cmd}" nerdfont | tail -n 1
fi
if test -e ~/.iterm2/; then
echo "[Info] Found iTerm2 utils ~/.iterm2/"
else
printf "\e[32m[Fixup]\e[0m Installing \e[32m%s\e[0m into \e[34m%s\e[0m\n" "iterm2-utils" '~'"/.iterm2/"
"${webi_cmd}" iterm2-utils > /dev/null
fi
echo ""
echo "[Info] PATH updates in ~/.config/envman/PATH.env"
fn_path_bin
fn_touch ~/.config/envman/alias.env '~'/.config/envman/alias.env
fn_touch ~/.config/envman/PATH.env '~'/.config/envman/PATH.env
echo ""
fn_node_install
for my_cmd in aliasman bat curlie jq pathman ssh-pubkey; do
fn_webi_bin "${my_cmd}"
done
echo ""
echo "[Info] Aliases in ~/.config/envman/alias.env"
fn_setalias setalias 'aliasman'
fn_setalias cat 'bat --style=plain --pager=none'
fn_setalias curl 'curlie'
# vim config and plugins
echo ""
fn_vim_install
fn_touch ~/.vimrc '~'/.vimrc
for my_plugin in leader shell; do
fn_webi_vimrc "${my_plugin}"
done
fn_webi_vim_plugin sensible
for my_plugin in viminfo smartcase spell whitespace; do
fn_webi_vim_config "${my_plugin}"
done
fn_webi_vim_plugin_ale
for my_plugin in commentary lastplace shfmt prettier; do
fn_webi_vim_plugin "${my_plugin}"
done
for my_cmd in shfmt@3.5 shellcheck; do
fn_webi_bin "${my_cmd}"
done
} | tee -a "${my_log}"
echo ""
printf "\e[34mLog\e[0m written to \e[32m%s\e[0m\n" "${my_log}"
echo ""
}
__install

View File

@@ -5,13 +5,24 @@ tagline: |
Brew: The Missing Package Manager for macOS (and Linux).
---
## Updating `brew`
`brew` has its own built-in upgrade management.
To update or switch versions, use brew's built-in upgrade management, or our
handy update service script:
```sh
brew update
brew upgrade
brew-update-service-install # Webi helper script
```
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/opt/brew/
~/.local/bin/brew-update-service-install
```
## Cheat Sheet

View File

@@ -0,0 +1,48 @@
#!/bin/sh
set -e
set -u
main() { (
sed '1,/^#~\/.local\/bin\/brew-updater/d' "${0}" > ~/.local/bin/brew-update-hourly
chmod a+x ~/.local/bin/brew-update-hourly
env PATH="$PATH" serviceman add --user \
--workdir ~/.local/opt/brew/ \
--name sh.brew.updater -- \
~/.local/bin/brew-update-hourly
); }
if main; then
exit 0
fi
#~/.local/bin/brew-updater
#!/bin/sh
#set -e
set -u
if test -e ~/.config/envman/PATH.env; then
# shellcheck disable=SC1090
. ~/.config/envman/PATH.env
fi
while true; do
my_start="$(date '+%s')"
my_date="$(date '+%F %T')"
echo "[$my_date] Updating brew..."
brew update
echo ''
my_end="$(date '+%s')"
my_elapsed="$((my_end - my_start))"
my_date="$(date '+%F %T')"
echo "[$my_date] Updated in ${my_elapsed}s."
echo "[$my_date] Cleaning up..."
brew cleanup
echo "[$my_date] Waiting 24 hours..."
my_wait="$((24 * 60 * 60))"
sleep "$my_wait"
done

View File

@@ -7,9 +7,9 @@ _install_brew() {
# Straight from https://brew.sh
#/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
if uname -a | grep -q -i darwin; then
if test "Darwin" = "$(uname -s)"; then
needs_xcode="$(/usr/bin/xcode-select -p > /dev/null 2> /dev/null || echo "true")"
if [ -n "${needs_xcode}" ]; then
if test -n "${needs_xcode}"; then
echo ""
echo ""
echo "ERROR: Run this command to install XCode Command Line Tools first:"
@@ -20,11 +20,11 @@ _install_brew() {
echo ""
fi
else
if [ -z "$(command -v gcc)" ]; then
if ! command -v gcc > /dev/null; then
echo >&2 "Warning: to install 'gcc' et al on Linux use the built-in package manager."
echo >&2 " For example, try: sudo apt install -y build-essential"
fi
if [ -z "$(command -v git)" ]; then
if ! command -v git > /dev/null; then
echo >&2 "Error: to install 'git' on Linux use the built-in package manager."
echo >&2 " For example, try: sudo apt install -y git"
exit 1
@@ -32,7 +32,7 @@ _install_brew() {
fi
# From Straight from https://brew.sh
if ! [ -d "$HOME/.local/opt/brew" ]; then
if ! test -d "$HOME/.local/opt/brew"; then
echo "Installing to '$HOME/.local/opt/brew'"
echo ""
echo "If you prefer to have brew installed to '/usr/local' cancel now and do the following:"
@@ -44,6 +44,13 @@ _install_brew() {
git clone --depth=1 https://github.com/Homebrew/brew "$HOME/.local/opt/brew"
fi
rm -rf "$HOME/.local/bin/brew-update-service-install"
webi_download \
"$WEBI_HOST/packages/brew/brew-update-service-install" \
"$HOME/.local/bin/brew-update-service-install" \
brew-update-service-install
chmod a+x "$HOME/.local/bin/brew-update-service-install"
webi_path_add "$HOME/.local/opt/brew/bin"
export PATH="$HOME/.local/opt/brew/bin:$PATH"
@@ -60,6 +67,10 @@ _install_brew() {
# shellcheck disable=2016
echo ' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"'
echo ""
echo "To register 'brew update' as a hourly system service:"
echo " brew-update-service-install"
echo ""
}
_install_brew

160
bun/README.md Normal file
View File

@@ -0,0 +1,160 @@
---
title: Bun
homepage: https://bun.sh
tagline: |
Bun is a fast all-in-one JavaScript runtime
---
To update or switch versions, run `webi bun@<tag>`. \
(you can use `@beta` for pre-releases, or `@x.y.z` for a specific version)
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/opt/bun/
~/.local/opt/bun-<VERSION>/
```
## Cheat Sheet
> Bun is a wicked-fast JavaScript runtime for developer tooling, API servers,
> and edge computing.
>
> It's built in Zig and provides a more curated, "batteries-included" approach
> to developing with JavaScript and JavaScript-ish languages - such as
> TypeScript, JSX, and TSX.
Run some <strong><em>x</em></strong>Script:
```sh
bun run ./hello.js
bun run ./hello.jsx
bun run ./hello.ts
bun run ./hello.tsx
```
Run a package from `npm`:
```sh
bun x jswt
```
More goodies:
```
bun help
```
(there's also a built-in **development server** and lots of stuff)
### bun<span>.</span>sh/install vs webi
Bun has an official installer:
```sh
export BUN_INSTALL="$HOME/.bun"
curl -fsSL https://bun.sh/install | bash
```
You might want to still use webi if you want to be able to switch between
versions.
### How to install command line completions
```sh
bun completions
```
### How to create a bun executable
1. Create your script
```sh
vim ./hello
```
```js
#!/usr/bin/env bun
console.log('hello');
```
2. Make it executable
```sh
chmod a+x ./hello
```
3. Run it
```sh
./hello
```
### How to publish bun packages
At the time of this writing (bun v0.5.1), you'll still need to publish with
`npm`.
`npm` is installed with [node](/node).
See
[Getting Started with NPM (as a developer)](https://gist.github.com/coolaj86/1318304).
### How to install bun packages
You can run it with `bun x`:
```sh
bun x <whatever>
```
Or you can put the `#!/usr/bin/env bun` shebang before publishing, and install
from npm:
```sh
bun install -g <whatever>
<whatever>
```
### How to run a Bun program as a service
As a system service on Linux:
(**note**: swap 'my-project' and './my-project' for the name of your project and
file)
1. Install serviceman (compatible with systemd)
```sh
webi serviceman
source ~/.config/envman/PATH.env
```
2. Go into your program's directory
```sh
pushd ./my-project/
```
3. Add your project to the system launcher, running as the current user
```sh
sudo env PATH="$PATH" \
serviceman add --path="$PATH" --system \
--username "$(whoami)" --name my-project -- \
bun run ./my-project.js
```
4. Restart the logging service
```sh
sudo systemctl restart systemd-journald
```
For **macOS**:
1. Install serviceman (compatible with `launchctl`)
```sh
webi serviceman
source ~/.config/envman/PATH.env
```
2. Go into your program's directory
```sh
pushd ./my-project/
```
3. Add your project to the system launcher, running as the current user
```sh
serviceman add --path="$PATH" --user --name my-project -- \
bun run ./my-project.js
```

39
bun/install.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
set -e
set -u
# NOTE: pkg_* variables can be defined here
# pkg_cmd_name
# pkg_src, pkg_src_bin, pkg_src_cmd
# pkg_dst, pkg_dst_bin, pkg_dst_cmd
#
# Their defaults are defined in _webi/template.sh at https://github.com/webinstall/packages
# Every package should define these 6 variables
pkg_cmd_name="bun"
pkg_dst_cmd="$HOME/.local/opt/bun/bin/bun"
pkg_dst_dir="$HOME/.local/opt/bun"
pkg_dst="$pkg_dst_dir"
pkg_src_cmd="$HOME/.local/opt/bun-v$WEBI_VERSION/bin/bun"
pkg_src_dir="$HOME/.local/opt/bun-v$WEBI_VERSION"
pkg_src="$pkg_src_dir"
pkg_get_current_version() {
# 'bun --version' only outputs the version:
# 0.5.1
# But we future-proof it a little anyway
# 0.5.1
bun --version 2> /dev/null | head -n 1 | cut -d' ' -f1
}
# pkg_install must be defined by every package
pkg_install() {
# ~/.local/opt/bun-v0.5.1/bin
mkdir -p "$(dirname "$pkg_src_cmd")"
# mv ./bun-*/bun ~/.local/opt/bun-v0.5.1/bin/bun
mv ./bun-*/bun* "$pkg_src_cmd"
}

39
bun/releases.js Normal file
View File

@@ -0,0 +1,39 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'oven-sh';
var repo = 'bun';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
all.releases = all.releases
.filter(function (r) {
let isDebug = r.name.includes('-profile');
if (isDebug) {
return false;
}
let isAncient = r.name.includes('-baseline');
if (isAncient) {
return false;
}
return true;
})
.map(function (r) {
// bun-v0.5.1 => v0.5.1
r.version = r.version.replace(/bun-/g, '');
return r;
});
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
// just select the first 5 for demonstration
all.releases = all.releases.slice(0, 5);
console.info(JSON.stringify(all, null, 2));
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,22 +16,22 @@ $pkg_src_bin = "$Env:USERPROFILE\.local\opt\caddy-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\caddy-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | out-null
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
echo "Downloading caddy from $Env:WEBI_PKG_URL to $pkg_download"
Write-Output "Downloading caddy from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& move "$pkg_download.part" "$pkg_download"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
echo "Installing caddy"
Write-Output "Installing caddy"
# TODO: create package-specific temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\caddy-v*" -Recurse -ErrorAction Ignore
@@ -39,19 +39,19 @@ IF (!(Test-Path -Path "$pkg_src_cmd")) {
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
echo "Unpacking $pkg_download"
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Settle unpacked archive into place
echo "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | out-null
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Move-Item -Path ".\caddy.exe" -Destination "$pkg_src_bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | out-null
New-Item "$pkg_dst_bin" -ItemType Directory -Force | out-null
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
New-Item "$pkg_dst_bin" -ItemType Directory -Force | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

View File

@@ -8,7 +8,12 @@ module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
// remove checksums and .deb
all.releases = all.releases.filter(function (rel) {
return !/(\.txt)|(\.deb)$/i.test(rel.name);
let isOneOffAsset = rel.download.includes('buildable-artifact');
if (isOneOffAsset) {
return false;
}
return true;
});
return all;
});

View File

@@ -33,7 +33,7 @@ sudo apt --fix-broken install -y
You may get an error like this:
```txt
```text
chromedriver: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory
```

View File

@@ -15,46 +15,44 @@ $pkg_src_bin = "$Env:USERPROFILE\.local\opt\chromedriver-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\chromedriver-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | out-null
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"))
{
echo "Downloading chromedriver from $Env:WEBI_PKG_URL to $pkg_download"
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading chromedriver from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& move "$pkg_download.part" "$pkg_download"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd"))
{
echo "Installing chromedriver"
IF (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing chromedriver"
# TODO: create package-specific temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\chromedriver.exe" -Recurse -ErrorAction Ignore
# Remove any leftover tmp cruft
Remove-Item -Path ".\chromedriver.exe" -Recurse -ErrorAction Ignore
# NOTE: DELETE THIS COMMENT IF NOT USED
# Move single binary into root of temporary folder
#& move "$pkg_download" "chromedriver.exe"
# NOTE: DELETE THIS COMMENT IF NOT USED
# Move single binary into root of temporary folder
#& move "$pkg_download" "chromedriver.exe"
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
echo "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Settle unpacked archive into place
echo "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | out-null
Move-Item -Path ".\chromedriver-*\chromedriver.exe" -Destination "$pkg_src_bin"
# Settle unpacked archive into place
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Move-Item -Path ".\chromedriver-*\chromedriver.exe" -Destination "$pkg_src_bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | out-null
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

View File

@@ -20,20 +20,28 @@ __init_chromedriver() {
# pkg_install must be defined by every package
pkg_install() {
# ~/.local/opt/chromedriver-v88.0.4324.96/bin
# ~/.local/opt/chromedriver-v121.0.6130.0/bin
mkdir -p "$(dirname "$pkg_src_cmd")"
# mv ./chromedriver-*/chromedriver ~/.local/opt/chromedriver-v88.0.4324.96/bin/chromedriver
mv ./chromedriver* "$pkg_src_cmd"
# mv ./chromedriver-macos-arm64/chromedriver \
# ~/.local/opt/chromedriver-v121.0.6130.0/bin/chromedriver
mv ./chromedriver-*/chromedriver "$pkg_src_cmd"
echo ""
echo " $(t_warn 'MANUAL STEPS TO FINISH:') you may need to install libnss3:"
echo " $(t_cmd 'sudo apt install -y') $(t_warn 'libnss3')"
}
# pkg_get_current_version is recommended, but (soon) not required
pkg_get_current_version() {
# 'chromedriver --version' has output in this format:
# ChromeDriver 88.0.4324.96 (68dba2d8a0b149a1d3afac56fa74648032bcf46b-refs/branch-heads/4324@{#1784})
# ChromeDriver 121.0.6130.0 (5fc19c7ab5e88bb674c2efc65db4b7890a52a4ec-refs/branch-heads/6130@{#1})
# This trims it down to just the version number:
# 88.0.4324.96
chromedriver --version 2> /dev/null | head -n 1 | cut -d ' ' -f 2
# 121.0.6130.0
chromedriver --version 2> /dev/null |
head -n 1 |
tr -s ' ' |
cut -d' ' -f2
}
}

View File

@@ -1,81 +1,77 @@
'use strict';
var matchers = {
key: /.*Key>(.*)<\/Key.*/,
generation: /.*Generation>(.*)<\/Generation.*/,
metaGeneration: /.*MetaGeneration>(.*)<\/MetaGeneration.*/,
lastModified: /.*LastModified>(.*)<\/LastModified.*/,
etag: /.*ETag>(.*)<\/ETag.*/,
size: /.*Size>(.*)<\/Size.*/
};
var baseUrl = 'https://chromedriver.storage.googleapis.com';
// See <https://googlechromelabs.github.io/chrome-for-testing/>
var releaseApiUrl =
'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json';
module.exports = function (request) {
var all = {
// {
// "timestamp": "2023-11-15T21:08:56.730Z",
// "versions": [
// {
// "version": "121.0.6120.0",
// "revision": "1222902",
// "downloads": {
// "chrome": [],
// "chromedriver": [
// {
// "platform": "linux64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/121.0.6120.0/linux64/chromedriver-linux64.zip"
// },
// {
// "platform": "mac-arm64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/121.0.6120.0/mac-arm64/chromedriver-mac-arm64.zip"
// },
// {
// "platform": "mac-x64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/121.0.6120.0/mac-x64/chromedriver-mac-x64.zip"
// },
// {
// "platform": "win32",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/121.0.6120.0/win32/chromedriver-win32.zip"
// },
// {
// "platform": "win64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/121.0.6120.0/win64/chromedriver-win64.zip"
// }
// ],
// "chrome-headless-shell": []
// }
// }
// ]
// }
module.exports = async function (request) {
let resp = await request({
url: releaseApiUrl,
json: true,
});
let builds = [];
for (let release of resp.body.versions) {
if (!release.downloads.chromedriver) {
continue;
}
let version = release.version;
for (let asset of release.downloads.chromedriver) {
let build = {
version: version,
download: asset.url,
// I' not sure that this is actually statically built but it
// seems to be and at worst we'll just get bug reports for Apline
libc: 'none',
};
builds.push(build);
}
}
let all = {
download: '',
releases: []
releases: builds,
};
// XML
return request({
url: 'https://chromedriver.storage.googleapis.com/',
json: false
})
.then(function (resp) {
var body = resp.body;
var groups = body.split(/<\/?Contents>/g);
// get rid of leading and trailing junk
groups.shift();
groups.pop();
var metas = groups.map(function (group) {
return {
key: group.replace(matchers.key, '$1'),
//generation: group.replace(matchers.generation, '$1'),
//metaGeneration: group.replace(matchers.metaGeneration, '$1'),
lastModified: group.replace(matchers.lastModified, '$1')
//etag: group.replace(matchers.etag, '$1'),
//size: group.replace(matchers.size, '$1')
};
});
all.download = baseUrl + '/{{ download }}';
metas.forEach(function (asset) {
if (!asset.key.includes('chromedriver')) {
// skip the indexes, images, etc
return null;
}
var osname = asset.key.replace(/.*(win|mac|linux)/, '$1');
var arch;
if (asset.key.includes('linux')) {
osname = 'linux';
} else if (asset.key.includes('mac64')) {
osname = 'macos';
if (asset.key.includes('_m1.')) {
arch = 'arm64';
}
} else if (asset.key.includes('win')) {
osname = 'windows';
arch = 'amd64';
}
all.releases.push({
// 87.0.4280.88/chromedriver_win32.zip => 87.0.4280.88
version: asset.key.replace(/(.*)\/.*/, '$1'),
lts: false,
channel: 'stable',
date: asset.lastModified.replace(/T.*/, '$1'),
os: osname,
arch: arch,
hash: '-', // not sure about including etag as hash yet
download: asset.key
});
});
})
.then(function () {
all.releases.sort(function (a, b) {
return new Date(b.date).valueOf() - new Date(a.date).valueOf();
});
return all;
});
return all;
};
if (module === require.main) {

83
cmake/README.md Normal file
View File

@@ -0,0 +1,83 @@
---
title: CMake
homepage: https://github.com/Kitware/CMake
tagline: |
CMake is a cross-platform, open-source build system generator
---
To update or switch versions, run `webi cmake@stable` (or `@v2`, `@beta`, etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/cmake
~/.local/opt/cmake
```
## Cheat Sheet
> CMake is a cross-platform alternative to autoconf that works on Windows, Mac,
> and Linux
A project structure looks like this:
```text
my-project/
├── build/
├── CMakeLists.txt
├── hello-world*
└── hello-world.cpp
```
And can be built my running `cmake` from the `build` directory:
```sh
pushd ./build/
cmake ../
make
```
### How to create a Hello World with CMake
Lets create a hello world program in C++ and build it with CMake.
1. Create a project directory
```sh
mkdir ./my-project/
pushd ./my-project/
```
2. Create a Hello World C++ file named `hello-world.cpp` `hello-world.cpp`:
```cpp
#include <iostream>
int main(int argc, char** argv) {
std::cout << "Hello World!" << std::endl;
return 0;
}
```
3. Create a `CMakeLists.txt` to compile our code `CMakeLists.txt`:
```cmake
project{hello-world}
cmake_minimum_required(VERSION 3.10)
add_executable(hello-world hello-world.cpp)
```
4. Create a build directory and build the binary
```sh
mkdir ./build/
pushd ./build/
cmake ../
make
```
5. Test the built binary:
```sh
./hello-world
```

56
cmake/install.ps1 Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env pwsh
#################
# Install cmake #
#################
# Every package should define these variables
$pkg_cmd_name = "cmake"
$pkg_dst_cmd = "$Env:USERPROFILE\.local\opt\cmake\bin\cmake.exe"
$pkg_dst_bin = "$Env:USERPROFILE\.local\opt\cmake\bin"
$pkg_dst_dir = "$Env:USERPROFILE\.local\opt\cmake"
$pkg_dst = "$pkg_dst_dir"
$pkg_src_cmd = "$Env:USERPROFILE\.local\opt\cmake-v$Env:WEBI_VERSION\bin\cmake.exe"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\cmake-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_dir"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$pkg_download")) {
Write-Output "Downloading cmake from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_dir")) {
Write-Output "Installing cmake"
# TODO: create package-specific temp directory
# Enter tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path ".\cmake*" -Recurse -ErrorAction Ignore
# Unpack archive file into this temporary directory
# Windows BSD-tar handles zip. Imagine that.
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
# Settle unpacked archive into place
Write-Output "Install Location: $pkg_src_cmd"
Move-Item -Path ".\cmake*" -Destination "$pkg_src_dir"
# Exit tmp
Pop-Location
}
Write-Output "Copying into '$pkg_dst' from '$pkg_src'"
Remove-Item -Path "$pkg_dst" -Recurse -ErrorAction Ignore | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse
webi_path_add "$pkg_dst_bin"
& "$pkg_dst_cmd" --version

51
cmake/install.sh Normal file
View File

@@ -0,0 +1,51 @@
#!/bin/sh
# shellcheck disable=SC2034
# "'pkg_cmd_name' appears unused. Verify it or export it."
__init_cmake() {
set -e
set -u
##################
# Install cmake #
##################
# Every package should define these 6 variables
pkg_cmd_name="cmake"
pkg_dst_cmd="$HOME/.local/opt/cmake/bin/cmake"
pkg_dst_dir="$HOME/.local/opt/cmake"
pkg_dst="$pkg_dst_dir"
pkg_src_cmd="$HOME/.local/opt/cmake-v$WEBI_VERSION/bin/cmake"
pkg_src_dir="$HOME/.local/opt/cmake-v$WEBI_VERSION"
pkg_src="$pkg_src_dir"
# pkg_install must be defined by every package
pkg_install() {
# ~/.local/opt/cmake-v3.27.0/
mkdir -p "$(dirname "${pkg_src_dir}")"
# mv ./cmake-*/ ~/.local/opt/cmake-v3.27.0/
if test -e ./cmake-*/CMake.app/Contents/; then
mv ./cmake-*/CMake.app/Contents/ "${pkg_src_dir}"
else
mv ./cmake-*/ "${pkg_src_dir}"
fi
}
# pkg_get_current_version is recommended, but not required
pkg_get_current_version() {
# 'cmake --version' has output in this format:
# cmake 3.27.0 (rev abcdef0123)
# This trims it down to just the version number:
# 3.27.0
cmake --version 2> /dev/null |
head -n 1 |
cut -d ' ' -f 3
}
}
__init_cmake

53
cmake/releases.js Normal file
View File

@@ -0,0 +1,53 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'Kitware';
var repo = 'CMake';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
for (let rel of all.releases) {
if (rel.version.startsWith('v')) {
rel._version = rel.version.slice(1);
}
{
let linuxRe = /(\b|_)(linux|gnu)(\b|_)/i;
let isLinux = linuxRe.test(rel.download) || linuxRe.test(rel.name);
if (isLinux) {
let muslRe = /(\b|_)(musl|alpine)(\b|_)/i;
let isMusl = muslRe.test(rel.download) || muslRe.test(rel.name);
if (isMusl) {
rel.libc = 'musl';
} else {
rel.libc = 'gnu';
}
continue;
}
}
{
let windowsRe = /(\b|_)(win\d*|windows\d*)(\b|_)/i;
let isWindows =
windowsRe.test(rel.download) || windowsRe.test(rel.name);
if (isWindows) {
rel.libc = 'msvc';
continue;
}
}
}
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
// just select the first 5 for demonstration
all.releases = all.releases.slice(0, 5);
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -8,6 +8,17 @@ tagline: |
To update or switch versions, run `webi comrak@stable` (or `@v0.11`, `@beta`,
etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/comrak
~/.config/comrak/config
```
## Cheat Sheet
> Comrak supports the five extensions to CommonMark defined in the GitHub

View File

@@ -3,37 +3,35 @@
$VERNAME = "$Env:PKG_NAME-v$Env:WEBI_VERSION.exe"
$EXENAME = "$Env:PKG_NAME.exe"
# Fetch archive
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"))
{
echo "Downloading $Env:PKG_NAME from $Env:WEBI_PKG_URL to $Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading $Env:PKG_NAME from $Env:WEBI_PKG_URL to $Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part"
& move "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part" "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
& Move-Item "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part" "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
}
IF (!(Test-Path -Path "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME"))
{
echo "Installing $Env:PKG_NAME"
IF (!(Test-Path -Path "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME")) {
Write-Output "Installing $Env:PKG_NAME"
# TODO: temp directory
# Enter tmp
pushd .local\tmp
Push-Location .local\tmp
# Remove any leftover tmp cruft
Remove-Item -Path "$Env:PKG_NAME-v*" -Recurse -ErrorAction Ignore
# Remove any leftover tmp cruft
Remove-Item -Path "$Env:PKG_NAME-v*" -Recurse -ErrorAction Ignore
# Move single binary into root of temporary folder
& move "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE" "$VERNAME"
# Move single binary into root of temporary folder
& Move-Item "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE" "$VERNAME"
# Settle unpacked archive into place
echo "New Name: $VERNAME"
echo "New Location: $Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME"
New-Item "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin" -ItemType Directory -Force | out-null
Move-Item -Path "$VERNAME" -Destination "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin"
# Settle unpacked archive into place
Write-Output "New Name: $VERNAME"
Write-Output "New Location: $Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME"
New-Item "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin" -ItemType Directory -Force | Out-Null
Move-Item -Path "$VERNAME" -Destination "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin"
# Exit tmp
popd
Pop-Location
}
echo "Copying into '$Env:USERPROFILE\.local\bin\$EXENAME' from '$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME'"
Write-Output "Copying into '$Env:USERPROFILE\.local\bin\$EXENAME' from '$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME'"
Remove-Item -Path "$Env:USERPROFILE\.local\bin\$EXENAME" -Recurse -ErrorAction Ignore
Copy-Item -Path "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION\bin\$VERNAME" -Destination "$Env:USERPROFILE\.local\bin\$EXENAME" -Recurse

View File

@@ -4,8 +4,28 @@ var github = require('../_common/github.js');
var owner = 'kivikakk';
var repo = 'comrak';
var ODDITIES = ['-musleabihf.1-'];
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
let builds = [];
loopBuilds: for (let build of all.releases) {
let isOddity;
for (let oddity of ODDITIES) {
isOddity = build.name.includes(oddity);
if (isOddity) {
break;
}
}
if (isOddity) {
continue;
}
builds.push(build);
}
all.releases = builds;
return all;
});
};

116
crabz/README.md Normal file
View File

@@ -0,0 +1,116 @@
---
title: Crabz
homepage: https://github.com/sstadick/crabz
tagline: |
crabz: multi-threaded gzip (like pigz, but in Rust)
---
To update or switch versions, run `webi crabz@stable` (or `@0.8`, `@beta`, etc).
## Cheat Sheet
> `crabz` brings the power of multi-core compression to gzip and deflate. \
> (and a few other formats + other useful features)
gzip, faster.
```sh
crabz -I ./example.json
crabz -d -I ./example.json.gz
```
```text
Compressing (gzip) with 8 threads at compression level 6.
Decompressing (gzip) with 8 threads available.
```
## Table of Contents
- Files
- Tar
- Other Formats
### Files
These are the files/directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/crabz
```
#### How to Optimize
| Flag | Value | Comments |
| ----------------------------- | ----- | ------------------------------------------ |
| `-l`, `--compression-level` | 1-9 | higher is slower |
| `-p`, `--compression-threads` | 8 | set to the number of available cores |
| | | (but no more than 4 for decompression) |
| `-P`, `--pin-at` | 0 | pin to physical cores, starting at N |
| | | (so 4 threads starting at 0 is 0, 1, 2, 3) |
```sh
crabz -l 9 -p 8 -I ./example.tar
crabz -d -p 4 -I ./example.tar.gz
```
#### How to use with Tar
Tar and then compress:
```sh
tar cv ./example/ | crabz -o ./example.tar.gz
```
Or decompress and then untar:
```sh
crabz -d ./example.tar.gz | tar xv
```
#### How to use with other formats
`crabz` supports most of the _LZ77 with Huffman coding_ compression formats:
| Format | Extension | Notes |
| ---------------- | --------- | ------------------------------------ |
| `gzip` | `.gz` | of GNU fame |
| [`bgzf`][bgzf] | `.gz` | supports random-access decompression |
| [`mgzip`][mgzip] | `.gz` | of python fame |
| `zlib` | `.zz` | of PNG fame, also `.z` |
| [`snap`][snap] | `.sz` | of LevelDB and MongoDB fame |
| `deflate` | `.gz` | the O.G. LZ77 |
```sh
crabz --format mgzip -I ./example.tar
```
```sh
# DO NOT decompress in-place
crabz --format mgzip -d ./example.tar.gz -o ./example.tar
# verify before removing the original
tar tf ./example.tar
```
⚠️ **Warnings**:
- DO NOT deflate in-place with non-standard formats: \
Although `gunzip` will work correctly on files compressed with `mgzip` or
`bgzf`, some combinations (ex: decompressing from `mgzip` with `bgzf`) could
result in corruption!
- `tar xvf` and `gzip -l` may report incorrect information, even though `gunzip`
will work
See also:
- https://dev.to/biellls/compression-clearing-the-confusion-on-zip-gzip-zlib-and-deflate-15g1
(p.s. `zip` isn't in the list because it's a container format like `tar`, not a
zip format)
[snap]: https://github.com/google/snappy/blob/main/format_description.txt
[bgzf]: https://samtools.github.io/hts-specs/SAMv1.pdf
[mgzip]: https://pypi.org/project/mgzip/

48
crabz/install.ps1 Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env pwsh
#################
# Install crabz #
#################
# Every package should define these variables
$pkg_cmd_name = "crabz"
$pkg_dst_cmd = "$Env:USERPROFILE\.local\bin\crabz.exe"
$pkg_dst_bin = "$Env:USERPROFILE\.local\bin"
$pkg_dst = "$pkg_dst_cmd"
$pkg_src_cmd = "$Env:USERPROFILE\.local\opt\crabz-v$Env:WEBI_VERSION\bin\crabz.exe"
$pkg_src_bin = "$Env:USERPROFILE\.local\opt\crabz-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\crabz-v$Env:WEBI_VERSION"
$pkg_src = "$pkg_src_cmd"
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
# Fetch archive
IF (!(Test-Path -Path "$pkg_download")) {
Write-Output "Downloading crabz from $Env:WEBI_PKG_URL to $pkg_download"
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing crabz"
# TODO: create package-specific temp directory
# Enter tmp
Push-Location .local\tmp
# Settle unpacked archive into place
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
Copy-Item -Path "$pkg_download" -Destination "$pkg_src_cmd"
# Exit tmp
Pop-Location
}
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
New-Item "$pkg_dst_bin" -ItemType Directory -Force | Out-Null
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

49
crabz/install.sh Normal file
View File

@@ -0,0 +1,49 @@
#!/bin/sh
# The generic functions - version checks, download, extract, etc - are here:
# - https://github.com/webinstall/packages/branches/master/_webi/template.sh
set -e
set -u
pkg_cmd_name="crabz"
# IMPORTANT: this let's other functions know to expect this to be a single file
WEBI_SINGLE=true
# Every package should define these 6 variables
pkg_cmd_name="crabz"
pkg_dst_cmd="$HOME/.local/bin/crabz"
#pkg_dst="$pkg_dst_cmd"
pkg_src_cmd="$HOME/.local/opt/crabz-v$WEBI_VERSION/bin/crabz"
#pkg_src_dir="$HOME/.local/opt/crabz-v$WEBI_VERSION/bin"
#pkg_src="$pkg_src_cmd"
pkg_get_current_version() {
# 'crabz version' has output in this format:
# crabz git:xxxxxxx
# Since that's not sortable, this prints v0.0.0
# v0.0.0
echo "v0.0.0"
}
pkg_install() {
# $HOME/.local/opt/crabz-v0.3.5/bin
mkdir -p "${pkg_src_bin}"
# mv ./crabz* "$HOME/.local/opt/crabz-v0.3.5/bin/crabz"
mv ./"${pkg_cmd_name}"* "${pkg_src_cmd}"
# chmod a+x "$HOME/.local/opt/crabz-v0.3.5/bin/crabz"
chmod a+x "${pkg_src_cmd}"
}
pkg_link() {
# rm -f "$HOME/.local/bin/crabz"
rm -f "${pkg_dst_cmd}"
# ln -s "$HOME/.local/opt/crabz-v0.3.5/bin/crabz" "$HOME/.local/bin/crabz"
ln -s "${pkg_src_cmd}" "${pkg_dst_cmd}"
}

31
crabz/releases.js Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'sstadick';
var repo = 'crabz';
module.exports = async function (request) {
let all = await github(request, owner, repo);
let releases = [];
for (let rel of all.releases) {
let isSrc = rel.download.includes('-src.');
if (isSrc) {
continue;
}
releases.push(rel);
}
all.releases = releases;
return all;
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
// just select the first 5 for demonstration
all.releases = all.releases.slice(0, 5);
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -8,6 +8,16 @@ tagline: |
To update or switch versions, run `webi curlie@stable` (or `@v1.6`, `@beta`,
etc).
### Files
These are the files / directories that are created and/or modified with this
install:
```text
~/.config/envman/PATH.env
~/.local/bin/curlie
```
## Cheat Sheet
> If you like the interface of HTTPie but miss the features of curl, curlie is
@@ -20,6 +30,17 @@ etc).
**JSON** (`=`) is the default encoding for `key=value` pairs.
### How to alias as `curl`
Use [aliasman](/aliasman):
```sh
aliasman curl 'curlie'
alias curl='curlie'
```
This will affect the interactive shell, but not scripts.
### Simple GET
```sh

Some files were not shown because too many files have changed in this diff Show More