Compare commits

...

160 Commits

Author SHA1 Message Date
AJ ONeal
c57757a027 fix(docs): fix typos in sshd-prohibit-password, ssh-harden, and ssh-adduser 2026-05-14 16:00:57 -06:00
AJ ONeal
0bf485dcc4 ref!: add releases.conf (replacing releases.js) 2026-05-14 15:11:56 -06:00
AJ ONeal
9f28505af7 ref: delete unreachable upstream-fetcher modules
Stacked on the modifications PR. Now that no live code path references
the per-package fetchers, the shared HTTP/parsing helpers, the
in-process normalizer, or the example template, delete them. Pure
deletion — no behavior change.

- ~93 per-package <pkg>/releases.js fetcher modules.
- _common/{brew,fetcher,git-tag,gitea,github,github-source,
  githubish,githubish-source}.js shared HTTP/parsing helpers.
- _webi/normalize.js in-process normalization layer (cache files
  arrive normalized from webicached).
- _example/releases.js fetcher template for new packages.

The Go cache daemon (webicached) is now the sole producer of release
metadata; the Node process never makes an upstream request.
2026-05-08 16:31:59 -06:00
AJ ONeal
46508b2ec2 ref: drop unreachable upstream-fetcher references and fix classify-one cache path
The Node server's read path now goes through ~/.cache/webi/legacy/ only
(see #1075). A handful of supporting tools and tests still carried
references to the obsolete upstream-fetcher modules and the old
year-month cache layout. Update them in place; the actual deletion of
the orphaned modules follows in #1076.

- _webi/classify-one.js — read from ~/.cache/webi/legacy/<pkg>.json
  instead of ../_cache/<yearMonth>/<pkg>.json.
- _webi/builds-cacher-test.js — drop the bc.freshenRandomPackage(...)
  call; the freshener was removed when fetching went away.
- _webi/builds.js — drop the //Releases: Releases stub comment.
- _webi/lint-builds.js — drop two now-unused require()s.
- _webi/test.js — adjust a single reference to the post-cleanup shape.
2026-05-08 16:31:18 -06:00
AJ ONeal
70067a620e fix(api): only apply libc filter when caller pinned a meaningful libc
filterReleases unconditionally rejected libc=musl entries unless the
host was libc=musl, even when the caller never specified a libc in
the request. serve-releases.js defaults the libc parameter to 'libc'
(the catch-all glibc-host bucket the installer-side resolver uses),
so the website's release table and the WEBI_RELEASES probe were both
stripped of every musl entry that the cache actually contained — even
though the installer would happily consider those builds on a glibc
host (its waterfall is [none, gnu, musl, libc]).

Treat libc='libc' (and missing) as 'no preference' so the filter only
runs when the caller pinned a real libc (musl, gnu, msvc, etc.).
Specific-libc queries (?libc=musl, ?libc=gnu) still filter exactly as
before.
2026-05-08 11:48:24 -06:00
AJ ONeal
e221dafd69 chore(build-classifier): bump to v1.0.3 for parsePrefix fix
Lexver.parsePrefix now produces a true string-prefix of parseVersion
when the input has a release suffix (e.g. '1.0.0-beta',
'2025.11.15-15.42.45'). Unblocks pinned-version queries with a
non-trivial release suffix, including the 'webi zig.vim' alias chain
which redirects through 'vim-zig@2025.11.15-15.42.45' at install time.

See webinstall/webi-build-classifier#22.
2026-05-08 11:48:24 -06:00
AJ ONeal
07ad89ce46 ref(builds-cacher): cache-only Node server, no fetches or writes
Make _webi/builds-cacher.js and _webi/transform-releases.js read
exclusively from ~/.cache/webi/legacy/<name>.json and remove every code
path that fetched from upstream or wrote to disk. The Go cache daemon
(webicached) is now the sole writer; the Node server is a thin reader.

builds-cacher.js:
- Resolve cache files via Os.homedir() + '/.cache/webi/legacy/' instead
  of the cacheDir argument. Drop the 'caches' constructor parameter.
- Remove getLatestBuilds / getLatestBuildsInner — they require()d
  per-package releases.js modules, fetched upstream, and wrote
  <yyyy-mm>/<name>.json + .updated.txt to disk.
- Remove the process.nextTick stale-refresh hook in _doGetPackages.
  Cold reads return what's on disk; if the file is missing, return
  empty meta instead of fetching.
- Remove freshenRandomPackage and its supporting state
  (bc._staleNames, bc._freshenTimeout, bc._staleAge). The hourly
  background freshener competed with webicached for the same files.
- In getProjectTypeByEntry, decide selfhosted vs valid by probing for
  the cache file rather than require()-ing releases.js. Drop the
  not_found / 'PROBLEM/SOLUTION/npm clean-install' diagnostic in
  getProjectsByType — the cache-file probe replaces the module-load
  failure mode.

transform-releases.js:
- Remove Releases.get and the _normalize import. Replace
  getCachedReleases's fetch+race+stale-age machinery with a single
  Fs.readFile of ~/.cache/webi/legacy/<pkg>.json.
- Drop the in-process version re-sort in createFormatsSorter; the
  cache file is already version-sorted by webicached, so the sorter
  only re-orders within the same version.

No callers' public signatures change. Every other file is untouched —
the per-package releases.js modules, _common/*.js fetchers, and
_webi/normalize.js still exist on disk but are no longer reachable
from the request path.
2026-05-08 11:48:24 -06:00
AJ ONeal
2617520555 doc(pg): update postgres and psql docs 2026-05-07 04:04:20 -06:00
AJ ONeal
db312a98fc feat: add pg-essentials 2026-05-07 03:58:53 -06:00
AJ ONeal
cd832d024c ref(setcap-netbind): update variable names as per our conventions 2026-05-07 03:22:14 -06:00
AJ ONeal
a57faa74f3 fix(setcap-netbind): don't quote possibly-empty sudo command 2026-05-07 03:22:14 -06:00
AJ ONeal
da10371c71 chore(build-classifier): bump to v1.0.2 + maybeInstallable filename fix
Pulls in webinstall/webi-build-classifier#21 (merged 2026-05-07,
SHA 574eff5) and the host-target x64/win32 fix from #20 (SHA 71c0768)
that landed alongside it.

#21 fixes `maybeInstallable` rejecting any package version ending in
`.1` whose download URL is a GitHub source-archive endpoint
(`/tarball/vX.Y.1` or `/zipball/vX.Y.1`). Without it, this PR's
`_enumerateTriplets` priority fix is undermined: even after picking
the correct posix_2017 triplet, the newest version (e.g. serviceman
v1.0.1) is silently dropped by the classifier and the resolver falls
back to v1.0.0.

Confirmed on next.webi.sh after deploying this branch with the bumped
submodule: `serviceman@stable.sh` now resolves to v1.0.1/zip on macOS
arm64 (was v1.0.0/zip with the pre-rebase pre-fix submodule).
2026-05-07 00:22:02 -06:00
AJ ONeal
d0b0d54d18 fix(builds-cacher): enumerate specific OS/arch before ANYOS/ANYARCH
In _enumerateTriplets, the order of `oses` and `arches` was
ANYOS/ANYARCH first, specific second. This caused findMatchingPackages
to pick the most-generic triplet (e.g. ANYOS-ANYARCH-none) before
trying specific OS triplets — and packages that have a wildcard git
fallback alongside per-platform binaries would resolve to the git
source instead of the binary, even when the client never asked for
git as an unpacker.

Reverse the order so specific platforms win:
  - oses: hostTarget.os, posix_2017, posix_2024, ANYOS
  - arches: arches.concat(['ANYARCH'])

Concrete example: serviceman has both posix_2017/*/tar.gz and
*/*/git in the cache. Pre-fix, findMatchingPackages picks
ANYOS-ANYARCH-none (containing only the .git entry). The .git gate
in getSortedFormats then correctly excludes git from format
candidates, but the chosen triplet has nothing else, so selectPackage
falls through to packages[0] = git entry. Post-fix,
findMatchingPackages picks posix_2017-ANYARCH-none first (containing
[tar.gz, zip]), and selectPackage returns tar.gz.
2026-05-07 00:22:02 -06:00
AJ ONeal
2d1c082e30 feat(webi): probe zst as unpacker; properly probe formats in webi-pwsh
webi/webi.sh: detect unzstd/zstd alongside the existing git/unxz/
unzip/tar probes. Sends `?formats=...,zst` when zstd is available so
the server can pick a .tar.zst build only on hosts that can extract
it.

webi/webi-pwsh.ps1: replace the hardcoded `formats=zip,exe,tar,git`
TODO with real Get-Command probing for git, zstd, and 7z.
2026-05-06 23:23:02 -06:00
AJ ONeal
28cd129a23 ref(builds-cacher): gate .git on client-provided unpacker
`.git` was pushed unconditionally into getSortedFormats's candidate
ext list, while sibling unpacker formats (.tar.xz, .tar.zst, .zip,
.7z) are gated on whether the caller's `formats` argument signals
the client has the corresponding tool.

Make `.git` consistent: only add it to the candidate list when
formats includes 'git'. The default WEBI_FORMATS ('tar,exe,zip,xz,
dmg') doesn't include git, so the change is a no-op for the
current default. Clients that want git-source packages installed
can pass `?formats=tar,exe,zip,xz,dmg,git` (or set the equivalent
in a future client-side probe).

For packages that have only a git-source asset (e.g. some vim
plugins), the existing fallback to `packages[0]` still returns the
git entry — behavior unchanged. The only observable change is for
packages where both a binary and a git fallback exist for the same
triplet: previously the git entry could win over the binary; now
it wins only when the client opts in.
2026-05-06 23:05:11 -06:00
AJ ONeal
a5c8fc28a4 fix(builds-cacher): coalesce concurrent getPackages for same name
When two HTTP requests arrived simultaneously for the same package on
a cold in-memory cache (bc._caches[name] === undefined), they would
both:
  1. Enter getPackages, see no warm cache,
  2. Read and parse the same _cache/{pkg}.json independently,
  3. Both call transformAndUpdate, which re-runs _classify on every
     build.

The first call populates bc._targetsByBuildIdCache as it classifies.
The second call then hits the cache shortcut at the top of _classify
and skips the projInfo.oses/arches/libcs/formats/triplets
accumulation block entirely. Its projInfo ends up with empty tracking
arrays (because the prior Object.assign(projInfo, meta) reset them),
and that poisoned projInfo gets written to bc._caches[name],
overwriting the first call's good cache.

After this, every subsequent installer request returns errPackage
because serve-installer.js checks projInfo.oses.includes(hostTarget.os)
— and projInfo.oses is now [].

Fix: a per-name in-flight promise. Concurrent callers for a cold
package share a single load. Calls for warm packages take the fast
path with no synchronization.

Reproduced reliably with Promise.all of 6 cold-cache calls for the
same package: 1/6 succeeded before the fix, 6/6 after. On staging at
HTTP concurrency=12, installer cand-only-errors went from 24-229
(cause-dependent) to 0.
2026-05-06 11:45:26 -06:00
AJ ONeal
d739ca89ba fix(bun): drop .txt/.asc assets and strip .zip from release names 2026-03-09 13:23:57 -06:00
AJ ONeal
012661c935 fix(bun): only select baseline builds rather than relying on sort order 2026-03-09 13:23:57 -06:00
Tori0419
303417d513 fix(bun): prefer baseline linux releases (fix #879) 2026-03-08 22:59:30 -06:00
AJ ONeal
3e2e7f2f65 feat(monorel): add installer for monorepo release tool
Adds releases.js, install.sh, install.ps1, and README.md for monorel,
a Go monorepo release tool from therootcompany/golib. Filters monorepo
releases by tools/monorel/ prefix and auto-installs prerequisites
(git, gh, goreleaser).
2026-03-08 22:50:34 -06:00
AJ ONeal
ca81127b93 fix(docs): fix typos in goreleaser, ssh-authorize, and node READMEs
- goreleaser: "you should the git tag" → "you should see the git tag"
- ssh-authorize: "will to do" → "will be able to do"
- node: "jhint" → "jshint"
2026-03-08 19:53:26 -06:00
AJ ONeal
3c8b66be55 docs: add AGENTS.md with conventions and design principles 2026-03-08 19:53:26 -06:00
AJ ONeal
8f9b9da4a3 chore: npm run fmt 2026-03-08 19:38:49 -06:00
bry-val
81ffcf3182 doc(deno): update Hello World example to use deno.com URL
Signed-off-by: bry-val <94031627+bry-val@users.noreply.github.com>
2025-03-06 20:48:54 +00:00
AJ ONeal
3d1a75102f ref(koji): keep backwards-compat 2025-02-20 10:56:41 +00:00
Finley Thomalla
e6b3aec8c0 docs(koji): fix mistakes, improve 2025-02-20 10:48:52 +00:00
Finley Thomalla
ed8058deb8 fix(koji): update binary path
The release process of koji has been updated a while ago, resulting in the binary not being nested in the archive anymore.

Fixes cococonscious/koji#137
2025-02-20 10:48:51 +00:00
AJ ONeal
480169beac fix(terramate): link both terramate and terramate-ls 2025-02-03 21:18:43 +00:00
AJ ONeal
625168156f fix(terramate): don't exclude package files 2025-02-03 21:18:43 +00:00
Michael Dubner
75e39c54a2 fix: add '386' and 'i386' to tab regexp (fixes GH-941) 2025-01-29 22:59:56 +00:00
AJ ONeal
cac2e62da8 feat(mariadb): add mysql and mariadb-server aliases 2025-01-26 00:35:51 +00:00
AJ ONeal
b6ab62c13f feat: add MariaDB 2025-01-26 00:35:50 +00:00
AJ ONeal
d666a860d1 chore(serviceman): remove junk debug log 2025-01-25 00:18:04 +00:00
AJ ONeal
7ff40e175f ref(ssh-pubkey): switch to ed25519 as the primary algorithm 2025-01-23 22:00:30 +00:00
AJ ONeal
45e7dc314b fix(sass): manually match arches { arm: armv7, ia32: x86, x64: amd64 } 2025-01-23 06:51:45 +00:00
AJ ONeal
976602236b chore: npm run fmt 2024-12-18 22:08:20 +00:00
AJ ONeal
83a214a032 ref(terramate): mostly style updates 2024-12-18 22:08:19 +00:00
OG
cc66f930b0 feat: add terramate 2024-12-18 21:19:09 +00:00
AJ ONeal
afe35f9198 feat(node): ask to install libstdc++ on Alpine 2024-12-18 18:08:52 +00:00
AJ ONeal
910fa48278 doc(node): list node dependencies 2024-12-18 18:08:51 +00:00
AJ ONeal
5544ff9f1b feat(shellcheck): include ~/.shellcheckrc with example ignores and enables 2024-12-17 20:34:55 +00:00
AJ ONeal
d3f3ad1688 doc(shellcheck): include ignore/enable code list, update usage and doc links 2024-12-17 20:34:54 +00:00
AJ ONeal
4eff5b6cbe doc(syncthing): 'env PATH=' is no longer needed for serviceman 2024-12-16 19:12:36 +00:00
AJ ONeal
7b8f882d80 fix(serviceman): do not use 'sudo' or 'env PATH="$PATH"' 2024-12-16 19:12:35 +00:00
AJ ONeal
117ee6117d doc(node): add types to package 2024-12-16 01:21:48 +00:00
AJ ONeal
ce18bd5e61 doc(gh-source): make baseurl optional 2024-12-16 01:21:47 +00:00
AJ ONeal
6aeb60008b fix(bun): mark musl builds as hard-musl (not gnu-compatible) 2024-12-16 01:21:46 +00:00
AJ ONeal
83a6d02d50 fix(api): project 'alias'es (symlinks) should be resolved before checking for 'selfhosted' 2024-12-16 01:02:37 +00:00
AJ ONeal
40316a866c doc(serviceman): update docs across installers 2024-12-16 00:59:46 +00:00
AJ ONeal
e2ad197067 doc(serviceman): --agent instead of --user 2024-12-16 00:59:46 +00:00
AJ ONeal
3995b7e568 feat(serviceman): update for v0.9 2024-12-16 00:59:45 +00:00
AJ ONeal
de71f667a0 doc(uuidgen): add uppercase and uuidv4 examples 2024-12-16 00:56:26 +00:00
AJ ONeal
6320c519dc fix(terraform): correct channel for stable and non-stable (rc, beta, alpha) 2024-12-16 00:54:42 +00:00
AJ ONeal
f1d1027701 ref: handle fetch errors consistently (Fetcher.fetch) 2024-12-16 00:20:01 +00:00
AJ ONeal
fe59a2f35c chore: update deps 2024-12-16 00:01:15 +00:00
AJ ONeal
a5ed5dbe91 chore: remove @root/request dependency 2024-12-16 00:01:14 +00:00
AJ ONeal
217d61ed34 doc: remove references to 'request' 2024-12-16 00:01:14 +00:00
AJ ONeal
14cebeeb61 ref(webi): complete transition from 'request' for 'fetch' 2024-12-16 00:01:13 +00:00
MichalTirpak
ba94ad883b partial refactor for files regarding the ISSUE#898 request to fetch besides mariadb 2024-12-15 06:51:44 +00:00
AJ ONeal
801df24541 chore: remove junk mariadb releases (never completed) 2024-12-15 06:51:44 +00:00
AJ ONeal
d6fc5cec97 doc: remove excess whitespace for (id -u -n) 2024-12-15 06:07:43 +00:00
Shuchit
1f3e7b5bf0 ref: replace 'whoami' with 'id -u -n' for POSIX compatibility 2024-12-15 06:03:30 +00:00
AJ ONeal
c94b4cf5c7 fix(windows): use Get-CimInstance instead of deprecated Get-WmiObject 2024-11-10 07:51:52 +00:00
Caleb
d7a4aaf6b7 fix(windows): use Get-WmiObject instead of deprecated wmic
Signed-off-by: Caleb <53413881+CK6853@users.noreply.github.com>
2024-11-10 07:51:23 +00:00
AJ ONeal
93be13f388 fix(ssh-adduser): use 'wget' when 'curl' isn't available 2024-11-10 07:15:41 +00:00
AJ ONeal
257adec36d feat: add uuidv7 2024-10-15 00:37:27 +00:00
AJ ONeal
aa3f468989 chore(git): ignore file explorer preference files 2024-10-14 23:28:36 +00:00
AJ ONeal
553380e64c chore: remove junk .DS_Store 2024-10-14 23:28:12 +00:00
AJ ONeal
231b6d12e4 feat: add sqlc 2024-10-14 22:51:54 +00:00
AJ ONeal
f4ec7ca640 ref(fetch): manual removal of unused request 2024-10-14 09:33:55 +00:00
AJ ONeal
5d28f7333a ref(githubish): manual removal of unused request 2024-10-14 09:07:17 +00:00
AJ ONeal
2010c62226 ref(githubish): automated removal of unused request 2024-10-14 09:03:22 +00:00
Neeraj
93e6c64349 ref(fetch) replace request in chromedriver/releases.js 2024-10-14 08:27:37 +00:00
Neeraj
ea0762c3ea ref(fetch) replace request in _common/brew.js 2024-10-14 08:27:28 +00:00
AJ ONeal
f2c4694647 fix(windows+ollama): create download directory before downloading 2024-10-14 07:45:35 +00:00
AJ ONeal
d8fffe0dc3 doc(postgres): revamp with current best-known practices, separate server and client info 2024-10-14 07:42:49 +00:00
AJ ONeal
6924baca2b doc(psql): show creating a table 2024-10-14 07:41:57 +00:00
AJ ONeal
3c9609457b doc(psql): add backup / restore instructions 2024-10-14 07:41:56 +00:00
AJ ONeal
731beff35c doc(psql): add cheat sheet 2024-10-14 07:41:56 +00:00
AJ ONeal
b375bd8d7e feat(psql): add aliases 2024-10-14 07:41:55 +00:00
AJ ONeal
502e3d6aa0 feat: add psql as its own installer 2024-10-14 07:41:55 +00:00
AJ ONeal
a2034c99e9 feat(postgres): add pg as alias 2024-10-14 07:41:55 +00:00
AJ ONeal
8f436dcedd ref(postgres): update aliases 2024-10-14 07:41:54 +00:00
AJ ONeal
500b69e70c feat(postgres): add latest releases 2024-10-14 07:41:48 +00:00
AJ ONeal
c503f105fb feat: add DistributableRaw type for Githubish release assets 2024-10-12 00:48:29 +00:00
AJ ONeal
b7a113a001 feat(ollama+windows): replace placeholder with working installer 2024-09-16 17:52:26 -06:00
AJ ONeal
3385ceaa02 fix(ollama): support bespoke file extensions, locations, version format, etc 2024-09-16 23:18:08 +00:00
AJ ONeal
005ca9f7da fix(ollama): classify 'rocm' as its own cpu + update classifier 2024-09-16 23:18:07 +00:00
AJ ONeal
efe3df6453 fix(webi): define PKG_STABLE for template when no suitable version is found 2024-09-15 23:39:51 +00:00
AJ ONeal
c4a6d74776 doc(git): update reasonable defaults 2024-09-15 15:56:43 -06:00
AJ ONeal
4628cc0333 fix(runzip): use .getDistributables() 2024-09-14 17:10:36 +00:00
AJ ONeal
deb8f37f8f chore: add more config, cache, and temp files to .gitignore 2024-09-14 17:06:34 +00:00
AJ ONeal
7c62699a43 chore: organize and label .gitignore 2024-09-14 17:06:21 +00:00
AJ ONeal
62c9fcc1ba doc(node): mention ~/.npm/, ~/.node/, and ~/.node_repl_history 2024-09-13 22:36:36 +00:00
AJ ONeal
90eb1587ba doc+fix: enumerateLatestVersions only takes 1 argument 2024-09-13 22:27:08 +00:00
AJ ONeal
e6dcbfb83a feat(installer): show stable, and latest if different from stable 2024-09-13 22:27:07 +00:00
AJ ONeal
35a1d08d3a chore(lint): add jsconfig.json 2024-09-13 22:23:21 +00:00
AJ ONeal
fbd5211cd8 fix(alpine): set vim-shell to 'sh' if 'bash' isn't in PATH 2024-09-13 08:35:38 +00:00
AJ ONeal
54fc06904d feat: add runzip for unzipping rar files 2024-09-13 08:35:13 +00:00
AJ ONeal
fc3fef8a89 chore: add AbortController to .jshintrc.globals 2024-09-13 08:33:50 +00:00
AJ ONeal
566ea0fc9a ref!(githubish-source): bring over updates from 'githubish' 2024-09-13 08:33:49 +00:00
AJ ONeal
b2c62dc6b6 ref: rename _common/githubish-source.js 2024-09-13 08:33:49 +00:00
AJ ONeal
f19fcc361a ref!: git sources & posix releases 2024-09-13 08:33:49 +00:00
AJ ONeal
8f39617bcd fix: allow 'posix_2017' and 'posix_2024' for non-windows OSes 2024-09-13 08:33:48 +00:00
AJ ONeal
5bb2832ad9 feat(webi): detect 'prev', 'dev', & 'developer' as beta 2024-09-13 08:33:47 +00:00
AJ ONeal
81605ddf61 ref(duckdns): disambiguate from duckdns.sh more clearly 2024-09-13 08:33:34 +00:00
AJ ONeal
13ac3e32fc fix(go-essentials): remove guru (deprecated) 2024-09-13 08:33:34 +00:00
AJ ONeal
e3e61ca256 ref: rename .getDistributables() 2024-09-13 08:33:33 +00:00
AJ ONeal
3364dcb075 fix(rpi-zero): add armv6 alias 2024-09-13 08:33:33 +00:00
AJ ONeal
58be5ce649 ref: explicitly mark as 'commonjs' module 2024-09-13 08:33:33 +00:00
AJ ONeal
87b308550d doc(githubish): be explicit that 'request' is no longer used 2024-09-13 08:33:32 +00:00
AJ ONeal
15098ba1d2 ref: use Releases.latest() and Releases.sample() 2024-09-13 08:33:32 +00:00
Finley Thomalla
941453677e feat(koji): update releases and readme
Repository has been transferred to https://github.com/cococonscious/koji
2024-09-13 08:22:36 +00:00
AJ ONeal
556697ad67 ref: make ./builds-cacher.js one-off testable; add ./classify-one.js 2024-09-11 23:29:22 +00:00
AJ ONeal
595a0b8ef9 fix(linux-gnu): update build-classifier@v1.0.0: respect explicit 'gnu' 2024-09-11 17:18:17 -06:00
AJ ONeal
66ba82181c ref(example): update to use async/await 2024-09-11 20:22:17 +00:00
Nikolay Nikolaev
9d1cde0ced feat: add cilium
Signed-off-by: Nikolay Nikolaev <nicknickolaev@gmail.com>
2024-09-11 20:20:04 +00:00
AJ ONeal
566b5c047f fix(classifier): log rather than throw on failed classification 2024-08-20 15:51:02 -06:00
AJ ONeal
c71126fcd8 fix: init ~/.config/envman/ during init phase (before pre-install) 2024-07-24 12:23:34 -06:00
AJ ONeal
3263196360 fix(github-like): correct Authorization header 2024-06-05 19:41:24 +00:00
AJ ONeal
5c12cb1fa7 fix(busybox): use -k instead of --keep 2024-06-05 17:40:01 +00:00
AJ ONeal
b1738741d1 ref!(github-like): use 'fetch' instead of '@root/request' 2024-06-05 17:37:20 +00:00
AJ ONeal
125ffa96dc ref(github-like): pass options object for args 2024-06-05 17:37:19 +00:00
AJ ONeal
dca40ff2ed ref+sec(github-like): lift _common/github.js from _common/githubish.js 2024-06-05 17:35:28 +00:00
AJ ONeal
36af48aceb ref(github-like): rename _common/githubish.js 2024-06-05 17:35:28 +00:00
AJ ONeal
9e9c3a610d fix(github-like): don't send GitHub Tokens to GitHub-like APIs (i.e. Gitea) 2024-06-05 17:35:21 +00:00
AJ ONeal
fefeb076f2 doc(github-like): update types 2024-06-05 17:35:20 +00:00
AJ ONeal
e54262cf13 ref(ssh-pubkey): show key generation message since it takes so long 2024-06-05 17:32:38 +00:00
AJ ONeal
6c5b040b8c fix: update ssh-pubkey for modern 3072+ bit and ed25519 keys 2024-06-05 17:32:37 +00:00
AJ ONeal
e872442f3b fix(ssh-pubkey): use POSIX-compliant cp -RPp 2024-06-05 17:32:14 +00:00
AJ ONeal
2d65bb14ff ref(ssh-pubkey): prefer 'test ...' over '[ ... ]' 2024-06-05 17:32:14 +00:00
AJ ONeal
5ead8362c0 ref(ssh-pubkey): prefer ~ over $HOME for unquoted paths 2024-06-05 17:32:13 +00:00
AJ ONeal
88a9319431 chore: remove junk file vim-things.sh 2024-06-05 17:32:12 +00:00
AJ ONeal
7b70ab8538 ref(pwsh): cleanup and comment 'pkg_done_message' 2024-05-16 21:10:25 +00:00
Victor Irzak
974e2cfc9c fix: Use full path to call pwsh, since it is not in the PATH yet 2024-05-16 21:10:04 +00:00
AJ ONeal
3810cd621e doc(README.md): use 'npm clean-install' 2024-05-16 20:35:44 +00:00
HacDan
8ffba627f3 docs(README.md): add command for cloning submodules to readme 2024-05-16 20:34:51 +00:00
HacDan
1a3f22e2ba docs(README.md): update readme to reflect repo name change
Updated README.md from packages to webi-installers to be in line with current project structure
2024-05-16 20:34:37 +00:00
Patrick Matthiesen
48574168a8 Fix vim-devicons homepage link
added the missing 'vim-' from the repository name

Signed-off-by: Patrick Matthiesen <43612965+PatrickMatthiesen@users.noreply.github.com>
2024-05-11 22:48:40 +02:00
AJ ONeal
c431ce0b22 feat(webi-essentials+bsd): support 'pkg_add' 2024-01-15 23:01:01 -07:00
AJ ONeal
7ea58dcdfb fix(bsd): silence 'uname -o' error when not supported 2024-01-15 22:00:01 -07:00
AJ ONeal
67f361d35c fix(bsd): detect and use OpenBSD 'shasum' 2024-01-15 22:00:01 -07:00
AJ ONeal
05f33b1ff3 fix(bsd): use explicit untar 2024-01-15 22:00:00 -07:00
AJ ONeal
adef540665 doc(dashcore-utils): show the known mount rather than 'EXAMPLE' 2024-01-15 21:48:53 -07:00
AJ ONeal
7293a3de5f doc(dashcore-utils): conf for listen, daemon, and evonode 2024-01-15 21:48:53 -07:00
AJ ONeal
603cc96996 doc(dashd): typo 'bin/bin/' => 'bin/' 2024-01-15 21:48:53 -07:00
AJ ONeal
c5c258747e fix(dashcore-utils): show correct filename during download 2024-01-15 21:48:53 -07:00
AJ ONeal
141b56b694 fix(go-essentials): correctly silence 'command -v' 2024-01-15 21:48:53 -07:00
AJ ONeal
d306f7fc56 fix(dashcore-utils): run 'webi' by path, not WEBI_HOST url 2024-01-15 21:48:53 -07:00
AJ ONeal
90344c5365 fix(brew-update-service): install serviceman as needed 2024-01-15 21:46:24 -07:00
AJ ONeal
396e34da41 feat(pyenv+macos): install Command Line Tools for macOS 2024-01-15 21:46:24 -07:00
AJ ONeal
e00fe40725 feat: add macos commandlinetools (for pyenv, brew, etc) 2024-01-15 21:46:24 -07:00
AJ ONeal
63a08e8ace doc(pyenv): update deps, move Files, add ToC
f: doc(pyenv)
2024-01-15 21:46:23 -07:00
AJ ONeal
22f09bbf5c fix(webi-essentials): replace '==' bashism with '=' 2024-01-15 21:44:43 -07:00
Soren Richenberg
af6b09ef2a fix(windows): apply -File and quotations to webi.bat file to pass %USERPROFILE% as a single argument when it contains spaces 2024-01-15 21:44:43 -07:00
AJ ONeal
379a301bf3 fix(windows): 'powershell -File ...' to work with $HOME with spaces 2024-01-15 21:44:43 -07:00
AJ ONeal
73c5ea082c fix(sd+windows): correct install dir (had typo) 2024-01-15 21:44:42 -07:00
AJ ONeal
16d108e3d5 fix(fish): treat .app.zip as .zip for extraction 2024-01-15 16:23:14 -07:00
AJ ONeal
4738ba04b9 feat: add julia (programming language) 2024-01-11 15:20:12 -07:00
AJ ONeal
904ce1f2d6 fix(cache): set 'updated' to old date, correct typo 2024-01-07 21:04:01 -07:00
434 changed files with 5963 additions and 5326 deletions

View File

@@ -35,15 +35,18 @@ info, and doing a find and replace on a few file system path names.
```
2. Copy the example template and update with info from Official Releases:
<https://github.com/___CHANGE/ME___/releases>
```bash
rsync -av _example/ CHANGE-ME/
```
- [ ] update `CHANGE-ME/release.js` to use the official repo
- [ ] Learn how `CHANGE-ME` unpacks (i.e. as a single file? as a .tar.gz? as
a .tar.gz with a folder named CHANGE-ME?)
- [ ] find and replace to change the name
- [ ] update `CHANGE-ME/install.sh` (see `bat` and `jq` as examples)
- [ ] update `CHANGE-ME/install.ps1` (see `bat` and `jq` as examples)
3. Needs an updated tagline and cheat sheet
- [ ] update `CHANGE-ME/README.md`
- [ ] official URL

21
.gitignore vendored
View File

@@ -1,7 +1,24 @@
.env
node_modules
# generated artifacts
install-*.sh
install-*.bat
install-*.ps1
# local config
.env.*
*.env
.env
!example.env
# caches
_cache/
node_modules/
# temporary & backup files
.*.sw*
*.bak
*.bak.*
# other
.DS_Store
desktop.ini
.directory

View File

@@ -1,4 +1,7 @@
{
"globals": {
"AbortController": false
},
"browser": true,
"node": true,
"esversion": 11,

372
AGENTS.md Normal file
View File

@@ -0,0 +1,372 @@
# Webi Installers — Agent Guide
Webi installs dev tools to `~/.local/` without sudo. Each installer is a small
package of 3-4 files. This guide tells you how to create and modify them.
## Why Webi Exists
Webi makes tool installation trivially repeatable for people who aren't
sysadmins — freelance clients, junior devs, anyone who shouldn't have to care
about PATH, permissions, or platform differences. Three things matter:
1. **Install without friction.** No sudo, no manual PATH edits, no "necessary
but unimportant" steps leaking into the experience.
2. **Know where things are.** The Files section tells you exactly what got
created or modified. Nothing should be mysterious.
3. **Copy-paste recipes.** The cheat sheet is what you'd send someone less
experienced than yourself instead of a project's full README — scannable,
concrete, easy to reference by name.
## Quick Start: Adding a New Installer
1. Identify the **package type** (see [Categories](#categories) below)
2. Find an existing installer of the same type to use as a template
3. Create `<name>/releases.js`, `install.sh`, `install.ps1`, `README.md`
4. Test with the command in [Testing releases.js](#testing-releasesjs)
5. Run formatters before committing (see [Code Style](#code-style))
## Directory Layout
```
<package-name>/
README.md # YAML frontmatter + docs
releases.js # Fetches release metadata (Node.js)
install.sh # POSIX shell installer (macOS/Linux)
install.ps1 # PowerShell installer (Windows) — optional
```
Key infrastructure directories (do not modify without good reason):
- `_webi/` — bootstrap templates, `normalize.js` (auto-detects OS/arch/ext from
filenames)
- `_common/` — shared JS: `github.js`, `githubish.js`, `gitea.js`, `fetcher.js`
- `_example/` — canonical template for new packages
- `_examples/` — specialized templates (goreleaser, xz-compressed)
## Categories
Ref: <https://github.com/webinstall/webi-installers/issues/412>
| Type | Description | Template to copy |
| ----- | -------------------------------------- | ---------------- |
| `bin` | Single binary in tar/zip | `koji`, `delta` |
| `bin` | Single bare binary (no archive) | `arc`, `shfmt` |
| `bin` | Goreleaser-style archives | `keypairs` |
| 📦 | Self-contained package (bin/man/share) | `node`, `go` |
| 📂 | Multiple binaries/scripts | `pg` |
| 🔗 | Alias/redirect to another package | `ripgrep``rg` |
| 📝 | Bespoke / custom install | `rustlang` |
## releases.js
Fetches release metadata and returns a normalized object. Most packages use
GitHub releases:
```js
'use strict';
var github = require('../_common/github.js');
var owner = 'OWNER';
var repo = 'REPO';
let Releases = module.exports;
Releases.latest = async function () {
let all = await github(null, owner, repo);
return all;
};
Releases.sample = async function () {
let normalize = require('../_webi/normalize.js');
let all = await Releases.latest();
all = normalize(all);
all.releases = all.releases.slice(0, 5);
return all;
};
if (module === require.main) {
(async function () {
let samples = await Releases.sample();
console.info(JSON.stringify(samples, null, 2));
})();
}
```
### Common release transformations
**Strip version prefix** (monorepo or tool-prefixed tags):
```js
// e.g. "tools/monorel/v0.6.5" → "v0.6.5"
rel.version = rel.version.replace(/^tools\/monorel\//, '');
// e.g. "cli-v1.2.3" → "v1.2.3"
rel.version = rel.version.replace(/^cli-/, '');
```
**Filter releases** (monorepo with multiple tools, or unwanted assets):
```js
all.releases = all.releases.filter(function (rel) {
// Keep only releases for this tool
return rel.version.startsWith('tools/monorel/');
});
```
Apply transformations inside `Releases.latest`, before returning `all`.
**Available sources** beyond `github.js`:
- `_common/gitea.js` — Gitea servers
- `_common/git-tag.js` — Git tag listing
- Custom fetch from any JSON API (see `go/releases.js`, `terraform/releases.js`)
### Testing releases.js
```sh
node -e "
let Releases = require('./<name>/releases.js');
Releases.sample().then(function (all) {
console.log(JSON.stringify(all, null, 2));
});
"
```
Verify: versions are clean semver (`0.6.5` not `tools/monorel/v0.6.5`), OS/arch
detected correctly, download URLs resolve.
## install.sh
POSIX shell (`sh`, not bash). Always wrapped in a function:
```sh
#!/bin/sh
# shellcheck disable=SC2034
set -e
set -u
__init_pkgname() {
# These 6 variables are required
pkg_cmd_name="cmd"
pkg_dst_cmd="$HOME/.local/bin/cmd"
pkg_dst="$pkg_dst_cmd"
pkg_src_cmd="$HOME/.local/opt/cmd-v$WEBI_VERSION/bin/cmd"
pkg_src_dir="$HOME/.local/opt/cmd-v$WEBI_VERSION"
pkg_src="$pkg_src_cmd"
pkg_install() {
mkdir -p "$(dirname "$pkg_src_cmd")"
mv ./cmd "$pkg_src_cmd"
}
pkg_get_current_version() {
cmd --version 2> /dev/null | head -n 1 | cut -d' ' -f2
}
}
__init_pkgname
```
### Framework variables available in install.sh
Set by the webi bootstrap (`_webi/package-install.tpl.sh`):
| Variable | Example | Description |
| --------------- | ------------------- | --------------------- |
| `WEBI_VERSION` | `1.2.3` | Selected version |
| `WEBI_PKG_URL` | `https://...` | Download URL |
| `WEBI_PKG_FILE` | `foo-v1.2.3.tar.gz` | Download filename |
| `WEBI_OS` | `linux` | Detected OS |
| `WEBI_ARCH` | `amd64` | Detected architecture |
| `WEBI_EXT` | `tar.gz` | Archive extension |
| `WEBI_CHANNEL` | `stable` | Release channel |
| `PKG_NAME` | `foo` | Package name |
### Override functions
| Function | Purpose |
| --------------------------- | --------------------------------------------- |
| `pkg_install()` | **Required.** Move files to `$pkg_src` |
| `pkg_get_current_version()` | Parse installed version from command output |
| `pkg_post_install()` | Post-install setup (git config, shell config) |
| `pkg_done_message()` | Custom completion message |
| `pkg_link()` | Override default symlink behavior |
| `pkg_pre_install()` | Custom pre-install logic |
### Framework helper functions
| Function | Purpose |
| ------------------------ | ---------------------------------- |
| `webi_download()` | Download package if not cached |
| `webi_extract()` | Extract archive by extension |
| `webi_path_add <dir>` | Add to PATH via envman |
| `webi_link()` | Create versioned symlinks |
| `webi_check_installed()` | Check if version already installed |
### pkg_install patterns
**Bare binary in archive root:**
```sh
mv ./cmd "$pkg_src_cmd"
```
**Binary in a subdirectory (goreleaser-style `cmd-OS-arch/cmd`):**
```sh
mv ./cmd-*/cmd "$pkg_src_cmd"
```
**Flexible detection (handles multiple archive layouts):**
```sh
if test -f ./cmd; then
mv ./cmd "$pkg_src_cmd"
elif test -e ./cmd-*/cmd; then
mv ./cmd-*/cmd "$pkg_src_cmd"
elif test -e ./cmd-*; then
mv ./cmd-* "$pkg_src_cmd"
fi
```
## install.ps1
PowerShell for Windows. Uses `$Env:` variables. See `_example/install.ps1` for
the full template. Key differences from install.sh:
- Paths use backslashes, commands end in `.exe`
- `$Env:USERPROFILE` instead of `$HOME`
- `Test-Path`, `Move-Item`, `Copy-Item` instead of shell equivalents
- Downloads go to `$Env:USERPROFILE\Downloads\webi\`
- Temp work in `.local\tmp`, use `Push-Location`/`Pop-Location`
- Symlinks done via `Copy-Item` (not actual symlinks)
## README.md
````markdown
---
title: toolname
homepage: https://github.com/owner/repo
tagline: |
toolname: A short one-line description.
---
To update or switch versions, run `webi toolname@stable` (or `@v2`, `@beta`,
etc).
### Files
These are the files that are created and/or modified with this installer:
```text
~/.config/envman/PATH.env
~/.local/bin/toolname
~/.local/opt/toolname-VERSION/bin/toolname
```
## Cheat Sheet
> `toolname` does X. Brief description.
### How to use toolname
```sh
toolname --example
```
````
Note: **Files goes above Cheat Sheet**, not inside it.
### Cheat Sheet tone and style
Webi cheat sheets are **opinionated quick-reference guides**, not comprehensive
documentation. Think "colleague's sticky note" — not the project's official
README.
The tool is the topic, but **the problem is the reason**. Cheat sheets are
organized around tasks the reader already wants to do — the tool is how they get
there. Headings reference the tool (the reader came to this page on purpose),
but the content solves the underlying problem completely:
- "How to reverse proxy to Node" (caddy knowledge, not just node)
- "How to run a Node app as a System Service" (serviceman knowledge)
- "How to Enable Secure Remote Postgres Access" (openssl, pg_hba.conf, systemd)
- "How to manually configure git to use delta" (gitconfig, not delta flags)
- "How to make fish the default shell in iTerm2" (iTerm2 knowledge, not fish)
The reader's question is "how do I do X?" and the cheat sheet answers it
completely — including configs, adjacent tools, and platform-specific
variations. A goreleaser cheat sheet teaches you goreleaser YAML. A postgres
cheat sheet teaches you pg_hba.conf, openssl certs, and systemd units.
Cheat sheets cross tool boundaries freely. Node's references caddy, serviceman,
setcap-netbind, GitHub Actions. Postgres references serviceman, openrc, launchd.
They link to each other's webi pages. The scope is "everything you need to
accomplish this task," not "everything this one binary does."
They show the actual files and configs that matter — not documentation _about_
configs, but the configs themselves, copy-pasteable, with inline comments
explaining the non-obvious parts.
**Guidelines:**
- **Show the 3-5 things someone will actually do**, with copy-pasteable
commands. Skip exhaustive flag lists and API docs.
- **Lead with practical integration.** Show the exact `git config` lines, the
exact hook script, the exact shell alias — don't just explain the feature and
leave wiring up to the reader.
- **Skip what they already know.** No need to re-explain what the tool is at
length — the tagline and one-liner blockquote handle that. Get to the
commands.
- **Prefer concrete over abstract.** Instead of "you can configure X via a
config file", show the config file contents.
## Shell Naming Conventions
**Variables:**
- `ALL_CAPS` — environment variables only (`PATH`, `HOME`, `WEBI_VERSION`)
- `b_varname` — block-scoped (inside a function, loop, or conditional)
- `g_varname` — global to the script (and sourced scripts)
- `a_varname` — function arguments
**Functions and commands:**
- `fn_name` — helper functions (anything other than the script's main/entry
function)
- `cmd_name` — command aliases, e.g. `cmd_curl='curl --fail-with-body -sSL'`
## Code Style
Requires `node`, `shfmt`, `pwsh`, and `pwsh-essentials` (install all via webi).
Run before committing:
```sh
npm run fmt # prettier (JS/MD) + shfmt (sh) + pwsh-fmt (ps1)
npm run lint # jshint + shellcheck + pwsh-lint
```
Commit messages: `feat(<pkg>): add installer`, `fix(<pkg>): update install.sh`,
`docs(<pkg>): add cheat sheet`.
## Naming Conventions
- The canonical package name is the **command name** you type: `go`, `node`,
`rg`
- The alternate/alias name is the project name: `golang`, `nodejs`, `ripgrep`
- Package directories are lowercase with hyphens
## Common Pitfalls
- **Monorepo releases**: The GitHub API returns ALL releases for the repo. You
must filter in `releases.js` and strip the tag prefix from the version.
- **No `--version` flag**: Some tools lack version introspection. Comment out
`pkg_get_current_version` — webi still works, it just can't skip reinstalls.
- **normalize.js auto-detection**: OS/arch/ext are guessed from download
filenames. If the tool uses non-standard naming, you may need to set `os`,
`arch`, or `ext` explicitly in `releases.js`.
- **Goreleaser archives**: Typically contain a bare binary at the archive root
(not nested in a directory). Use `mv ./cmd "$pkg_src_cmd"`.

View File

@@ -10,7 +10,6 @@
- You'll be asked to make changes if you don't run the code formatters and
linters:
- Node / JavaScript:
- [prettier](https://webinstall.dev/prettier)
```sh
@@ -21,7 +20,6 @@
npm run lint
```
- Bash
- [shfmt](https://webinstall.dev/shfmt)
```sh
npm run shfmt

View File

@@ -98,9 +98,10 @@ You just fill in the blanks.
Just create an empty directory and run the tests until you get a good result.
```sh
git clone git@github.com:webinstall/packages.git
pushd packages
npm install
git clone git@github.com:webinstall/webi-installers.git
pushd ./webi-installers/
git submodule update --init
npm clean-install
```
```sh
@@ -144,8 +145,8 @@ It looks like this:
`releases.js`:
```js
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
module.exports = function () {
return github(null, owner, repo).then(function (all) {
// if you need to do something special, you can do it here
// ...
return all;

View File

@@ -1,64 +0,0 @@
'use strict';
/**
* Gets a releases from 'brew'.
*
* @param request
* @param {string} formula
* @returns {PromiseLike<any> | Promise<any>}
*/
function getAllReleases(request, formula) {
if (!formula) {
return Promise.reject('missing formula for brew');
}
return request({
url: 'https://formulae.brew.sh/api/formula/' + formula + '.json',
fail: true, // https://git.coolaj86.com/coolaj86/request.js/issues/2
json: true,
})
.then(failOnBadStatus)
.then(function (resp) {
var ver = resp.body.versions.stable;
var dl = (
resp.body.bottle.stable.files.high_sierra ||
resp.body.bottle.stable.files.catalina
).url.replace(new RegExp(ver.replace(/\./g, '\\.'), 'g'), '{{ v }}');
return [
{
version: ver,
download: dl.replace(/{{ v }}/g, ver),
},
].concat(
resp.body.versioned_formulae.map(function (f) {
var ver = f.replace(/.*@/, '');
return {
version: ver,
download: dl,
};
}),
);
})
.catch(function (err) {
console.error('Error fetching MariaDB versions (brew)');
console.error(err);
return [];
});
}
function failOnBadStatus(resp) {
if (resp.statusCode >= 400) {
var err = new Error('Non-successful status code: ' + resp.statusCode);
err.code = 'ESTATUS';
err.response = resp;
throw err;
}
return resp;
}
module.exports = getAllReleases;
if (module === require.main) {
getAllReleases(require('@root/request'), 'mariadb').then(function (all) {
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -1,218 +0,0 @@
'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

@@ -1,39 +0,0 @@
'use strict';
var ghRelease = require('./github.js');
/**
* 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>}
*/
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;
},
);
}
module.exports = getAllReleases;
if (module === require.main) {
getAllReleases(
require('@root/request'),
'coolaj86',
'go-pathman',
'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));
},
);
}

View File

@@ -1,133 +0,0 @@
'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

@@ -1,102 +0,0 @@
'use strict';
require('dotenv').config();
/**
* Lists GitHub Releases (w/ uploaded assets)
*
* @param request
* @param {string} owner
* @param {string} repo
* @returns {PromiseLike<any> | Promise<any>}
*/
async function getAllReleases(
request,
owner,
repo,
baseurl = 'https://api.github.com',
) {
if (!owner) {
throw new Error('missing owner for repo');
}
if (!repo) {
throw new Error('missing repo name');
}
var 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);
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');
}
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;
}
module.exports = getAllReleases;
if (module === require.main) {
getAllReleases(require('@root/request'), 'BurntSushi', 'ripgrep').then(
function (all) {
console.info(JSON.stringify(all, null, 2));
},
);
}

View File

@@ -23,7 +23,7 @@ install:
```text
~/.config/envman/PATH.env
~/.local/bin/foo
~/.local/opt/foo
~/.local/opt/foo/
```
## Cheat Sheet

View File

@@ -19,13 +19,13 @@ 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")) {
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-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing foobar"
# TODO: create package-specific temp directory

3
_example/releases.conf Normal file
View File

@@ -0,0 +1,3 @@
# Example releases.conf — uses ripgrep as a sample project.
# Copy this file into your package directory and adjust.
github_releases = BurntSushi/ripgrep

View File

@@ -1,28 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'BurntSushi';
var repo = 'ripgrep';
/******************************************************************************/
/** Note: Delete this Comment! **/
/** **/
/** Need a an example that filters out miscellaneous release files? **/
/** See `deno`, `gitea`, or `caddy` **/
/** **/
/******************************************************************************/
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
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

@@ -4,7 +4,6 @@
//var pkg = require('../package.json');
var os = require('os');
//var request = require('@root/request');
//var promisify = require('util').promisify;
//var exec = promisify(require('child_process').exec);
var exec = require('child_process').exec;

View File

@@ -15,8 +15,8 @@ foreach ($my_dir in $my_dirs) {
$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)) {
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
}
@@ -31,7 +31,7 @@ foreach ($my_dir in $my_dirs) {
$my_new_file | Set-Content -Path $my_ps1
$my_new_file = $my_new_file + "`n"
IF ($text -ne $my_new_file) {
if ($text -ne $my_new_file) {
$my_status = 1
}
}

View File

@@ -15,8 +15,8 @@ foreach ($my_dir in $my_dirs) {
$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)) {
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
}
@@ -39,7 +39,7 @@ foreach ($my_dir in $my_dirs) {
$my_new_file | Set-Content -Path $my_ps1
$my_new_file = $my_new_file + "`n"
IF ($my_old_file -ne $my_new_file) {
if ($my_old_file -ne $my_new_file) {
$my_status = 1
}
}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env pwsh
IF (!(Test-Path -Path "$Env:USERPROFILE\.vim\pack\plugins\start")) {
if (!(Test-Path -Path "$Env:USERPROFILE\.vim\pack\plugins\start")) {
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

View File

@@ -0,0 +1,72 @@
'use strict';
let Path = require('node:path');
let BuildsCacher = require('./builds-cacher.js');
// let Parallel = require('./parallel.js');
var INSTALLERS_DIR = Path.join(__dirname, '..');
var CACHE_DIR = Path.join(__dirname, '../_cache');
async function main() {
let bc = BuildsCacher.create({
caches: CACHE_DIR,
installers: INSTALLERS_DIR,
});
// let dirs = await bc.getProjectsByType();
// let projNames = Object.keys(dirs.valid);
let lastUpdate;
let projName = 'k9s';
{
let packages = await bc.getPackages({
//Releases: Releases,
name: projName,
date: new Date(),
});
lastUpdate = packages.updated;
console.info(
`Last update for '${projName}': ${packages.updated} (${packages.releases.length} assets)`,
);
}
console.info('Waiting 5s');
{
setTimeout(async function () {
let packages = await bc.getPackages({
//Releases: Releases,
name: projName,
date: new Date(),
});
console.info(
`Last update for '${projName}': ${packages.updated} (${packages.releases.length} assets)`,
);
if (lastUpdate < packages.updated) {
console.info(`PASS`);
} else {
console.info(`MAYBE fail`);
}
}, 5 * 1000);
}
//let parallel = 25;
//await Parallel.run(parallel, projNames, getAll);
//async function getAll(name) {
// void (await bc.getPackages({
// //Releases: Releases,
// name: name,
// date: new Date(),
// }));
//}
}
main()
.then(function () {
console.log('Done');
})
.catch(function (e) {
console.error(e.stack || e);
process.exit(1);
});

View File

@@ -3,15 +3,16 @@
var BuildsCacher = module.exports;
let Fs = require('node:fs/promises');
let Os = require('node:os');
let Path = require('node:path');
let LEGACY_CACHE_DIR = Path.join(Os.homedir(), '.cache/webi/legacy');
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 ALIAS_RE = /^alias: ([\w.-]+)$/m;
var LEGACY_ARCH_MAP = {
'*': 'ANYARCH',
@@ -128,75 +129,27 @@ async function readFirstBytes(path) {
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 }) {
BuildsCacher.create = function ({ ALL_TERMS, installers }) {
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.ALL_TERMS = ALL_TERMS;
bc.orphanTerms = Object.assign({}, bc.ALL_TERMS);
bc.unknownTerms = {};
bc.usedTerms = {};
bc.formats = [];
bc._triplets = {};
bc._targetsByBuildIdCache = {};
bc._caches = {};
bc._staleAge = 15 * 60 * 1000;
bc._allFormats = {};
bc._allTriplets = {};
// Per-name lock: serializes cold-cache getPackages so concurrent
// callers can't corrupt bc._caches[name] via a transformAndUpdate race.
bc._inflight = {};
for (let term of TERMS_META) {
delete bc.orphanTerms[term];
@@ -215,19 +168,6 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
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;
}
@@ -296,19 +236,16 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
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 };
let cacheFile = `${LEGACY_CACHE_DIR}/${entry.name}.json`;
let hasCacheFile = await Fs.access(cacheFile)
.then(function () {
return true;
})
.catch(function () {
return false;
});
if (!hasCacheFile) {
return { type: 'selfhosted', detail: true };
}
return { type: 'valid', detail: true };
@@ -316,24 +253,40 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
// 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();
bc.getPackages = async function (args) {
let name = args.name;
let warm = bc._caches[name];
if (warm) {
return _doGetPackages(args);
}
let isoDate = date.toISOString();
let yearMonth = isoDate.slice(0, 7);
let dataFile = `${cacheDir}/${yearMonth}/${name}.json`;
let inflight = bc._inflight[name];
if (inflight) {
return inflight;
}
let p = _doGetPackages(args).finally(function () {
delete bc._inflight[name];
});
bc._inflight[name] = p;
return p;
};
// 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;
async function _doGetPackages({ name }) {
let dataFile = `${LEGACY_CACHE_DIR}/${name}.json`;
let tsFile = `${LEGACY_CACHE_DIR}/${name}.updated.txt`;
// let age = now - seconds;
let tsDate;
{
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 ms = seconds * 1000;
tsDate = new Date(ms);
}
let projInfo = bc._caches[name];
@@ -373,276 +326,20 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
}
}
if (!projInfo) {
projInfo = await getLatestBuilds(Releases, installersDir, cacheDir, name);
return meta;
}
let latestProjInfo = await BuildsCacher.transformAndUpdate(
name,
projInfo,
meta,
tsDate,
bc,
);
bc._caches[name] = latestProjInfo;
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;
return latestProjInfo;
}
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):
@@ -733,9 +430,13 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
exts.push('.gz');
exts.push('.sh');
}
exts.push('.git');
let hasGit = formats.includes('git') || formats.includes('.git');
if (hasGit) {
exts.push('.git');
}
// Fallbacks
// (we include everything to bubble an extract error over not found)
exts.push('.app.zip');
exts.push('.dmg');
exts.push('.pkg');
@@ -787,9 +488,8 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
/**
* @param {ProjectInfo} projInfo
* @param {HostTarget} hostTarget
*/
bc.enumerateLatestVersions = function (projInfo, hostTarget) {
bc.enumerateLatestVersions = function (projInfo) {
let lexPrefix = '';
let matchInfo = Lexver.matchSorted(projInfo.lexvers, lexPrefix);
let verInfo = {
@@ -842,29 +542,19 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
return null;
}
for (let _triplet of triplets) {
let targetReleases = projInfo.releasesByTriplet[_triplet];
if (!targetReleases) {
continue;
}
// Version-first iteration, not triplet-first: take the newest
// version even when its only build lives in a fallback triplet
// (e.g. serviceman v1.0.1 only exists at posix_2017-ANYARCH-none).
for (let lexver of projInfo.lexvers) {
let ver = projInfo.lexversMap[lexver] || lexver;
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
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 lexvers) {
let ver = projInfo.lexversMap[matchver] || matchver;
let packages = targetReleases[ver];
//console.log('dbg: packages', packages);
if (!packages) {
continue;
}
@@ -915,22 +605,32 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
return triplets;
}
// Prefer platform-specific matches over ANYOS/ANYARCH fallbacks.
// This ensures e.g. darwin-aarch64-none matches before
// ANYOS-ANYARCH-none (.git source URLs from old releases).
let oses = [];
if (hostTarget.os === 'windows') {
oses = ['ANYOS', 'windows'];
oses = ['windows', 'ANYOS'];
} else if (hostTarget.os === 'android') {
oses = ['ANYOS', 'posix_2017', 'android', 'linux'];
oses = ['android', 'linux', 'posix_2017', 'posix_2024', 'ANYOS'];
} else {
oses = ['ANYOS', 'posix_2017', hostTarget.os];
oses = [hostTarget.os, 'posix_2017', 'posix_2024', 'ANYOS'];
}
let waterfall = HostTargets.WATERFALL[hostTarget.os] || {};
let arches = waterfall[hostTarget.arch] ||
HostTargets.WATERFALL.ANYOS[hostTarget.arch] || [hostTarget.arch];
arches = ['ANYARCH'].concat(arches);
arches = arches.concat(['ANYARCH']);
let libcs = waterfall[hostTarget.libc] ||
HostTargets.WATERFALL.ANYOS[hostTarget.libc] || [hostTarget.libc];
// Extend the glibc-host waterfall: the table only lists [none, libc]
// but Rust projects (bat, rg) and node ship libc='gnu' builds, and
// static musl builds also run on glibc hosts.
if (hostTarget.libc === 'libc' && !libcs.includes('gnu')) {
libcs = ['none', 'gnu', 'musl', 'libc'];
}
for (let os of oses) {
for (let arch of arches) {
for (let libc of libcs) {
@@ -956,3 +656,250 @@ BuildsCacher.create = function ({ ALL_TERMS, installers, caches }) {
return bc;
};
BuildsCacher._classify = function (bc, projInfo, build) {
/* jshint maxcomplexity: 30 */
// Cache entries arrive pre-classified (os/arch/libc/ext set). Skip
// maybeInstallable for those — it false-rejects names ending in a
// version tag (`serviceman-v1.0.1`, `v1.0.1.zip`).
let cacheClassified =
build.os && build.arch && build.libc && build.ext;
if (!cacheClassified) {
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}`;
//console.log(`dbg: buildId`, buildId);
let target = bc._targetsByBuildIdCache[buildId];
if (target) {
Object.assign(build, { target: target, triplet: target.triplet });
return target;
}
let pattern = Triplet.toPattern(projInfo, build);
//console.log(`dbg: pattern`, pattern);
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);
//console.log(`dbg: rawTerms`, rawTerms);
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);
//console.log(`dbg: terms`, terms);
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 (bc.ALL_TERMS[term]) {
delete bc.orphanTerms[term];
bc.usedTerms[term] = true;
continue;
}
bc.unknownTerms[term] = true;
}
// Skip termsToTarget for cache-classified entries: it false-flags
// e.g. .git URLs as os=ANYOS while the cache says os=posix_2017,
// and the mismatch check throws.
target = { triplet: '' };
if (cacheClassified) {
target.os = build.os;
target.arch = build.arch;
target.libc = build.libc;
target.vendor = build.vendor || 'unknown';
target.android = false;
target.unknownTerms = [];
} else {
try {
void Triplet.termsToTarget(target, projInfo, build, terms);
} catch (e) {
console.error(`PACKAGE FORMAT CHANGE for '${projInfo.name}':`);
console.error(e.message);
console.error(build);
return null;
}
}
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 (!bc.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;
};
BuildsCacher.transformAndUpdate = function (name, projInfo, meta, date, bc) {
meta.packages = [];
let updated = date.valueOf();
Object.assign(projInfo, { name, updated }, meta);
for (let build of projInfo.releases) {
let buildTarget = BuildsCacher._classify(bc, 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);
}
BuildsCacher.updateReleasesByTriplet(meta);
BuildsCacher.updateAndSortVersions(projInfo, meta);
Object.assign(projInfo, { name, updated }, meta);
return projInfo;
};
// TODO
// - tag channels
BuildsCacher.updateAndSortVersions = function (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;
});
};
BuildsCacher.updateReleasesByTriplet = function (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);
}
};

View File

@@ -15,19 +15,15 @@ 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 dirs = await bc.getProjectsByType();
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(),
}));

90
_webi/classify-one.js Normal file
View File

@@ -0,0 +1,90 @@
'use strict';
let Fs = require('node:fs/promises');
let Os = require('node:os');
let Path = require('node:path');
let BuildsCacher = require('./builds-cacher.js');
let Triplet = require('./build-classifier/triplet.js');
let LEGACY_CACHE_DIR = Path.join(Os.homedir(), '.cache/webi/legacy');
async function main() {
let projName = process.argv[2];
if (!projName) {
console.error(``);
console.error(`USAGE`);
console.error(``);
console.error(` classify-one <project-name>`);
console.error(``);
console.error(`EXAMPLE`);
console.error(``);
console.error(` classify-one caddy`);
console.error(``);
return;
}
let tsDate = new Date(0);
let meta = {
// version info
versions: [],
lexvers: [],
lexversMap: {},
// culled release assets
packages: [],
releasesByTriplet: {},
// target info
triplets: [],
oses: [],
arches: [],
libcs: [],
formats: [],
};
let dataFile = Path.join(LEGACY_CACHE_DIR, `${projName}.json`);
let json = await Fs.readFile(dataFile, 'utf8');
let projInfo = JSON.parse(json);
// let packages = await Builds.getPackage({ name: projName });
// console.log(packages);
let bc = {};
bc.ALL_TERMS = Triplet.TERMS_PRIMARY_MAP;
bc.orphanTerms = Object.assign({}, bc.ALL_TERMS);
bc.unknownTerms = {};
bc.usedTerms = {};
bc.formats = [];
bc._targetsByBuildIdCache = {};
bc._triplets = {};
let transformed = BuildsCacher.transformAndUpdate(
projName,
projInfo,
meta,
tsDate,
bc,
);
console.log(`[DEBUG] transformed`);
let sample = transformed.packages.slice(0, 20);
console.log('packages:', sample, ':packages');
let firstTriplet = Object.keys(transformed.releasesByTriplet)[0];
let firstVersion = transformed.versions[0];
console.log(
`releasesByTriplet[${firstTriplet}][${firstVersion}]:`,
transformed.releasesByTriplet[firstTriplet]?.[firstVersion],
':releasesByTriplet',
);
console.log('versions:', transformed.versions, ':versions');
console.log('triplets:', transformed.triplets, ':triplets');
console.log('oses:', transformed.oses, ':oses');
console.log('arches:', transformed.arches, ':arches');
console.log('libcs:', transformed.libcs, ':libcs');
console.log('formats:', transformed.formats, ':formats');
console.log(Object.keys(transformed));
}
main().catch(function (err) {
console.error('Error:');
console.error(err);
});

View File

@@ -6,7 +6,7 @@
############################################################
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" }
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

@@ -267,12 +267,23 @@ webi_bootstrap() { (
fn_checksum() {
a_filepath="${1}"
cmd_shasum='sha1sum'
if command -v shasum > /dev/null; then
cmd_shasum='shasum'
if command -v sha1sum > /dev/null; then
sha1sum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
return 0
fi
$cmd_shasum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
if command -v shasum > /dev/null; then
shasum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
return 0
fi
if command -v sha1 > /dev/null; then
sha1 "${a_filepath}" | cut -d'=' -f2 | cut -c 2-9
return 0
fi
echo >&2 " warn: no sha1 sum program"
date '+%F %H:%M'
}
##############################################

View File

@@ -16,7 +16,7 @@ var BAD_SH_RE = /[<>'"`$\\]/;
Installers.renderBash = async function (
pkgdir,
rel,
{ baseurl, pkg, tag, ver, os = '', arch = '', libc = '', formats, latest },
{ baseurl, pkg, tag, ver, os = '', arch = '', libc = '', formats },
) {
if (!Array.isArray(formats)) {
formats = [];
@@ -93,17 +93,18 @@ Installers.renderBash = async function (
['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_EXT', rel.ext],
['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_STABLE', rel.stable],
['PKG_LATEST', rel.latest],
['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) {

View File

@@ -139,8 +139,6 @@ async function main() {
console.info('');
}
bc.freshenRandomPackage(600 * 1000);
let rows = [];
let triples = [];
let valids = Object.keys(dirs.valid);

View File

@@ -1,258 +0,0 @@
'use strict';
// this may need customizations between packages
var osMap = {
macos: /(\b|_)(apple|os(\s_-)?x\b|mac|darwin|iPhone|iOS|iPad)/i,
linux: /(\b|_)(linux)/i,
freebsd: /(\b|_)(freebsd)/i,
windows: /(\b|_)(win|microsoft|msft)/i,
sunos: /(\b|_)(sun)/i,
aix: /(\b|_)(aix)/i,
};
var maps = {
oses: {},
arches: {},
libcs: {},
formats: {},
};
Object.keys(osMap).forEach(function (name) {
maps.oses[name] = true;
});
var formats = ['zip', 'xz', 'tar', 'pkg', 'msi', 'git', 'exe', 'dmg', 'git'];
formats.forEach(function (name) {
maps.formats[name] = true;
});
// evaluation order matters
// (i.e. otherwise x86 and x64 can cross match)
var arches = [
// 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',
];
// Used for detecting system arch from package download url, for example:
//
// https://git.com/org/foo/releases/v0.7.9/foo-aarch64-linux-musl.tar.gz
// https://git.com/org/foo/releases/v0.7.9/foo-arm-linux-musleabihf.tar.gz
// https://git.com/org/foo/releases/v0.7.9/foo-armv7-linux-musleabihf.tar.gz
// https://git.com/org/foo/releases/v0.7.9/foo-x86_64-linux-musl.tar.gz
//
var archMap = {
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,
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,
};
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: {},
libcs: {},
formats: {},
};
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 = '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) {
for (let arch of arches) {
let name = rel.name || rel.download;
let isArch = name.match(archMap[arch]);
if (isArch) {
rel.arch = arch;
break;
}
}
}
if (!rel.arch) {
if ('macos' === rel.os) {
rel.arch = 'amd64';
}
}
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']
// pkg-v1.0.tar => ['tar', '0' ,'pkg-v1']
// pkg-v1.0.zip => ['zip', '0', 'pkg-v1']
var exts = (rel.name || rel.download).split('.');
if (1 === exts.length) {
// for bare releases in the format of foo-linux-amd64
rel.ext = 'exe';
}
exts = exts.reverse().slice(0, 2);
if ('tar' === exts[1]) {
rel.ext = exts.reverse().join('.');
tarExt = 'tar';
} else if ('tgz' === exts[0]) {
rel.ext = 'tar.gz';
tarExt = 'tar';
} else {
rel.ext = exts[0];
}
if (/\-|linux|mac|os[_\-]?x|arm|amd|86|64|mip/i.test(rel.ext)) {
// for bare releases in the format of foo.linux-amd64
rel.ext = 'exe';
}
}
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];
});
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];
});
return all;
}
module.exports = normalize;
module.exports._debug = function (all) {
all = normalize(all);
all.releases = all.releases
.filter(function (r) {
return ['windows', 'macos', 'linux'].includes(r.os) && 'amd64' === r.arch;
})
.slice(0, 10);
return 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

@@ -45,15 +45,15 @@ $TDim = "${Esc}[2m"
$TReset = "${Esc}[0m"
function Invoke-DownloadUrl {
Param (
param (
[string]$URL,
[string]$Params,
[string]$Path,
[switch]$Force
)
IF (Test-Path -Path "$Path") {
IF (-Not $Force.IsPresent) {
if (Test-Path -Path "$Path") {
if (-not $Force.IsPresent) {
Write-Host " ${TDim}Found${TReset} $Path"
return
}
@@ -65,7 +65,7 @@ function Invoke-DownloadUrl {
Write-Host " Downloading ${TDim}from${TReset}"
Write-Host " ${TDim}${URL}${TReset}"
IF ($Params.Length -ne 0) {
if ($Params.Length -ne 0) {
Write-Host " ?$Params"
$URL = "${URL}?${Params}"
}
@@ -80,18 +80,18 @@ 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 "") {
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") {
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
$my_os_arch = (Get-CimInstance -ClassName Win32_OperatingSystem).OSArchitecture
# Using -clike because of the trailing newline
IF ($my_os_arch -clike "ARM 64*") {
if ($my_os_arch -clike "ARM 64*") {
$my_arch = "ARM64"
}
}
@@ -124,7 +124,7 @@ function webi_path_add($pathname) {
$exists_in_path = $true
}
}
if (-Not $exists_in_path) {
if (-not $exists_in_path) {
$all_user_paths = "${pathname};${all_user_paths}".Trim(';')
[Environment]::SetEnvironmentVariable("Path", $all_user_paths, "User")
$null = Sync-EnvPath

View File

@@ -27,6 +27,7 @@ __bootstrap_webi() {
#PKG_LIBCS=
#PKG_FORMATS=
#PKG_LATEST=
#PKG_STABLE=
WEBI_PKG_DOWNLOAD=""
WEBI_DOWNLOAD_DIR="${HOME}/Downloads"
if command -v xdg-user-dir > /dev/null; then
@@ -36,7 +37,7 @@ __bootstrap_webi() {
fi
fi
WEBI_PKG_PATH="${WEBI_DOWNLOAD_DIR}/webi/${PKG_NAME:-error}/${WEBI_VERSION:-latest}"
WEBI_PKG_PATH="${WEBI_DOWNLOAD_DIR}/webi/${PKG_NAME:-error}/${WEBI_VERSION:-stable}"
# get the special formatted version
# (i.e. "go is go1.14" while node is "node v12.10.8")
@@ -127,7 +128,10 @@ __bootstrap_webi() {
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 " Latest Stable: ${PKG_STABLE}"
if test "${PKG_LATEST}" != "${PKG_STABLE}"; then
echo " Next Version: ${PKG_LATEST}"
fi
echo " CPUs: $PKG_ARCHES"
echo " OSes: $PKG_OSES"
echo " libcs: $PKG_LIBCS"
@@ -197,20 +201,32 @@ __bootstrap_webi() {
my_dl_rel="$(
fn_sub_home "${WEBI_PKG_PATH}/${WEBI_PKG_FILE}"
)"
if [ "tar" = "$WEBI_EXT" ]; then
if test "$WEBI_EXT" = "tar.zst"; then
echo " Extracting $(t_path "${my_dl_rel}")"
unzstd -c --keep "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" | tar xf -
elif test "$WEBI_EXT" = "tar.xz"; then
echo " Extracting $(t_path "${my_dl_rel}")"
unxz -c -k "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" | tar xf -
elif test "$WEBI_EXT" = "tar.gz"; then
echo " Extracting $(t_path "${my_dl_rel}")"
tar xzf "${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
elif test "$WEBI_EXT" = "tar.bz2"; then
echo " Extracting $(t_path "${my_dl_rel}")"
tar xjf "${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
elif test "$WEBI_EXT" = "tar"; then
echo " Extracting $(t_path "${my_dl_rel}")"
tar xf "${WEBI_PKG_PATH}/$WEBI_PKG_FILE"
elif [ "zip" = "$WEBI_EXT" ]; then
elif test "$WEBI_EXT" = "zip" || test "$WEBI_EXT" = "app.zip"; then
echo " Extracting $(t_path "${my_dl_rel}")"
unzip "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" > __unzip__.log
elif [ "exe" = "$WEBI_EXT" ]; then
elif test "$WEBI_EXT" = "exe"; 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
elif test "$WEBI_EXT" = "git"; then
echo " Moving $(t_path "${my_dl_rel}")"
mv "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" .
elif [ "xz" = "$WEBI_EXT" ]; then
elif test "$WEBI_EXT" = "xz"; then
echo " Inflating $(t_path "${my_dl_rel}")"
unxz -c "${WEBI_PKG_PATH}/$WEBI_PKG_FILE" > "$(basename "$WEBI_PKG_FILE")"
else
@@ -223,8 +239,6 @@ __bootstrap_webi() {
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)
@@ -387,7 +401,6 @@ __bootstrap_webi() {
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
@@ -396,6 +409,7 @@ __bootstrap_webi() {
echo " Creating$(t_path ' ~/.local/bin')"
mkdir -p "$HOME/.local/bin"
fi
fn_envman_init
##
##
@@ -776,12 +790,23 @@ webi_upgrade() { (
fn_checksum() {
a_filepath="${1}"
cmd_shasum='sha1sum'
if command -v shasum > /dev/null; then
cmd_shasum='shasum'
if command -v sha1sum > /dev/null; then
sha1sum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
return 0
fi
$cmd_shasum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
if command -v shasum > /dev/null; then
shasum "${a_filepath}" | cut -d' ' -f1 | cut -c 1-8
return 0
fi
if command -v sha1 > /dev/null; then
sha1 "${a_filepath}" | cut -d'=' -f2 | cut -c 2-9
return 0
fi
echo >&2 " warn: no sha1 sum program"
date '+%F %H:%M'
}
##############################################

View File

@@ -70,18 +70,20 @@ InstallerServer.helper = async function ({
console.log(`dbg: Get Project Installer Type for '${projectName}':`);
let proj = await Builds.getProjectType(projectName);
console.log(proj);
if (proj.type === 'alias') {
console.log(`dbg: alias`, proj);
projectName = proj.detail;
proj = await Builds.getProjectType(projectName); // an alias should never resolve to an alias
}
console.log(`dbg: proj`, proj);
let validTypes = ['alias', 'selfhosted', 'valid'];
let validTypes = ['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,
@@ -120,8 +122,7 @@ InstallerServer.helper = async function ({
name: projectName,
date: new Date(),
});
let latest = projInfo.versions[0];
Object.assign(tmplParams, { latest });
let latestVersions = Builds.enumerateLatestVersions(projInfo);
//console.log('projInfo', projInfo);
let buildTargetInfo = {
@@ -130,12 +131,27 @@ InstallerServer.helper = async function ({
arches: projInfo.arches,
libcs: projInfo.libcs,
formats: projInfo.formats,
latest: latestVersions.latest,
stable: latestVersions.stable,
};
// TODO .findMatchingPackages() should probably account for this
let hasOs = projInfo.oses.includes(hostTarget.os);
let maybePosix = !hasOs && hostTarget.os !== 'windows';
if (maybePosix) {
let posixes = ['posix_2017', 'posix_2024'];
for (let posixYear of posixes) {
let hasPosix = projInfo.oses.includes(posixYear);
if (hasPosix) {
hasOs = true;
break;
}
}
}
if (!hasOs) {
hasOs = projInfo.oses.includes('ANYOS');
}
if (!hasOs) {
let pkg1 = Object.assign(buildTargetInfo, errPackage);
return [pkg1, tmplParams];

View File

@@ -49,7 +49,7 @@ var baseurl = 'https://webinstall.dev';
var maxLen = 0;
console.info('');
console.info('Has the necessary files?');
['README.md', 'releases.js', 'install.sh', 'install.ps1']
['README.md', 'install.sh', 'install.ps1']
.map(function (node) {
maxLen = Math.max(maxLen, node.length);
return node;

View File

@@ -2,72 +2,30 @@
var Releases = module.exports;
var Fs = require('node:fs/promises');
var Os = require('node:os');
var path = require('path');
var request = require('@root/request');
var _normalize = require('./normalize.js');
var cache = {};
//var staleAge = 5 * 1000;
//var expiredAge = 15 * 1000;
var staleAge = 5 * 60 * 1000;
var expiredAge = 15 * 60 * 1000;
let installerDir = path.join(__dirname, '..');
var LEGACY_CACHE_DIR = path.join(Os.homedir(), '.cache/webi/legacy');
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
// Sort releases by ext preference and libc within the same version.
// The cache is already sorted by version (stable before beta, newest first),
// so we only re-order within the same version string.
function createFormatsSorter(formats) {
return function sortByVerExt(a, b) {
function lexver(semver) {
// v1.20.156 => 00001.00020.00156.zzzzz
// TODO BUG: v1.20.156-rc2 => 00001.00020.00156.rc2zz
var parts = semver.split(/[+\.\-]/g);
while (parts.length < 4) {
parts.push('');
}
return parts
.map(function (num, i) {
if (3 === i) {
return num.toString().padEnd(10, 'z');
}
return num.toString().padStart(10, '0');
})
.join('.');
}
var aver = lexver(a.version);
var bver = lexver(b.version);
if (aver > bver) {
//console.log(aver, '>', bver);
return -1;
}
if (aver < bver) {
//console.log(aver, '<', bver);
return 1;
return function sortByExtLibc(a, b) {
if (a.version !== b.version) {
// Array.sort is stable (V8, ES2019), so returning 0 across
// versions preserves the cache's pre-sorted version-desc order.
return 0;
}
var aExtPri = formats.indexOf(a.ext.replace(/tar\..*/, 'tar'));
var bExtPri = formats.indexOf(b.ext.replace(/tar\..*/, 'tar'));
if (aExtPri > bExtPri) {
//console.log(a.ext, aExtPri, '>', b.ext, bExtPri);
return -1;
}
if (aExtPri < bExtPri) {
//console.log(a.ext, aExtPri, '<', b.ext, bExtPri);
return 1;
}
@@ -84,99 +42,39 @@ function createFormatsSorter(formats) {
}
async function getCachedReleases(pkg) {
// returns { download: '<template string>', releases: [{ version, date, os, arch, lts, channel, download}] }
// returns { download: '', releases: [{ version, date, os, arch, lts, channel, download}] }
async function chainCachePromise(fn) {
cache[pkg].promise = cache[pkg].promise.then(fn);
return cache[pkg].promise;
if (cache[pkg]) {
return cache[pkg];
}
async function sleep(ms) {
return await new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
});
}
let dataFile = `${LEGACY_CACHE_DIR}/${pkg}.json`;
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;
let json = await Fs.readFile(dataFile, 'utf8').catch(function (err) {
if (err.code === 'ENOENT') {
return null;
}
throw err;
});
//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 (!json) {
let empty = { download: '', releases: [] };
cache[pkg] = empty;
return empty;
}
if (!cache[pkg]) {
cache[pkg] = {
updatedAt: 0,
all: { download: '', releases: [] },
promise: Promise.resolve(),
};
let all;
try {
all = JSON.parse(json);
} catch (e) {
console.error(`error: ${dataFile}:\n\t${e.message}`);
let empty = { download: '', releases: [] };
cache[pkg] = empty;
return empty;
}
var bgRenewal;
var age = Date.now() - cache[pkg].updatedAt;
var fresh = age < staleAge;
if (!fresh) {
bgRenewal = chainCachePromise(putCache);
}
var tooStale = age > expiredAge;
if (!tooStale) {
return await cache[pkg].all;
}
return await Promise.race([
bgRenewal,
sleep(5000).then(function () {
return cache[pkg].all;
}),
]);
cache[pkg] = all;
return all;
}
async function filterReleases(
@@ -187,15 +85,22 @@ async function filterReleases(
// sort the most compatible format first
// (i.e. so that we don't do .pkg on linux except on purpose)
var rformats = formats.slice(0).reverse();
var sortByVerExt = createFormatsSorter(rformats);
var sortByExtLibc = createFormatsSorter(rformats);
var reVer = new RegExp('^' + ver + '\\b');
function selectMatches(rel) {
/* jshint maxcomplexity: 25 */
if (os) {
if (rel.os !== '*') {
if (rel.os !== os) {
return false;
}
// '*' = any OS (matches anything, including windows).
// 'posix' / 'posix_20xx' = any POSIX OS (matches linux, macos,
// freebsd, etc., but NOT windows).
let isPosix = rel.os === 'posix' || rel.os.startsWith('posix_20');
let osMatches =
rel.os === '*' ||
rel.os === os ||
(isPosix && os !== 'windows');
if (!osMatches) {
return false;
}
}
@@ -207,7 +112,10 @@ async function filterReleases(
}
}
if (rel.libc !== 'none') {
// libc='libc' is serve-releases.js's default when the caller
// didn't pin one — treat it as 'no preference', not a filter.
let isMeaningfulLibc = libc && libc !== 'libc' && rel.libc !== 'none';
if (isMeaningfulLibc) {
let releaseRequiresMusl = rel.libc === 'musl';
// goal: handle non-glibc (Alpine / Docker / musl)
let osHasMusl = libc === 'musl';
@@ -254,7 +162,7 @@ async function filterReleases(
return true;
}
var sortedRels = all.releases.filter(selectMatches).sort(sortByVerExt);
var sortedRels = all.releases.filter(selectMatches).sort(sortByExtLibc);
//console.log(sortedRels.slice(0, 4));
return sortedRels.slice(0, limit || 1000);
@@ -413,8 +321,8 @@ Releases.getReleases = function ({
};
if (require.main === module) {
return module
.exports({
return Releases
.getReleases({
pkg: 'node',
ver: '',
os: 'macos',

View File

@@ -72,8 +72,8 @@ gc "feat: new feature"
### Common aliases
Use *alias*es to make other tools you find around webi even _more_ convenient
⚡️ (and powerful 💪).
Use *alias*es to make other tools you find around webi even _more_ convenient ⚡️
(and powerful 💪).
```sh
aliasman curl 'curlie'

2
aliasman/releases.conf Normal file
View File

@@ -0,0 +1,2 @@
github_sources = BeyondCodeBootcamp/aliasman
git_url = https://github.com/BeyondCodeBootcamp/aliasman.git

View File

@@ -1,30 +0,0 @@
'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

@@ -19,13 +19,13 @@ 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")) {
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-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing archiver"
# TODO: create package-specific temp directory

1
arc/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = mholt/archiver

View File

@@ -1,21 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'mholt';
var repo = 'archiver';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
all._names = ['archiver', 'arc'];
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

@@ -1,5 +1,5 @@
#!/bin/pwsh
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" }
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

@@ -20,7 +20,7 @@ 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")) {
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
@@ -29,7 +29,7 @@ IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
& Move-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing AtomicParsley"
# TODO: create package-specific temp directory

View File

@@ -0,0 +1 @@
github_releases = wez/atomicparsley

View File

@@ -1,79 +0,0 @@
'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

@@ -20,13 +20,13 @@ 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
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-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing awless"
# TODO: create package-specific temp directory

1
awless/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = wallix/awless

View File

@@ -1,22 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'wallix';
var repo = 'awless';
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);
});
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));
});
}

View File

@@ -3,7 +3,7 @@
$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")) {
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-Item "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part" "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
@@ -11,11 +11,11 @@ IF (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
# Fetch MSVC Runtime
Write-Output "Checking for MSVC Runtime..."
IF (-not (Test-Path "\Windows\System32\vcruntime140.dll")) {
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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\.local\bin\$VERNAME")) {
Write-Output "Installing $Env:PKG_NAME"
# TODO: temp directory

1
bat/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = sharkdp/bat

View File

@@ -1,20 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'sharkdp';
var repo = 'bat';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
all.releases = all.releases.slice(0, 10);
//console.info(JSON.stringify(all));
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -6,15 +6,23 @@ 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 \
echo "Checking for serviceman..."
~/.local/bin/webi serviceman
if ! command -v serviceman > /dev/null; then
export PATH="$HOME/.local/bin:$PATH"
fi
serviceman --version
serviceman add --agent \
--workdir ~/.local/opt/brew/ \
--name sh.brew.updater -- \
~/.local/bin/brew-update-hourly
); }
if main; then
exit 0
if ! main; then
exit 1
fi
exit 0
#~/.local/bin/brew-updater
#!/bin/sh

View File

@@ -132,9 +132,7 @@ file)
```
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 -- \
serviceman add --name 'my-project' --daemon -- \
bun run ./my-project.js
```
4. Restart the logging service
@@ -155,6 +153,6 @@ For **macOS**:
```
3. Add your project to the system launcher, running as the current user
```sh
serviceman add --path="$PATH" --user --name my-project -- \
serviceman add --agent --name 'my-project' -- \
bun run ./my-project.js
```

5
bun/releases.conf Normal file
View File

@@ -0,0 +1,5 @@
github_releases = oven-sh/bun
tag_prefix = bun-
default_x86_64 = x86_64_v3
x86_64_v2 = baseline
variants = profile

View File

@@ -1,39 +0,0 @@
'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));
});
}

View File

@@ -819,10 +819,10 @@ To avoid the nitty-gritty details of `launchd` plist files, you can use
2. Use Serviceman to create a _launchd_ plist file
```sh
my_username="$( id -u -n )"
my_username="$(id -u -n)"
serviceman add --user --name caddy -- \
caddy run --config ./Caddyfile --envfile ~/.config/caddy/env
serviceman add --agent --name 'caddy' --workdir ./ -- \
caddy run --envfile ~/.config/caddy/env --config ./Caddyfile --adapter caddyfile
```
(this will create `~/Library/LaunchAgents/caddy.plist`)
@@ -837,8 +837,8 @@ This process creates a _User-Level_ service in `~/Library/LaunchAgents`. To
create a _System-Level_ service in `/Library/LaunchDaemons/` instead:
```sh
sudo serviceman add --system --name caddy -- \
caddy run --config ./Caddyfile --envfile ~/.config/caddy/env
serviceman add --name 'caddy' --workdir ./ --daemon -- \
caddy run --envfile ~/.config/caddy/env --config ./Caddyfile --adapter caddyfile
```
### How to run Caddy as a Windows Service
@@ -856,7 +856,7 @@ sudo serviceman add --system --name caddy -- \
3. Create a **Startup Registry Entry** with Serviceman.
```sh
serviceman.exe add --name caddy -- \
caddy run --config ./Caddyfile --envfile ~/.config/caddy/env
caddy run --envfile ~/.config/caddy/env --config ./Caddyfile --adapter caddyfile
```
4. You can manage the service directly with Serviceman. For example:
```sh
@@ -901,10 +901,8 @@ See the notes below to run as a **User Service** or use the JSON Config.
```
4. Use Serviceman to create a _systemd_ config file.
```sh
my_username="$( id -u -n )"
sudo env PATH="$PATH" \
serviceman add --system --username "${my_username}" --name caddy -- \
caddy run --config ./Caddyfile --envfile ~/.config/caddy/env
serviceman add --name 'caddy' --daemon -- \
caddy run --envfile ~/.config/caddy/env --config ./Caddyfile --adapter caddyfile
```
(this will create `/etc/systemd/system/caddy.service`)
5. Manage the service with `systemctl` and `journalctl`:
@@ -915,10 +913,10 @@ See the notes below to run as a **User Service** or use the JSON Config.
To create a **User Service** instead:
- don't use `sudo`, but do use `--user` when running `serviceman`:
- use `--agent` when running `serviceman`:
```sh
serviceman add --user --name caddy -- \
caddy run --config ./Caddyfile --envfile ~/.config/caddy/env
serviceman add --agent --name caddy -- \
caddy run --envfile ~/.config/caddy/env --config ./Caddyfile --adapter caddyfile
```
(this will create `~/.config/systemd/user/`)
- user the `--user` flag to manage services and logs:
@@ -1183,7 +1181,8 @@ To prevent search engine and browser confusion
- _DO NOT_ prevent crawling via `robots.txt` \
(counter-intuitive, but pages _must_ be crawled for links to _NOT_ be indexed)
- _all_ domains using public TLS certs _will_ be indexed by default \
(they are all linked to and crawled from various Certificate Transparency reports)
(they are all linked to and crawled from various Certificate Transparency
reports)
- follow these guidelines even if the dev sites use HTTP Basic Auth
```Caddyfile
@@ -1363,19 +1362,13 @@ See also: <https://caddyserver.com/docs/running>
2. Generate the `service` file: \
- JSON Config
```sh
my_app_user="$( id -u -n )"
sudo env PATH="${PATH}" \
serviceman add --system --cap-net-bind \
--username "${my_app_user}" --name caddy -- \
caddy run --resume --envfile ./caddy.env
serviceman add --name 'caddy' --daemon -- \
caddy run --resume --envfile ./caddy.env
```
- Caddyfile
```sh
my_app_user="$( id -u -n )"
sudo env PATH="${PATH}" \
serviceman add --system --cap-net-bind \
--username "${my_app_user}" --name caddy -- \
caddy run --config ./Caddyfile --envfile ./caddy.env
serviceman add --name 'caddy' --daemon -- \
caddy run --config ./Caddyfile --envfile ./caddy.env
```
3. Reload `systemd` config files, the logging service (it may not be started on
a new VPS), and caddy

View File

@@ -20,13 +20,13 @@ 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
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-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing caddy"
# TODO: create package-specific temp directory

1
caddy/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = caddyserver/caddy

View File

@@ -1,27 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'caddyserver';
var repo = 'caddy';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
// remove checksums and .deb
all.releases = all.releases.filter(function (rel) {
let isOneOffAsset = rel.download.includes('buildable-artifact');
if (isOneOffAsset) {
return false;
}
return true;
});
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));
});
}

View File

@@ -19,13 +19,13 @@ 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")) {
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-Item "$pkg_download.part" "$pkg_download"
}
IF (!(Test-Path -Path "$pkg_src_cmd")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing chromedriver"
# TODO: create package-specific temp directory

View File

@@ -0,0 +1 @@
source = chromedist

View File

@@ -1,84 +0,0 @@
'use strict';
// See <https://googlechromelabs.github.io/chrome-for-testing/>
var releaseApiUrl =
'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json';
// {
// "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: builds,
};
return all;
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
// just select the latest 5 for demonstration
all.releases = all.releases.slice(-20);
console.info(JSON.stringify(all, null, 2));
});
}

47
cilium/README.md Normal file
View File

@@ -0,0 +1,47 @@
---
title: cilium
homepage: https://github.com/cilium/cilium-cli
tagline: |
cilium: manage & troubleshoot Kubernetes clusters running Cilium
---
To update or switch versions, run `webi cilium@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/cilium
~/.local/opt/cilium/
```
## Cheat Sheet
> Cilium is an open source, cloud native solution for providing, securing, and
> observing network connectivity between workloads, fueled by the revolutionary
> Kernel technology eBPF.
Quick Start User Guide:
<https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/#k8s-install-quick>
To install the default version of the Cilium image:
```sh
cilium install
```
To upgrade to a specific version of the Cilium image:
```sh
cilium upgrade --version v1.15.3
```
To check the status of the current Cilium deployment:
```sh
cilium status
```

45
cilium/install.ps1 Normal file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env pwsh
##################
# Install cilium #
##################
$pkg_cmd_name = "cilium"
$pkg_dst_cmd = "$Env:USERPROFILE\.local\bin\cilium.exe"
$pkg_dst = "$pkg_dst_cmd"
$pkg_src_cmd = "$Env:USERPROFILE\.local\opt\cilium-v$Env:WEBI_VERSION\bin\cilium.exe"
$pkg_src_bin = "$Env:USERPROFILE\.local\opt\cilium-v$Env:WEBI_VERSION\bin"
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\cilium-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"
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading cilium 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 cilium"
Push-Location .local\tmp
Remove-Item -Path ".\cilium-v*" -Recurse -ErrorAction Ignore
Remove-Item -Path ".\cilium.exe" -Recurse -ErrorAction Ignore
Write-Output "Unpacking $pkg_download"
& tar xf "$pkg_download"
Write-Output "Install Location: $pkg_src_cmd"
New-Item "$pkg_src_bin" -ItemType Directory -Force
Move-Item -Path ".\cilium-*\cilium.exe" -Destination "$pkg_src_bin"
Pop-Location
}
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse

39
cilium/install.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
__init_cilium() {
set -e
set -u
##################
# Install cilium #
##################
pkg_cmd_name="cilium"
pkg_dst_cmd="$HOME/.local/bin/cilium"
pkg_dst="$pkg_dst_cmd"
pkg_src_cmd="$HOME/.local/opt/cilium-v$WEBI_VERSION/bin/cilium"
pkg_src_dir="$HOME/.local/opt/cilium-v$WEBI_VERSION"
pkg_src="$pkg_src_cmd"
WEBI_SINGLE=true
# pkg_install must be defined by every package
pkg_install() {
# ~/.local/opt/cilium-v0.16.16/bin
mkdir -p "$(dirname "${pkg_src_cmd}")"
# mv ./hugo ~/.local/opt/cilium-v0.16.16/bin/
mv ./cilium "${pkg_src_cmd}"
}
pkg_get_current_version() {
cilium version 2> /dev/null |
head -n 1 |
cut -d ' ' -f 2
}
}
__init_cilium

1
cilium/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = cilium/cilium-cli

View File

@@ -20,13 +20,13 @@ 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")) {
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")) {
if (!(Test-Path -Path "$pkg_src_dir")) {
Write-Output "Installing cmake"
# TODO: create package-specific temp directory

1
cmake/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = Kitware/CMake

View File

@@ -1,53 +0,0 @@
'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

@@ -0,0 +1,55 @@
---
title: XCode Command Line Tools
homepage: https://webinstall.dev/commandlinetools
tagline: |
The XCode Command Line Tools include git, swift, make, clang, and other developer tools
---
## Cheat Sheet
> The developer tools provided by Apple for macOS.
- git
- swift
- make
- clang
- etc
This is also part of [webi-essentials](../webi-essentials/).
## Table of Contents
- Files
- Manual Install
- macOS
- Linux
- Alpine
- Windows
### Files
These are the files / directories that are created and/or modified with this
install:
```sh
/Library/Developer/CommandLineTools/
```
### How to Install Manually
It's very easy to start the installer:
```sh
xcode-select --install
```
The trick is to also have a mechanism to know when it has finished:
```sh
while ! test -x /Library/Developer/CommandLineTools/usr/bin/git ||
! test -x /Library/Developer/CommandLineTools/usr/bin/make; do
sleep 0.25
done
echo "Command Line Tools Installed"
```

View File

@@ -0,0 +1,62 @@
#!/bin/sh
set -e
set -u
fn_install_xcode_commandlinetools() { (
b_os="$(uname -s)"
if test "${b_os}" != 'Darwin'; then
echo >&2 'XCode Command Line Tools are for macOS only'
return 1
fi
# streamline the output to be pretty
fn_check_pkg '/Library/Developer/CommandLineTools/usr/bin/clang' 'clang'
fn_check_pkg '/Library/Developer/CommandLineTools/usr/bin/git' 'git'
fn_check_pkg '/Library/Developer/CommandLineTools/usr/bin/make' 'make'
echo >&2 ""
# git
if xcode-select -p > /dev/null 2> /dev/null; then
echo ""
return 0
fi
cmd_xcode_cli_tools_install="xcode-select --install"
echo " Running $(t_cmd "${cmd_xcode_cli_tools_install}")"
$cmd_xcode_cli_tools_install 2> /dev/null
echo ""
echo ">>> $(t_attn 'ACTION REQUIRED') <<<"
echo ""
echo " $(t_attn "Click") '$(t_bold 'Install')' $(t_attn "in the pop-up")"
echo " (it may appear $(t_em 'under') this window)"
echo ""
echo "^^^ $(t_attn 'ACTION REQUIRED') ^^^"
echo ""
printf " waiting %s to finish installing Command Line Developer Tools ..." "$(t_em 'for you')"
while ! test -x /Library/Developer/CommandLineTools/usr/bin/git ||
! test -x /Library/Developer/CommandLineTools/usr/bin/make; do
sleep 0.25
done
echo " $(t_info 'OK')"
echo " Installed to $(t_path '/Library/Developer/CommandLineTools/')"
sleep 1
); }
fn_check_pkg() { (
a_pkg="${1}"
a_pkgname="${2:-$a_pkg}"
printf >&2 ' %s %s %s' \
"$(t_dim "Checking for")" \
"$(t_pkg "${a_pkgname}")" \
"$(t_dim "...")"
if command -v "${a_pkg}" > /dev/null; then
echo >&2 " $(t_dim 'OK')"
return 0
fi
echo >&2 ' missing'
); }
fn_install_xcode_commandlinetools

View File

@@ -3,13 +3,13 @@
$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")) {
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-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")) {
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

1
comrak/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = kivikakk/comrak

View File

@@ -1,40 +0,0 @@
'use strict';
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;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
all.releases = all.releases.slice(0, 10);
//console.info(JSON.stringify(all));
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -20,13 +20,13 @@ 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")) {
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")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing crabz"
# TODO: create package-specific temp directory

1
crabz/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = sstadick/crabz

View File

@@ -1,31 +0,0 @@
'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

@@ -3,13 +3,13 @@
$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")) {
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-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\bin\$VERNAME")) {
if (!(Test-Path -Path "$Env:USERPROFILE\.local\bin\$VERNAME")) {
Write-Output "Installing $Env:PKG_NAME"
# TODO: temp directory

1
curlie/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = rs/curlie

View File

@@ -1,21 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'rs';
var repo = 'curlie';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
all._names = ['curlie', 'curl-httpie'];
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
all.releases = all.releases.slice(0, 10);
//console.info(JSON.stringify(all));
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -100,8 +100,7 @@ mkdir -p ~/.dashcore/wallets/
mkdir -p /mnt/slc1_vol_100g/dashcore/_data
mkdir -p /mnt/slc1_vol_100g/dashcore/_caches
sudo env PATH="$PATH" serviceman add \
--system --user "$my_user" --path "$PATH" --name dashd --force -- \
serviceman add --name 'dashd' --daemon -- \
dashd \
-usehd \
-conf="$HOME/.dashcore/dash.conf" \

View File

@@ -1,7 +1,14 @@
# for exposing RPCs, building APIs
txindex=1
addressindex=1
timestampindex=1
spentindex=1
# listen as server (explicit default)
listen=1
# because its already run as a service (systemd, openrc)
daemon=0
# for evonodes
#server=1
[main]
rpcuser=RPCUSER_MAIN

View File

@@ -17,7 +17,7 @@ fn_usage() { (
); }
fn_datadir_help() { (
my_vol="${1:-}"
my_vol="${1}"
my_user="$(
id -n -u
)"
@@ -25,6 +25,7 @@ fn_datadir_help() { (
id -n -g
)"
my_mount="$(dirname "${my_vol}")"
echo >&2 ""
echo >&2 "ERROR"
echo >&2 " '${my_vol}' is not writable"
@@ -32,8 +33,8 @@ fn_datadir_help() { (
echo >&2 "SOLUTION"
echo >&2 " 1. Mount a large (50gb+) volume"
echo >&2 ""
echo >&2 " sudo mkdir -p /mnt/EXAMPLE"
echo >&2 " sudo mount /dev/sdx1 /mnt/EXAMPLE"
echo >&2 " sudo mkdir -p ${my_mount}"
echo >&2 " sudo mount /dev/sdx1 ${my_mount}"
echo >&2 ""
echo >&2 " 2. Create a 'dashcore' inside of it"
echo >&2 ""
@@ -83,20 +84,8 @@ fn_srv_install() { (
my_name="dashd-${my_netname}"
fi
my_system_args=""
my_kernel="$(
uname -s
)"
if test "Darwin" != "${my_kernel}"; then
my_user="$(
id -u -n
)"
my_system_args="--system --username ${my_user}"
fi
# shellcheck disable=SC2016,SC1090
echo 'sudo env PATH="$PATH"' \
"serviceman add ${my_system_args} --path \"\$PATH\" --name \"${my_name}\" --force --" \
echo "serviceman add --name \"${my_name}\" --" \
"dashd " \
"${my_net_flag}" \
-usehd \
@@ -106,16 +95,16 @@ fn_srv_install() { (
"-datadir=\"${my_datadir}\"" \
"-blocksdir=\"${my_blocksdir}\""
echo ""
echo "Installing latest 'serviceman'..."
echo ""
"$HOME/.local/bin/webi" serviceman > /dev/null
if ! command -v serviceman > /dev/null; then
echo ""
echo "Installing 'serviceman'..."
echo ""
{
curl -fsSL "${WEBI_HOST}/serviceman" | sh
} > /dev/null
# shellcheck disable=SC1090
. ~/.config/envman/PATH.env || true
export PATH="$HOME/.local/bin:$PATH"
fi
serviceman --version
if ! command -v dashd > /dev/null; then
export PATH="$HOME/.local/opt/dashcore/bin:$PATH"
fi
mkdir -p "$HOME/.dashcore/wallets/"
@@ -130,8 +119,7 @@ fn_srv_install() { (
cd "${my_vol}" || return 1
# leave options unquoted so they're interpreted separately
# shellcheck disable=SC2086
sudo env PATH="${PATH}" \
serviceman add ${my_system_args} --path "${PATH}" --name "${my_name}" --force -- \
serviceman add --name "${my_name}" -- \
dashd \
${my_net_flag} \
-usehd \

View File

@@ -5,32 +5,38 @@ set -u
__install_dashcore_utils() {
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dash-qt-hd" \
"$HOME/.local/bin/dash-qt-hd"
"$HOME/.local/bin/dash-qt-hd" \
"dash-qt-hd"
chmod a+x "$HOME/.local/bin/dash-qt-hd"
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dash-qt-testnet" \
"$HOME/.local/bin/dash-qt-testnet"
"$HOME/.local/bin/dash-qt-testnet" \
"dash-qt-testnet"
chmod a+x "$HOME/.local/bin/dash-qt-testnet"
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dashd-hd" \
"$HOME/.local/bin/dashd-hd"
"$HOME/.local/bin/dashd-hd" \
"dashd-hd"
chmod a+x "$HOME/.local/bin/dashd-hd"
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dashd-testnet" \
"$HOME/.local/bin/dashd-testnet"
"$HOME/.local/bin/dashd-testnet" \
"dashd-testnet"
chmod a+x "$HOME/.local/bin/dashd-testnet"
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dashd-hd-service-install" \
"$HOME/.local/bin/dashd-hd-service-install"
"$HOME/.local/bin/dashd-hd-service-install" \
"dashd-hd-service-install"
chmod a+x "$HOME/.local/bin/dashd-hd-service-install"
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dashd-testnet-service-install" \
"$HOME/.local/bin/dashd-testnet-service-install"
"$HOME/.local/bin/dashd-testnet-service-install" \
"dashd-testnet-service-install"
chmod a+x "$HOME/.local/bin/dashd-testnet-service-install"
if ! test -e "${HOME}/.dashcore"; then
@@ -44,7 +50,8 @@ __install_dashcore_utils() {
webi_download \
"$WEBI_HOST/packages/dashcore-utils/dash.example.conf" \
"$HOME/.dashcore/dash.example.conf"
"$HOME/.dashcore/dash.example.conf" \
"dash.example.conf"
if ! grep -q rpcuser ~/.dashcore/dash.conf; then
cat ~/.dashcore/dash.example.conf >> ~/.dashcore/dash.conf

View File

@@ -147,8 +147,8 @@ dash-qt \
CoinJoin aids in preventing some bad actors and malicious observers being able
to easily reconstruct details about your transactions from the publicly
available data by creating many excess transactions. \
(be aware, however, that dedicated bad actors can use sophisticated software that
will reveal much of the same information over time)
(be aware, however, that dedicated bad actors can use sophisticated software
that will reveal much of the same information over time)
`dash-qt` does not enable CoinJoin mixing by default.

View File

@@ -20,13 +20,13 @@ 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading dashcore 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")) {
if (!(Test-Path -Path "$pkg_src_dir")) {
Write-Output "Installing dashcore"
# TODO: create package-specific temp directory

1
dashcore/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = dashpay/dash

View File

@@ -1,31 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'dashpay';
var repo = 'dash';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
all.releases.forEach(function (rel) {
if (rel.name.includes('osx64')) {
rel.os = 'macos';
}
if (rel.version.startsWith('v')) {
rel._version = rel.version.slice(1);
}
});
all._names = ['dashd', 'dashcore'];
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

@@ -24,7 +24,7 @@ install:
~/.config/envman/PATH.env
~/.dashcore/dash.conf
~/.dashcore/wallets/
~/.local/bin/bin/dashd-hd-service-install
~/.local/bin/dashd-hd-service-install
~/.local/opt/dashcore/
/mnt/<BLK_VOL>/dashcore/
```
@@ -219,14 +219,7 @@ You can use [`serviceman`](../serviceman/):
**Linux**
```sh
sudo env PATH="$PATH" \
serviceman add \
--system \
--username "$(id -n -u)" \
--path "$PATH" \
--name dashd \
--force \
-- \
serviceman add --name 'dashd' -- \
dashd \
-usehd \
-conf="$HOME/.dashcore/dash.conf" \
@@ -239,11 +232,7 @@ sudo env PATH="$PATH" \
**Mac**
```sh
serviceman add \
--path "$PATH" \
--name dashd \
--force \
-- \
serviceman add --name 'dashd' -- \
dashd \
-usehd \
-conf="$HOME/.dashcore/dash.conf" \

View File

@@ -20,13 +20,13 @@ 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading dashd 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")) {
if (!(Test-Path -Path "$pkg_src_dir")) {
Write-Output "Installing dashd"
# TODO: create package-specific temp directory

1
dashd/releases.conf Normal file
View File

@@ -0,0 +1 @@
alias_of = dashcore

View File

@@ -1,3 +0,0 @@
'use strict';
module.exports = require('../dashcore/releases.js');

View File

@@ -19,13 +19,13 @@ 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading dashmsg 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")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing dashmsg"
# TODO: create package-specific temp directory

1
dashmsg/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = dashhive/dashmsg

View File

@@ -1,18 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'dashhive';
var repo = 'dashmsg';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
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

@@ -19,13 +19,13 @@ 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE")) {
Write-Output "Downloading delta 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")) {
if (!(Test-Path -Path "$pkg_src_cmd")) {
Write-Output "Installing delta"
# TODO: create package-specific temp directory

1
delta/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = dandavison/delta

View File

@@ -1,20 +0,0 @@
'use strict';
var github = require('../_common/github.js');
var owner = 'dandavison';
var repo = 'delta';
module.exports = function (request) {
return github(request, owner, repo).then(function (all) {
return all;
});
};
if (module === require.main) {
module.exports(require('@root/request')).then(function (all) {
all = require('../_webi/normalize.js')(all);
// just select the first 15 for demonstration
all.releases = all.releases.slice(0, 15);
console.info(JSON.stringify(all, null, 2));
});
}

View File

@@ -18,7 +18,7 @@ etc).
The obligatory Hello World
```sh
deno run https://deno.land/std/examples/welcome.ts
deno run https://docs.deno.com/examples/scripts/hello_world.ts
```
Run a local file

View File

@@ -1,14 +1,14 @@
#!/usr/bin/env pwsh
# Fetch archive
IF (!(Test-Path -Path "$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"
#Invoke-WebRequest https://nodejs.org/dist/v12.16.2/node-v12.16.2-win-x64.zip -OutFile node-v12.16.2-win-x64.zip
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE.part"
& 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")) {
if (!(Test-Path -Path "$Env:USERPROFILE\.local\opt\$Env:PKG_NAME-v$Env:WEBI_VERSION")) {
Write-Output "Installing $Env:PKG_NAME"
# TODO: temp directory

1
deno/releases.conf Normal file
View File

@@ -0,0 +1 @@
github_releases = denoland/deno

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