diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..87a11ac3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + actions-minor: + update-types: + - minor + - patch diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5c09652d..d28b326d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ on: - ready_for_review env: - DENO_VERSION: "1.38.2" + DENO_VERSION: "1.38.5" jobs: changes: @@ -29,14 +29,29 @@ jobs: - uses: pre-commit/action@v3.0.0 test-e2e: - runs-on: ubuntu-latest + runs-on: "${{ matrix.os }}" + strategy: + matrix: + include: + - os: ubuntu-latest + e2eType: "docker" + - os: macos-latest + e2eType: "local" + # - os: windows-latest + # e2eType: "local" + env: + GHJK_TEST_E2E_TYPE: ${{ matrix.e2eType }} steps: - uses: actions/checkout@v4 - uses: denoland/setup-deno@v1 with: deno-version: ${{ env.DENO_VERSION }} - - uses: docker/setup-buildx-action@v3 - - uses: actions-hub/docker/cli@master + - if: "${{ matrix.os == 'macos-latest' }}" + run: brew install fish zsh + - if: "${{ matrix.e2eType == 'docker' }}" + uses: docker/setup-buildx-action@v3 + - if: "${{ matrix.e2eType == 'docker' }}" + uses: actions-hub/docker/cli@master env: SKIP_LOGIN: true - run: deno task test @@ -45,15 +60,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: denoland/setup-deno@v1 + - uses: metatypedev/setup-ghjk@v1 with: - deno-version: ${{ env.DENO_VERSION }} - - uses: ./ - id: ghjk-action + installer-url: ./install.ts env: GHJK_CONFIG: ./examples/protoc/ghjk.ts - - shell: bash - run: | + - run: | cd examples/protoc . $BASH_ENV protoc --version diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0eb641d4..4f9ca1b7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v4.5.0 hooks: - id: check-added-large-files + exclude: gh_action/dist/.*.js - id: check-ast - id: check-json exclude: .vscode/.*\.json @@ -13,14 +14,18 @@ repos: - id: check-merge-conflict - id: end-of-file-fixer # exclude all generated files - exclude: (deno.lock) + exclude: | + (?x)^( + deno.lock| + gh_action/.*.js + )$ - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.27.0 hooks: - id: check-dependabot - id: check-github-workflows - repo: https://github.com/commitizen-tools/commitizen - rev: 3.10.0 + rev: v3.13.0 hooks: - id: commitizen stages: @@ -44,7 +49,7 @@ repos: - id: deno-lint name: Deno lint language: system - entry: bash -c 'deno task lint' + entry: bash -c 'deno lint' pass_filenames: false types: - ts diff --git a/README.md b/README.md index 69b998c0..1abc7849 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ and looks as follows (abstracting away some implementation details): - detects any violation of the enforced rules - [ ] `ghjk list`: list installed tools and versions - [ ] `ghjk outdated`: list outdated tools - - [ ]`ghjk cleanup`: remove unused tools and versions + - [ ] `ghjk cleanup`: remove unused tools and versions ## Extending `ghjk` @@ -69,15 +69,10 @@ and looks as follows (abstracting away some implementation details): ## todo - multiple version of the same package (e.g. rust stable and rust nighted) -- [x] wasmedge -- [x] jco - [ ] python with virtual env dir - poetry - pre-commit -- [x] pnpm -- [x] mold -- [x] wasm-tools -- [x] cargo-insta +- [ ] rust toolchain - hash verifiable dependencies (timestamp) - hide the `Deno` object in an abstraction - support windows diff --git a/action.yml b/action.yml deleted file mode 100644 index 8a047137..00000000 --- a/action.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: 'Setup Ghjk' -description: 'Installs ghjk and optionally syncs according to the config' -inputs: - installer-uri: - description: 'Alternative installer script to use' - required: true - # FIXME: find a way to get commit sha of current executing action version - # default: $GITHUB_SERVER_URL/$GITHUB_ACTION_REPOSITORY/raw/feat/ - default: './install.ts' - sync: - description: 'Disable to skip syncing ports' - required: true - default: true -runs: - using: "composite" - steps: - - id: install-ghjk - shell: bash - run: | - deno run -A ${{ inputs.installer-uri }} - echo "$HOME/.local/bin" >> $GITHUB_PATH - - id: sync-ghjk - shell: bash - if: ${{ inputs.sync }} - run: | - ghjk ports sync - - id: ghjk-outputs - shell: bash - run: | - echo "GHJK_DIR=$HOME/.local/share/ghjk" >> $GITHUB_OUTPUT - echo "BASH_ENV=$HOME/.local/share/ghjk/env.sh" >> $GITHUB_ENV diff --git a/check.ts b/check.ts new file mode 100644 index 00000000..eb946107 --- /dev/null +++ b/check.ts @@ -0,0 +1,10 @@ +import "./setup_logger.ts"; +import { $ } from "./utils/mod.ts"; + +const files = (await Array.fromAsync( + $.path(import.meta.url).parentOrThrow().expandGlob("**/*.ts", { + exclude: ["./gh_action"], + }), +)).map((ref) => ref.path.toString()); + +await $`${Deno.execPath()} check ${files}`; diff --git a/deno.jsonc b/deno.jsonc index a883c5e4..b1267353 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,9 +1,16 @@ { "tasks": { - // "test": "DOCKER_SOCK=/var/run/docker.sock deno test --unstable --allow-env=DOCKER_SOCK --allow-read=/var/run/docker.sock --allow-write=/var/run/docker.sock tests/*" - "test": "deno test --fail-fast --parallel --unstable --allow-run=docker,sudo,which,ls,tar,git,curl,unzip --allow-read --allow-env tests/*", + "test": "deno test --fail-fast --parallel --unstable -A tests/*", "cache": "deno cache deps/*", - "check": "deno check *.ts **/*.ts", - "lint": "deno lint --ignore=ghjk.ts,play.ts --rules-exclude=no-explicit-any" + "check": "deno run -A check.ts" + }, + "fmt": { + "exclude": ["gh_action/*.js"] + }, + "lint": { + "exclude": ["gh_action/*", "ghjk.ts", "play.ts"], + "rules": { + "exclude": ["no-explicit-any"] + } } } diff --git a/deno.lock b/deno.lock index c255f3d3..64039df7 100644 --- a/deno.lock +++ b/deno.lock @@ -1,33 +1,36 @@ { "version": "3", - "redirects": { - "https://cdn.esm.sh/extract-files@12.0.0/extractFiles.mjs": "https://esm.sh/extract-files@12.0.0/extractFiles.mjs", - "https://cdn.esm.sh/extract-files@12.0.0/isExtractableFile.mjs": "https://esm.sh/extract-files@12.0.0/isExtractableFile.mjs", - "https://deno.land/x/graphql_request/mod.ts": "https://deno.land/x/graphql_request@v4.1.0/mod.ts" + "packages": { + "specifiers": { + "npm:@types/node": "npm:@types/node@18.16.19" + }, + "npm": { + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", + "dependencies": {} + } + } }, "remote": { + "https://deno.land/std@0.116.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58", + "https://deno.land/std@0.116.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac", + "https://deno.land/std@0.116.0/fs/walk.ts": "31464d75099aa3fc7764212576a8772dfabb2692783e6eabb910f874a26eac54", + "https://deno.land/std@0.116.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853", + "https://deno.land/std@0.116.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4", + "https://deno.land/std@0.116.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b", + "https://deno.land/std@0.116.0/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4", + "https://deno.land/std@0.116.0/path/glob.ts": "ea87985765b977cc284b92771003b2070c440e0807c90e1eb0ff3e095911a820", + "https://deno.land/std@0.116.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12", + "https://deno.land/std@0.116.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2", + "https://deno.land/std@0.116.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", + "https://deno.land/std@0.116.0/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e", "https://deno.land/std@0.129.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", - "https://deno.land/std@0.129.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", "https://deno.land/std@0.129.0/archive/tar.ts": "35ea1baddec7988cc4034765a2cee7613bc8074bd40940d3f5e98f63070a716a", - "https://deno.land/std@0.129.0/async/abortable.ts": "a896ac6b0d4237bd2d2d248217cfa1f0d85ccda93cb25ebda55e33850e526be6", - "https://deno.land/std@0.129.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0", - "https://deno.land/std@0.129.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620", "https://deno.land/std@0.129.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a", - "https://deno.land/std@0.129.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c", - "https://deno.land/std@0.129.0/async/mod.ts": "2240c6841157738414331f47dee09bb8c0482c5b1980b6e3234dd03515c8132f", - "https://deno.land/std@0.129.0/async/mux_async_iterator.ts": "f4d1d259b0c694d381770ddaaa4b799a94843eba80c17f4a2ec2949168e52d1e", - "https://deno.land/std@0.129.0/async/pool.ts": "97b0dd27c69544e374df857a40902e74e39532f226005543eabacb551e277082", - "https://deno.land/std@0.129.0/async/tee.ts": "1341feb1f5b1a96f8628d0f8fc07d8c43d3813423f18a63bf1b4785568d21b1f", "https://deno.land/std@0.129.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", "https://deno.land/std@0.129.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", "https://deno.land/std@0.129.0/bytes/mod.ts": "d3b455c0dbd4804644159d1e25946ade5ee385d2359894de49e2c6101b18b7a9", - "https://deno.land/std@0.129.0/encoding/base64.ts": "c8c16b4adaa60d7a8eee047c73ece26844435e8f7f1328d74593dbb2dd58ea4f", - "https://deno.land/std@0.129.0/encoding/base64url.ts": "55f9d13df02efac10c6f96169daa3e702606a64e8aa27c0295f645f198c27130", "https://deno.land/std@0.129.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", - "https://deno.land/std@0.129.0/fmt/printf.ts": "e2c0f72146aed1efecf0c39ab928b26ae493a2278f670a871a0fbdcf36ff3379", - "https://deno.land/std@0.129.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", - "https://deno.land/std@0.129.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", - "https://deno.land/std@0.129.0/fs/ensure_file.ts": "7d353e64fee3d4d1e7c6b6726a2a5e987ba402c15fb49566309042887349c545", "https://deno.land/std@0.129.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", "https://deno.land/std@0.129.0/io/files.ts": "d199ef64e918a256320ba8d8d44ae91de87c9077df8f8d6cca013f1b9fbbe285", "https://deno.land/std@0.129.0/io/mod.ts": "1a4e8d19d42745fb2ff68d6ffa801657a4a15713bf7e7173df2da4737f5c5450", @@ -35,50 +38,6 @@ "https://deno.land/std@0.129.0/io/streams.ts": "988a19155b52161f0035ce539e2f1d12edbc4c389fa7633da832a64e6edbe1a0", "https://deno.land/std@0.129.0/io/util.ts": "078da53bba767bec0d45f7da44411f6dbf269e51ef7fcfea5e3714e04681c674", "https://deno.land/std@0.129.0/io/writers.ts": "5db9995d2afc7ed391c88c6b441457df6fad6a0b09653e54c1dcd0387ab947fd", - "https://deno.land/std@0.129.0/node/_buffer.mjs": "f4a7df481d4eed06dc0151b833177d8ef74fc3a96dd4d2b073e690b6ced9474d", - "https://deno.land/std@0.129.0/node/_core.ts": "568d277be2e086af996cbdd599fec569f5280e9a494335ca23ad392b130d7bb9", - "https://deno.land/std@0.129.0/node/_events.mjs": "c0e3e0e290a8b81fee9d2973a529c8dcd5ebb4406782d1f91085274e2cb8490f", - "https://deno.land/std@0.129.0/node/_fixed_queue.ts": "455b3c484de48e810b13bdf95cd1658ecb1ba6bcb8b9315ffe994efcde3ba5f5", - "https://deno.land/std@0.129.0/node/_next_tick.ts": "64c361f6bca21df2a72dd77b84bd49d80d97a694dd3080703bc78f52146351d1", - "https://deno.land/std@0.129.0/node/_process/exiting.ts": "bc9694769139ffc596f962087155a8bfef10101d03423b9dcbc51ce6e1f88fce", - "https://deno.land/std@0.129.0/node/_util/_util_callbackify.ts": "79928ad80df3e469f7dcdb198118a7436d18a9f6c08bd7a4382332ad25a718cf", - "https://deno.land/std@0.129.0/node/_utils.ts": "c2c352e83c4c96f5ff994b1c8246bff2abcb21bfc3f1c06162cb3af1d201e615", - "https://deno.land/std@0.129.0/node/buffer.ts": "fbecbf3f237fa49bec96e97ecf56a7b92d48037b3d11219288e68943cc921600", - "https://deno.land/std@0.129.0/node/events.ts": "a1d40fc0dbccc944379ef968b80ea08f9fce579e88b5057fdb64e4f0812476dd", - "https://deno.land/std@0.129.0/node/internal/buffer.mjs": "6662fe7fe517329453545be34cea27a24f8ccd6d09afd4f609f11ade2b6dfca7", - "https://deno.land/std@0.129.0/node/internal/crypto/keys.ts": "16ce7b15a9fc5e4e3dee8fde75dae12f3d722558d5a1a6e65a9b4f86d64a21e9", - "https://deno.land/std@0.129.0/node/internal/crypto/util.mjs": "1de55a47fdbed6721b467a77ba48fdd1550c10b5eee77bbdb602eaffee365a5e", - "https://deno.land/std@0.129.0/node/internal/error_codes.ts": "ac03c4eae33de3a69d6c98e8678003207eecf75a6900eb847e3fea3c8c9e6d8f", - "https://deno.land/std@0.129.0/node/internal/errors.ts": "0d3a1eb03b654beb29b8354759a6902f45a840d4f957e9a3c632a24ce4c32632", - "https://deno.land/std@0.129.0/node/internal/hide_stack_frames.ts": "a91962ec84610bc7ec86022c4593cdf688156a5910c07b5bcd71994225c13a03", - "https://deno.land/std@0.129.0/node/internal/normalize_encoding.mjs": "3779ec8a7adf5d963b0224f9b85d1bc974a2ec2db0e858396b5d3c2c92138a0a", - "https://deno.land/std@0.129.0/node/internal/util.mjs": "684653b962fae84fd2bc08997291b1a50bed09b95dcfa7d35e3c4143163e879a", - "https://deno.land/std@0.129.0/node/internal/util/comparisons.ts": "680b55fe8bdf1613633bc469fa0440f43162c76dbe36af9aa2966310e1bb9f6e", - "https://deno.land/std@0.129.0/node/internal/util/debuglog.ts": "99e91bdf26f6c67861031f684817e1705a5bc300e81346585b396f413387edfb", - "https://deno.land/std@0.129.0/node/internal/util/inspect.mjs": "d1c2569c66a3dab45eec03208f22ad4351482527859c0011a28a6c797288a0aa", - "https://deno.land/std@0.129.0/node/internal/util/types.ts": "b2dacb8f1f5d28a51c4da5c5b75172b7fcf694073ce95ca141323657e18b0c60", - "https://deno.land/std@0.129.0/node/internal/validators.mjs": "a7e82eafb7deb85c332d5f8d9ffef052f46a42d4a121eada4a54232451acc49a", - "https://deno.land/std@0.129.0/node/internal_binding/_libuv_winerror.ts": "801e05c2742ae6cd42a5f0fd555a255a7308a65732551e962e5345f55eedc519", - "https://deno.land/std@0.129.0/node/internal_binding/_node.ts": "e4075ba8a37aef4eb5b592c8e3807c39cb49ca8653faf8e01a43421938076c1b", - "https://deno.land/std@0.129.0/node/internal_binding/_utils.ts": "1c50883b5751a9ea1b38951e62ed63bacfdc9d69ea665292edfa28e1b1c5bd94", - "https://deno.land/std@0.129.0/node/internal_binding/_winerror.ts": "8811d4be66f918c165370b619259c1f35e8c3e458b8539db64c704fbde0a7cd2", - "https://deno.land/std@0.129.0/node/internal_binding/buffer.ts": "722c62b85f966e0777b2d98c021b60e75d7f2c2dabc43413ef37d60dbd13a5d9", - "https://deno.land/std@0.129.0/node/internal_binding/constants.ts": "aff06aac49eda4234bd3a2b0b8e1fbfc67824e281c532ff9960831ab503014cc", - "https://deno.land/std@0.129.0/node/internal_binding/string_decoder.ts": "5cb1863763d1e9b458bc21d6f976f16d9c18b3b3f57eaf0ade120aee38fba227", - "https://deno.land/std@0.129.0/node/internal_binding/types.ts": "4c26fb74ba2e45de553c15014c916df6789529a93171e450d5afb016b4c765e7", - "https://deno.land/std@0.129.0/node/internal_binding/util.ts": "90364292e2bd598ab5d105b48ca49817b6708f2d1d9cbaf08b2b3ab5ca4c90a7", - "https://deno.land/std@0.129.0/node/internal_binding/uv.ts": "3821bc5e676d6955d68f581988c961d77dd28190aba5a9c59f16001a4deb34ba", - "https://deno.land/std@0.129.0/node/util.ts": "7fd6933b37af89a8e64d73dc6ee1732455a59e7e6d0965311fbd73cd634ea630", - "https://deno.land/std@0.129.0/node/util/types.mjs": "f9288198cacd374b41bae7e92a23179d3160f4c0eaf14e19be3a4e7057219a60", - "https://deno.land/std@0.129.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", - "https://deno.land/std@0.129.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", - "https://deno.land/std@0.129.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", - "https://deno.land/std@0.129.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", - "https://deno.land/std@0.129.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", - "https://deno.land/std@0.129.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", - "https://deno.land/std@0.129.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", - "https://deno.land/std@0.129.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", - "https://deno.land/std@0.129.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", "https://deno.land/std@0.129.0/streams/buffer.ts": "ee47194022d47fa23d4749b8afbadc83c237c4595467a9bddb322af0dd205815", "https://deno.land/std@0.129.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", "https://deno.land/std@0.129.0/streams/delimiter.ts": "75595345f14eb268d2f5edfd20600c9956f61499a36697baabef8043897bc50b", @@ -86,31 +45,6 @@ "https://deno.land/std@0.129.0/streams/mod.ts": "5f47811c2e983518cc0c82f323924b6a9bb1f5bf948cd6d498ff6aed77ada51c", "https://deno.land/std@0.129.0/testing/_diff.ts": "9d849cd6877694152e01775b2d93f9d6b7aef7e24bfe3bfafc4d7a1ac8e9f392", "https://deno.land/std@0.129.0/testing/asserts.ts": "0a95d9e8076dd3e7f0eeb605a67c148078b4b11f4abcd5eef115b0361b0736a2", - "https://deno.land/std@0.133.0/_deno_unstable.ts": "23a1a36928f1b6d3b0170aaa67de09af12aa998525f608ff7331b9fb364cbde6", - "https://deno.land/std@0.133.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", - "https://deno.land/std@0.133.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", - "https://deno.land/std@0.133.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", - "https://deno.land/std@0.133.0/fs/copy.ts": "9248d1492599957af8c693ceb10a432b09f0b0b61c60a4d6aff29b0c7d3a17b3", - "https://deno.land/std@0.133.0/fs/empty_dir.ts": "7274d87160de34cbed0531e284df383045cf43543bbeadeb97feac598bd8f3c5", - "https://deno.land/std@0.133.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", - "https://deno.land/std@0.133.0/fs/ensure_file.ts": "7d353e64fee3d4d1e7c6b6726a2a5e987ba402c15fb49566309042887349c545", - "https://deno.land/std@0.133.0/fs/ensure_link.ts": "489e23df9fe3e6636048b5830ddf0f111eb29621eb85719255ad9bd645f3471b", - "https://deno.land/std@0.133.0/fs/ensure_symlink.ts": "88dc83de1bc90ed883dd458c2d2eae3d5834a4617d12925734836e1f0803b274", - "https://deno.land/std@0.133.0/fs/eol.ts": "b92f0b88036de507e7e6fbedbe8f666835ea9dcbf5ac85917fa1fadc919f83a5", - "https://deno.land/std@0.133.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d", - "https://deno.land/std@0.133.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3", - "https://deno.land/std@0.133.0/fs/mod.ts": "4dc052c461c171abb5c25f6e0f218ab838a716230930b534ba351745864b7d6d", - "https://deno.land/std@0.133.0/fs/move.ts": "0573cedcf583f09a9494f2dfccbf67de68a93629942d6b5e6e74a9e45d4e8a2e", - "https://deno.land/std@0.133.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f", - "https://deno.land/std@0.133.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", - "https://deno.land/std@0.133.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", - "https://deno.land/std@0.133.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", - "https://deno.land/std@0.133.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", - "https://deno.land/std@0.133.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", - "https://deno.land/std@0.133.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", - "https://deno.land/std@0.133.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", - "https://deno.land/std@0.133.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", - "https://deno.land/std@0.133.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", "https://deno.land/std@0.196.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", "https://deno.land/std@0.196.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", "https://deno.land/std@0.196.0/console/_data.json": "cf2cc9d039a192b3adbfe64627167c7e6212704c888c25c769fc8f1709e1e1b8", @@ -342,8 +276,6 @@ "https://deno.land/std@0.205.0/url/join.ts": "fbc3488c641c38832f0c900fcf99cb970164d8e32b84f1427581bb83cf35efeb", "https://deno.land/std@0.205.0/url/mod.ts": "d4e4db2f85a4a1613d824367b750f36bbd1c0ff791daae2eb74795d292c722bb", "https://deno.land/std@0.205.0/url/normalize.ts": "5c5803452521a36faec1a91bdb665e1cbdf7ce22bc0482388ad79f229b74cd45", - "https://deno.land/x/base64@v0.2.1/base.ts": "47dc8d68f07dc91524bdd6db36eccbe59cf4d935b5fc09f27357a3944bb3ff7b", - "https://deno.land/x/base64@v0.2.1/mod.ts": "1cbdc4ba7229d3c6d1763fecdb9d46844777c7e153abb6dabea8b0dd01448db4", "https://deno.land/x/cliffy@v1.0.0-rc.3/_utils/distance.ts": "02af166952c7c358ac83beae397aa2fbca4ad630aecfcd38d92edb1ea429f004", "https://deno.land/x/cliffy@v1.0.0-rc.3/command/_argument_types.ts": "ab269dacea2030f865a07c2a1e953ec437a64419a05bad1f1ddaab3f99752ead", "https://deno.land/x/cliffy@v1.0.0-rc.3/command/_errors.ts": "12d513ff401020287a344e0830e1297ce1c80c077ecb91e0ac5db44d04a6019c", @@ -403,33 +335,6 @@ "https://deno.land/x/cliffy@v1.0.0-rc.3/table/deps.ts": "1226c4d39d53edc81d7c3e661fb8a79f2e704937c276c60355cd4947a0fe9153", "https://deno.land/x/cliffy@v1.0.0-rc.3/table/row.ts": "79eb1468aafdd951e5963898cdafe0752d4ab4c519d5f847f3d8ecb8fe857d4f", "https://deno.land/x/cliffy@v1.0.0-rc.3/table/table.ts": "298671e72e61f1ab18b42ae36643181993f79e29b39dc411fdc6ffd53aa04684", - "https://deno.land/x/compress@v0.4.5/deps.ts": "096395daebc7ed8a18f0484e4ffcc3a7f70e50946735f7df9611a7fcfd8272cc", - "https://deno.land/x/compress@v0.4.5/gzip/gzip.ts": "4bf22e9cd3368332928324dd9443ef72cabd05e9234e5a37dd7b3517d50e945e", - "https://deno.land/x/compress@v0.4.5/gzip/gzip_file.ts": "b044ec0df4266c084baa033a4ab5394882e44a86d09d5616636467dcb39c671d", - "https://deno.land/x/compress@v0.4.5/gzip/gzip_stream.ts": "6781cf0e47648e3e5631cba4cc2cd018a24935ce09fdaa86e0cabcf78b5012df", - "https://deno.land/x/compress@v0.4.5/gzip/mod.ts": "4ade8edbe01b54a84f289351e137ebdfc040a74cd616636770cf1724fbf522d1", - "https://deno.land/x/compress@v0.4.5/gzip/writer_gunzip.ts": "5aba34394820b835c414048ac2e15f52d443f1f773ebe61fd2517c938572d616", - "https://deno.land/x/compress@v0.4.5/gzip/writer_gzip.ts": "c7aad0c51ab4f5952c068088186339cfc79a2ee1e057d6e16731b1175f342645", - "https://deno.land/x/compress@v0.4.5/interface.ts": "fc5f87bd208ab8a03a1f65972b11781967c3d21c3d756fe9ae99ca98e10e5780", - "https://deno.land/x/compress@v0.4.5/mod.ts": "ae8b15826334021583a5bd1978c63840f85156ea3635f5941bfc6733aad247e5", - "https://deno.land/x/compress@v0.4.5/tar/mod.ts": "6d9073005e678479908047cbe9e4716e484f80d1f2a1e15d3d6ac92213ffaeba", - "https://deno.land/x/compress@v0.4.5/tgz/mod.ts": "2fd4e99f26b57b0055d4d2f87721682304541ed1ca41bbb49c034d121f936f00", - "https://deno.land/x/compress@v0.4.5/utils/uint8.ts": "9c82e09c065f1f4bc648e3b14df441b43a7960fc7bdb29e9fb8d3a69c7e9d425", - "https://deno.land/x/compress@v0.4.5/zlib/deflate.ts": "e1e3b406dcc3e20021e53cde427b4b9ced752b72df820de73fec17c6e5ba999e", - "https://deno.land/x/compress@v0.4.5/zlib/inflate.ts": "618cc3dd25d202bf6b89d92f3ab2865e7495884cafce950638c77cbc1537aeb1", - "https://deno.land/x/compress@v0.4.5/zlib/mod.ts": "4dca9c1e934b7ab27f31c318abd7bfd39b09be96fd76ba27bd46f3a4e73b4ad0", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/adler32.ts": "e34c7596d63a655755c4b0a44a40d4f9c1d1c4d3b891e5c1f3f840f8939e1940", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/crc32.ts": "b9bc4adaf327d32585205d1176bd52f6453c06dd1040544611d4c869e638119c", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/deflate.ts": "8d1dd88630279313e50deed4fe5feefe8128307cc48fa560e659b5234ab09d83", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/gzheader.ts": "11e6da7383447aae9791308dc2350a809fa341a876a2da396b03a2a31408c20c", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/inffast.ts": "282daf5ea16bb876d26e342f3c24fe1a8ec84640e713a970b02232955a853f86", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/inflate.ts": "76751c1a5b18d70a929fa31ce4959db0bde1b9097bfa1b5ea3b4d1fba2ab92fa", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/inftrees.ts": "8a6d765a5c42bf3b6990060cabbe52e88493f8ce6d082e6e35d97756914cfb8e", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/messages.ts": "c82229bd67ccc3b6162f3aca1c5e7f936e546aa91ac9a9ac4fcfefc3a9dc5ac8", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/status.ts": "5987864d2d43d59bbbfa2e6ef4d5a07284c1d10489cc5843ddf41ac547957ac3", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/trees.ts": "6b65a767646e031e87e7b725ffad0c511fe701f393a01652e1e7ee8884f60fee", - "https://deno.land/x/compress@v0.4.5/zlib/zlib/zstream.ts": "c110fd5919235e317d64933852e24a1bba0126202be592e90e58f7b19315ad93", - "https://deno.land/x/crc32@v0.2.0/mod.ts": "de7a3fa2d4ef24b96fc21e1cc4d2d65d1d2b1dcea92f63960e3e11bfa82df0fa", "https://deno.land/x/dax@0.35.0/mod.ts": "3fc382546bf3c7b90aa458aa144be7c6e8aed3e8c2680289f9c8694d986b7247", "https://deno.land/x/dax@0.35.0/src/command.ts": "6e7db06015b4ad6decbf59cc5fcb6bd4b03a46276f7e3f3472204c11b2109e0e", "https://deno.land/x/dax@0.35.0/src/command_handler.ts": "841cee0ce12b19eea6c7fcaeaa40a9e3ef4bf50c36cf02afbe3ab7b41f8571eb", @@ -467,202 +372,14 @@ "https://deno.land/x/dax@0.35.0/src/request.ts": "a2b20859de7a0fbe10584a41de435942ee4726f0b637b1cb55d7f632f4efc74f", "https://deno.land/x/dax@0.35.0/src/result.ts": "0908b69c16b25c3b258f6b2ada12e124686df5f7ea2b98daa27a83973c7b118c", "https://deno.land/x/dax@0.35.0/src/shell.ts": "9475a015d5493197f9611b1259c5dd6d27c7c2ab9c3711606cd4b47412568ee1", - "https://deno.land/x/denocker@v0.2.1/container.ts": "b13512efec6ec20655ddd263732f1386ac05b72926f9a45e9142b2200ff71275", - "https://deno.land/x/denocker@v0.2.1/index.ts": "c9f67e6ce29d7d6cc0516c1d0aedc4b927d66338a75226020e9a5670b8167452", - "https://deno.land/x/denocker@v0.2.1/lib/client/auth.ts": "dc672975a3356bc9e95e29106e6526cb27791daf7be340a1af55a85f4fd44897", - "https://deno.land/x/denocker@v0.2.1/lib/client/client.ts": "d612ec1f2c3dd50a0b21a21a4aabafa470e41bce6c9bda83390bf8e3b058f713", - "https://deno.land/x/denocker@v0.2.1/lib/client/httpClient.ts": "1e2e93b1b98c91f353371b14ec878d3eb646cd8ea1da1407a77cf9445ac0837e", - "https://deno.land/x/denocker@v0.2.1/lib/types/container/container.ts": "496a0aaca6e892cb1c6563334ad10f3fba0d72f26a7fbc0608ce74822009b7d2", - "https://deno.land/x/denocker@v0.2.1/lib/types/container/create.ts": "6fba0a043a4c7d21a1757ac845af1afc90612a4a08c62df383c6db919c7e739b", - "https://deno.land/x/denocker@v0.2.1/lib/types/container/inspect.ts": "83c70a637962c420fd19eb477e4357dfcb909ed698c735ae5bec4c424418edfa", - "https://deno.land/x/denocker@v0.2.1/lib/types/container/list.ts": "0593156829cc2cab0a7ed52c1e2b66c6a3a082bdc6de5011db3f5a3c0d717369", - "https://deno.land/x/denocker@v0.2.1/lib/types/container/mod.ts": "06b040fa7315aca141e0eb36a9993e1ca9d165694a3d46abac4dc0ddfde9e574", - "https://deno.land/x/docker@v0.0.1/deps.ts": "3fcd7d37f373abaab7e46d865b232ffc3061bd98310d00db6822a9066c05ac67", - "https://deno.land/x/docker@v0.0.1/mod.ts": "930d1a8890b6eb4a9b6334d245d536150079773f5d8df1fa624fe530a7988a80", - "https://deno.land/x/docker@v0.0.1/src/client.ts": "3b6abd8ac73b2364222acd43dc0a4cea8531278005b3047e2d7b1b9e2bf54916", - "https://deno.land/x/docker@v0.0.1/src/system.ts": "567c9b48da5ac913b63a12f4a52bee461ff9b2f5260e459457bfbf5c2b524020", "https://deno.land/x/foras@v2.1.4/src/deno/mod.ts": "c350ea5f32938e6dcb694df3761615f316d730dafc57440e9afd5f36f8e309fd", "https://deno.land/x/foras@v2.1.4/src/deno/mods/mod.ts": "cc099bbce378f3cdaa94303e8aff2611e207442e5ac2d5161aba636bb4a95b46", "https://deno.land/x/foras@v2.1.4/wasm/pkg/foras.js": "06f8875b456918b9671d52133f64f3047f1c95540feda87fdd4a55ba3d30091d", "https://deno.land/x/foras@v2.1.4/wasm/pkg/foras.wasm.js": "2df8522df7243b0f05b1d188e220629cd5d2c92080a5f1407e15396fc35bebb3", - "https://deno.land/x/graphql_deno@v15.0.0/lib/error/GraphQLError.js": "18adbba7aa651770e0876d0c7df4e6e2ab647a9f09d4b5c107c57d6fa157be9d", - "https://deno.land/x/graphql_deno@v15.0.0/lib/error/formatError.js": "aec87433c501df6d6272b64974e8edf53b2ed192e66782b827328d635ed55df8", - "https://deno.land/x/graphql_deno@v15.0.0/lib/error/index.js": "7557dcea8830550f82dd7b1984fdc216e14327d094f501bd2a03f80bf609a768", - "https://deno.land/x/graphql_deno@v15.0.0/lib/error/locatedError.js": "65ad5e9246747d2b63cd2ea48fa22db617e1c7c2b796a27b8ce32bfb0f2a401c", - "https://deno.land/x/graphql_deno@v15.0.0/lib/error/syntaxError.js": "9c53411030cf5f4e874e9d1c1926f242e4acc4b469197b395ae1a9a7a9092055", - "https://deno.land/x/graphql_deno@v15.0.0/lib/execution/execute.js": "2d3114e49268a50195195039cc14629b0fe3dff7afeafed9876f1532a95ed4e7", - "https://deno.land/x/graphql_deno@v15.0.0/lib/execution/index.js": "064615af63e0da1584c557ce130b3ca42acab750289a085ebcc3107aa024ce52", - "https://deno.land/x/graphql_deno@v15.0.0/lib/execution/values.js": "e9a42ff1593db210a47504eebcc145f70f8d97cfe53c4cce345116bb420fbf95", - "https://deno.land/x/graphql_deno@v15.0.0/lib/graphql.js": "c34b19e78a57ad0a0ac9b39ec2766ab755b23e61ea08edf124dda1d60104ae54", - "https://deno.land/x/graphql_deno@v15.0.0/lib/index.js": "ab428112340017d5b1f74e62f958cf0c50c17b664a33be53974ab8738a531c1c", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/Path.js": "28322158c8208f92d376969de58c30ea393aada1beb44223d40584d8d89285c0", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/defineToJSON.js": "5423bbfe56faf7c3b0cda65b515c7fcc0e111bef46da9f76fed93eaba1859149", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/devAssert.js": "7493987e163c294b31f354091b6ca5e87849a132aa3aad8e3c173ac008c8e970", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/didYouMean.js": "9b83a8fe7bdd5b02012c097de14a0fe08a5e33662624ce55eb0019f46067b974", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/identityFunc.js": "204f973b6a5fc6f60ea62641e58eed254f0bb7d76b2b05e9c0bae54dc7cd324a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/inspect.js": "bb756ad067f23b137b63bb2dabc4e50b070255da2ff10068d9f0a174dd5f3aae", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/instanceOf.js": "c2940e2f0c71ed73390ab03ceb04484c40a4f5f28973709702c8279b1b6f96cd", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/invariant.js": "b8d3e8438abe0ec591fdc3a3f0f9c2d7750925738ee146ba7a18173a8f2dc1cb", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/isCollection.js": "42ffc930d5982ec031b4a7f29db9fd7f3a9e488ca0ceb73183a51a67ad146e2b", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/isObjectLike.js": "96403248d91ae55fa67ce81d339bec098aa51e5fc7132eae3f8dcce91ac340ce", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/isPromise.js": "dbe9979b7d4ffd920d8a608a9ce7127b943b130822cd907d78b9e56e66843509", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/keyMap.js": "4e54790d45a13998d198b0b7b1bc20ae17259e8131c29821d67e64b96dcb249e", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/keyValMap.js": "515ebb0c4be9c26f8fa38b7e0db4f2ebf96057196a8fcd52078046f05bf4e4e0", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/mapValue.js": "5df0be208d71e372a693ef305287a8f041b061a293cff030aa10bef4c2735981", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/memoize3.js": "4212fbf3f75fc8625a8312b18e0b2907efaf9cf6b8131c78a731a707ba638c74", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/nodejsCustomInspectSymbol.js": "eb85744be5bab6c4511556b095198b45259b2c020ff2f88f3b04197acc9feb01", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/printPathArray.js": "15110572999ddb82bcde2e42a35092d18fa601f5b8312e5823bd43d48f9b773f", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/promiseForObject.js": "b82720027d9bf3b81f302baa8d72f80b5d04f2069ebb538875f22993be83ee11", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/promiseReduce.js": "178a7c704a3c124a68a60aa71f29fb078268e403dab310b41937d10f5bb55e01", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/suggestionList.js": "4504e10e0e9b230cd697ebcf1ffae9b2ae420db2927a44dfbab452558e2f4b4a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/jsutils/toObjMap.js": "482c18508c808bca6a8e657ab7e3dfacadbf9f78603ce452700199d02f1b9d32", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/ast.js": "faa944fc543e4997c82302a6230c190eaf72bfcca1b8db3b29502826bdf9e9bb", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/blockString.js": "413ad9886dee36a96875cea8bbb51763b1ab8f1c3e82b8468330e8a5ca877852", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/directiveLocation.js": "8500c3878eafe0142f1b1255a998e76b20d3f859d9016ef9f7252878926dfdd8", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/index.js": "9289b05fbe173c54e727465996239a3bce6057fb727677ca44af37d60409480d", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/kinds.js": "7bdebb8110e7345fe6c14f255225d7c9b5448fb2dd53ea91d035a4b2e1e8f72b", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/lexer.js": "68bf4d832a8d1b4af3a0432ad308bc6f3ffbf60eb1c4e786503b30aff7e17564", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/location.js": "9994a20bee8d97ff69781b35477f596a3e6bee9ac1d914340e435bc0298922ee", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/parser.js": "53af901714fda7e62137fe2fe9eccecb4945a8648cffbb93ace439a0e445f05e", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/predicates.js": "cfd7e10b724590b618f67845ce1d1ca712b7da5943622b40881e7653f5557ab7", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/printLocation.js": "26b21c32abaee20e11a072984c381384dd5645be50bcbbdb59bac435490a60e9", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/printer.js": "a2da61f578725d78182fadfdc0ce809c30ed7d6b8369b98b1606b68b0e949b39", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/source.js": "e2a870f2446abf092996453d87f98214f12898e83980b76706cc8160993f16a8", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/tokenKind.js": "ad57e9dddaca5336e49a715b710c8142d9ff963270f3d4d6b63348a482922a74", - "https://deno.land/x/graphql_deno@v15.0.0/lib/language/visitor.js": "47b0a0f0d2f1e1edbf18a4b26861d371c41c0bfebf7c3f9ccb2e423663184dcd", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/arrayFrom.js": "b57194d34b98ac5926ae83e0a83e884d63ee9ec2d2ae3e846f1783cb26f6dbf8", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/find.js": "30ebdb4e2cd0ed5b0e3e4a00877f2bd2ac62de6d70a4c63152021fe724e41911", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/flatMap.js": "29d62564cd9745536e366e09b7f6cbe8860d8423c00ca253ae6cbfba839f2fc6", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/isFinite.js": "231e2d149aa58e8288b6cf86f588a1597f3f8d432928a17af9c348b48bd8ddde", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/isInteger.js": "0b68164ae12c46b16e439a5384af0c8c395f5c941e0f03fb71ee2ee9cb45fae6", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/objectEntries.js": "c261873f15f88ecb1497427844a9afc541168a92b7d75d69446732a0d381c819", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/objectValues.js": "7c3ed07793a1685cf018fa028275ff556213ba953c43dd2410bb549a86ee801b", - "https://deno.land/x/graphql_deno@v15.0.0/lib/polyfills/symbols.js": "bbe907378637a4264484590815d80563cfbd8a8a5f396ac9d2be4107658e5bf7", - "https://deno.land/x/graphql_deno@v15.0.0/lib/subscription/index.js": "3c2e03b8225f66a541d870fb6729ad2e3afbd923a0d07713a087c22d134097b3", - "https://deno.land/x/graphql_deno@v15.0.0/lib/subscription/mapAsyncIterator.js": "ff581dcfccf8aeedfefe1c42a8f01c7898c1a2b093e5f6fb4d2e62b2f83ab8c8", - "https://deno.land/x/graphql_deno@v15.0.0/lib/subscription/subscribe.js": "f4d316d4f7e3e60cc3f9f8371e6122396653ff1836a27d44a3e8bf1cfa3d5def", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/definition.js": "94ef1069f5e034f9dc34ca7f25370fe81a58365acbb6e932ab9e0db92efe8287", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/directives.js": "d4614736de1997413a4970b73865c99d3b01922eda3de1e587c27c25c24e8892", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/index.js": "4a7d837f30c56cf3074ee527f4b9388d160ad20cd545d4fdebac500acdf102b0", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/introspection.js": "c63b4c5a578d76f8d159837ce531e1193dd3a2629148aedb895b511141303afd", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/scalars.js": "d447913dbdcaaa32f2eaa5d86bd39b5342bfcf78d9ddd6b2173ad717ae7fa734", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/schema.js": "1c92f80cc2c1f0a14a5b3fcb13d55f215f14e1b2bb7b92c2674ede22f969f033", - "https://deno.land/x/graphql_deno@v15.0.0/lib/type/validate.js": "f8300538e4327b46959ff83a7dabd55e42148ef07fa66c79f72b308a46cfd369", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/TypeInfo.js": "03276253d0ab3d4947f7bf63ca4ccc4fe0073f73063952103530f8193a98e546", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/assertValidName.js": "d6b44f4dddcc50ebe92c7cb9b94ecc9b27627994b3c156ce63d33279c7f7d0e4", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/astFromValue.js": "d460204b065b8e1036e215faa9a617a20c268e687ed424208dabc5cd5f40f2d1", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/buildASTSchema.js": "d4aeade98d0a1bc6ffdc160fdc78e9edf22a9efdc550cdc6121d1c83ff1e6c84", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/buildClientSchema.js": "1ac64f6aacdf57e9b552a0c45e7176c988c0d97310f646d7d048899524856d3a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/coerceInputValue.js": "ee59bf0ed0857af92c4f40a309365fd57770821b3c788b6b13d0d1051ffec0da", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/concatAST.js": "bf410b42f7aa015ff0cd938623894906ac19329d9cd9a2298cf97de7cb01442a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/extendSchema.js": "802fbd291d21cfa97c1ebea4fa6528ac6cddbbcc27c169942c975ce708a9f295", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/findBreakingChanges.js": "2c8dfd8acf617e5d8a96e389ba6bccb0845fc4470f680957a1ca64514337574a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/findDeprecatedUsages.js": "5a5366e6ed3c99e16d73b1235ee9062ac10d3ec16afafc4d6c4f635cc12370f6", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/getIntrospectionQuery.js": "81125e8afec11871bf532dff9dd523016292c5a57f84d0c04a142a6704ba1572", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/getOperationAST.js": "5fef27e56f708c5336a78e611f08627122a6673d10ea1db6f5466c6f984ad9a2", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/getOperationRootType.js": "58addddae7feabecce40191d836f7e65a9478cf0d9df6c360c4c30e6f5f6f09c", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/index.js": "d0e34dcf95627362527e93e7eaa970caee63b40ed04ff1da320b6426484c8278", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/introspectionFromSchema.js": "50a2202f28b25e98bc496f151de8fc6b2feeb81bab9739fee97f66ac30ef8652", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/lexicographicSortSchema.js": "ad2b935289966fc75700a3b511b86e79130d505399b0c2de052e9915624e2c2a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/printSchema.js": "104a4666bf3e745ad1fbd30c227d3e06902a2b255c95e29183dcd6d98fad76be", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/separateOperations.js": "ac7e7789fddc0fcf2b439897fce86601a16c6d3f4e20b7f6837278bcf1c36a56", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/stripIgnoredCharacters.js": "be44c652bfa218b18e5d4387a068d2e431d75e770ddc38ced6eb95a0b8854eab", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/typeComparators.js": "dcda7fffeaf3999bfd993a4faac36547dace3ec3f78d7686f4334f86a6c3aec0", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/typeFromAST.js": "99cd615599626259b8e2b9974e22bf8beb495f541b20d78f4a01d48de2f97942", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/valueFromAST.js": "26098b7e5074d408f877f8b4566e6183cd77f076ee2e3f1413e53c4677e150ce", - "https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/valueFromASTUntyped.js": "9db575f95392635bb02dc417d920e9ae6552b2fa871b19e94ad0cc9ceb83c892", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/ValidationContext.js": "52808fc712f4ef50f79edcb30cb907721e449b55c54225551d2ad09660351dba", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/index.js": "1b91224c90bae4b49c6b8227c190c34cb3600433423c257a672801e12910c06d", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/ExecutableDefinitionsRule.js": "f94c0c866335d1c249d08efa55da0dd6e5954844fa7851b6a0d6b3cd86383819", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/FieldsOnCorrectTypeRule.js": "587eb8a9e7376e58ea6a0bbb12585ca45621a0be102dc7e6c641adee9652e91d", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/FragmentsOnCompositeTypesRule.js": "6a6172831b4e7e6431af810505e23bdf82b7f46d92cfe719226c549a36a75b92", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/KnownArgumentNamesRule.js": "2ab0571f6a6b9c5486b76a09955efde97ef3ff7e9cc06ecfcec8e1f830b8e79b", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/KnownDirectivesRule.js": "57f4c9df617702d3695c3010f6822641b4c7343c65a4873bab6b29c911424e2c", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/KnownFragmentNamesRule.js": "24273c31aa208652113a2e635853c336227a2756fc642ece6e977bd9c4ccb094", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/KnownTypeNamesRule.js": "b600453083f32688c43f2225399439b4f6312903397572b947ea4fc80ac17b52", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/LoneAnonymousOperationRule.js": "ec860c3ce338509b1e407c5cea36aa6e97e34ed207e5cdf719d6f7fa4026398c", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/LoneSchemaDefinitionRule.js": "d3e40186003d89bd45c751156a6df69af6970beb5493c891cd79e1eaab734968", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/NoFragmentCyclesRule.js": "1445f37ad14819d117371c70acdc725459d89b9a8773dd2c15e131f5d71b92ac", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/NoUndefinedVariablesRule.js": "7aaf4409398526fef667fa6cede56b60824166409247b62c2f74b2c4a1ea5497", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/NoUnusedFragmentsRule.js": "9d1c9b11d6287f077cff8661c704f9aa0fdf53de0b4894c4e7fe29597a32a704", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/NoUnusedVariablesRule.js": "8d477bdbc19eb02556e7899b97b147511ccb69777ae29136c12e2f49d6c7a264", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/OverlappingFieldsCanBeMergedRule.js": "5f74a4c076f8c18ce6371bfda2d213c4461655f830fb506569a1cc71faa56e96", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/PossibleFragmentSpreadsRule.js": "0bf927e91d4103c630ef3a10b586586ababb1412874917fed1b5b908d40bc54b", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/PossibleTypeExtensionsRule.js": "2c1f8a1371656ff8c992d0e4df5500ecb36475286b5f131d9a880018baf00c73", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/ProvidedRequiredArgumentsRule.js": "03d3af937d5e08c544a10911770f0660ed6eb18d2b8877acc677aac90583f86e", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/ScalarLeafsRule.js": "374bba26d4e9e085560ef20179604955ae191e7c68c85f2f1905fb68686bc079", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/SingleFieldSubscriptionsRule.js": "8a748eb20d1b21fee38aaeae7b368893f113b5b35bbd0fd94c22df072d23e0be", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueArgumentNamesRule.js": "fe7fafd8d4a5efe66f0d690ef868f55f46c78512cf98abc1bd6ab4c175b86d79", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueDirectiveNamesRule.js": "6664f72231dfa4fa4337ffeed4868a95e91970825bd45f77d7d4f92bcb49bb50", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueDirectivesPerLocationRule.js": "0aedfb8194bd8630b84e344a8ff0f6572bb169ad290a18c8391000324c4d8a03", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueEnumValueNamesRule.js": "fbfec070d1c6ca58514f0e459b7dc5c8fe505d81bd1323509802d65db1463272", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueFieldDefinitionNamesRule.js": "3da7356eaaf7ae41bf5b86c7c55c4049b962bd9d5339252a90cc46538714b694", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueFragmentNamesRule.js": "523f0dfeb1ab059bd396d240376e3b602d32405826dd53d8b2e575aaebd7c055", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueInputFieldNamesRule.js": "433e878697932b4b5cd82dc9282f10650d568360afcd68a6130b9a54ef21e7f1", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueOperationNamesRule.js": "1de59fc010ff56ef74c856d23f949c523de78f299e9aa7c6d882f1f49959e3be", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueOperationTypesRule.js": "2faa2b9c0e9c0197f053162e1356dbfa819e02f0c294271873576ac4ea5786fc", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueTypeNamesRule.js": "d01564b48843b9a316b314f1f9079a25d381bbe02bb47bc47dca0a144fdb596f", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/UniqueVariableNamesRule.js": "5522d2278b64aaf70c30d0dc5e37f70bdbde0201bd6c06c136e0d9d50b0a3c4a", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/ValuesOfCorrectTypeRule.js": "f50002aac89fa875cfb594c7696d0d6eaa8ad39d699e3d0550cda34799797faa", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/VariablesAreInputTypesRule.js": "13337bf58985546627c8060c3ff1201dd21bb6d67e5293819ad0eefa8196a144", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/rules/VariablesInAllowedPositionRule.js": "3032c0af7f9287cb10230685807e1460f12775e24ac76f88496769ad0acc43f0", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/specifiedRules.js": "0e2f6954eb79701a15731f528209a5616ee028133cbf7d4fbecb4e88ac5028c5", - "https://deno.land/x/graphql_deno@v15.0.0/lib/validation/validate.js": "44bf2bc96845af0bf77e4421f0a05ee57cdc2a688abb32ff6270eacc501327d1", - "https://deno.land/x/graphql_deno@v15.0.0/lib/version.js": "a135d0beee3fa1405733453e86f1c887e4771d56357504dd78eaa872c0a2b482", - "https://deno.land/x/graphql_deno@v15.0.0/mod.ts": "c85d6ab2a9c22ff494bfaa5f662507249650419af6c6c27a7a5c8175de61870d", - "https://deno.land/x/graphql_request@v4.1.0/mod.ts": "a24c6f401b8253bb84c773583bf2b37b1b74eec960639f36eb874ca17972f9e7", - "https://deno.land/x/graphql_request@v4.1.0/src/createRequestBody.ts": "80b40c902de346622c222a068c4f96679a1bb3233c9b31d728518566ca4e679a", - "https://deno.land/x/graphql_request@v4.1.0/src/index.ts": "15194aba3fdfc1ba896e7fcf9c11227dd1962b635c7d2101e23ed2e0a57cd900", - "https://deno.land/x/graphql_request@v4.1.0/src/parseArgs.ts": "d00abde98728be96c902debd7ee5032b05f69fb6bb00f86a2b5adf09159541aa", - "https://deno.land/x/graphql_request@v4.1.0/src/types.dom.ts": "56432fd7ce0aff31542ec7198ff1a3ea5e3d62fda009510f78bf9c0fc47af13e", - "https://deno.land/x/graphql_request@v4.1.0/src/types.ts": "63a98d9f8efb1d6a580bbef62861c764f0f0b0d3dda04674f4d9a32d6490440b", - "https://deno.land/x/http_client@v0.0.3/mod.ts": "0ab3466319925fbc7c118024bd96e86f7cded7f8e7adade336a86e57d65229f4", - "https://deno.land/x/http_client@v0.0.3/src/client.ts": "2e4a9b7e735c2fcf6569b590c1390ffe6560de175d26bc77f32cd7769ab42d95", - "https://deno.land/x/http_client@v0.0.3/src/common.ts": "048ba99bcd9527cef687d44c6d5d1cf2cdbf2968149d233455098c105b50ef76", - "https://deno.land/x/http_client@v0.0.3/src/helpers.ts": "c91aec2f0158e0f6284a98947065b08de21818dcdaa4cac78daacfad5d332146", - "https://deno.land/x/http_client@v0.0.3/src/request.ts": "39ef37dbd2ea8115c284e269259feb4c839367410687a2371580206d6b2a7210", - "https://deno.land/x/http_client@v0.0.3/src/response.ts": "1f47769856e63fb22048ca9ce0c7282ed75905a5209b20619fb79b47d8ee030b", - "https://deno.land/x/monads@v0.5.10/either/either.ts": "89f539c7d50bd0ee8d9b902f37ef16687c19b62cc9dd23454029c97fbfc15cc6", - "https://deno.land/x/monads@v0.5.10/index.ts": "f0e90b8c1dd767efca137d682ac1a19b2dbae4d1990b8a79a40b4e054c69b3d6", - "https://deno.land/x/monads@v0.5.10/mod.ts": "f1b16a34d47e58fdf9f1f54c49d2fe6df67b3d2e077e21638f25fbe080eee6cf", - "https://deno.land/x/monads@v0.5.10/option/option.ts": "76ef03c3370207112759f932f39aab04999cdd1a5c5a954769b3868602faf883", - "https://deno.land/x/monads@v0.5.10/result/result.ts": "bb482b7b90949d3a67e78b4b0dd949774eccaa808df39ac83f6a585526edeb37", + "https://deno.land/x/jszip@0.11.0/mod.ts": "5661ddc18e9ac9c07e3c5d2483bc912a7022b6af0d784bb7b05035973e640ba1", + "https://deno.land/x/jszip@0.11.0/types.ts": "1528d1279fbb64dd118c371331c641a3a5eff2b594336fb38a7659cf4c53b2d1", "https://deno.land/x/outdent@v0.8.0/src/index.ts": "6dc3df4108d5d6fedcdb974844d321037ca81eaaa16be6073235ff3268841a22", "https://deno.land/x/which@0.3.0/mod.ts": "3e10d07953c14e4ddc809742a3447cef14202cdfe9be6678a1dfc8769c4487e6", - "https://deno.land/x/zip@v1.2.5/compress.ts": "43d9f4440960d15a85aec58f5d365acc25530d3d4186b2f5f896c090ecac20e8", - "https://deno.land/x/zip@v1.2.5/decompress.ts": "0bce3d453726f686274fab3f6c19b72b5e74223a00d89c176b1de49a5dd5528d", - "https://deno.land/x/zip@v1.2.5/deps.ts": "79548387594b3ae1efaaa870b5a507c4d6bedede13dbd5d4ad42f6cda0aeef86", - "https://deno.land/x/zip@v1.2.5/mod.ts": "28eecbc3e1e5adf564f4aa465e64268713a05653104bacdcb04561533f8caf57", - "https://deno.land/x/zip@v1.2.5/utils.ts": "43c323f2b79f9db1976c5739bbb1f9cced20e8077ca7e7e703f9d01d4330bd9d", - "https://deno.land/x/zipjs@v2.7.31/index.js": "7c71926e0c9618e48a22d9dce701131704fd3148a1d2eefd5dba1d786c846a5f", - "https://deno.land/x/zipjs@v2.7.31/lib/core/codec-pool.js": "e5ab8ee3ec800ed751ef1c63a1bd8e50f162aa256a5f625d173d7a32e76e828c", - "https://deno.land/x/zipjs@v2.7.31/lib/core/codec-worker.js": "744b7e149df6f2d105afbcb9cce573df2fbf7bf1c2e14c3689220c2dedeabe65", - "https://deno.land/x/zipjs@v2.7.31/lib/core/configuration.js": "baa316a63df2f8239f9d52cd4863eaedaddd34ad887b7513588da75d19e84932", - "https://deno.land/x/zipjs@v2.7.31/lib/core/constants.js": "14fe1468b87cd0fe20c6f1fec916485f875d8592beba94c9241af4cbd12dd88f", - "https://deno.land/x/zipjs@v2.7.31/lib/core/io.js": "4c4e86ba187540be533003271f222183455897cd144cb542539e9480882c2dda", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/aes-crypto-stream.js": "8242f23a221c496996071b68d498e821ca6b8f20d04bdf74ee0a589ac3367cc5", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/codec-stream.js": "685f1120b94b6295dcd61b195d6202cd24a5344e4588dc52f42e8ac0f9dfe294", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/codecs/crc32.js": "dfdde666f72b4a5ffc8cf5b1451e0db578ce4bd90de20df2cff5bfd47758cb23", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/codecs/deflate.js": "08c1b24d1845528f6db296570d690ecbe23c6c01c6cb26b561e601e770281c3a", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/codecs/inflate.js": "55d00eed332cf2c4f61e2ee23133e3257768d0608572ee3f9641a2921c3a6f67", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/codecs/sjcl.js": "462289c5312f01bba8a757a7a0f3d8f349f471183cb4c49fb73d58bba18a5428", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/common-crypto.js": "4d462619848d94427fcd486fd94e5c0741af60e476df6720da8224b086eba47e", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/crc32-stream.js": "10e26bd18df0e1e89d61a62827a1a1c19f4e541636dd0eccbd85af3afabce289", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/stream-adapter.js": "9e7f3fe1601cc447943cd37b5adb6d74c6e9c404d002e707e8eace7bc048929c", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/zip-crypto-stream.js": "19305af1e8296e7fa6763f3391d0b8149a1e09c659e1d1ff32a484448b18243c", - "https://deno.land/x/zipjs@v2.7.31/lib/core/streams/zip-entry-stream.js": "01d4dc0843e8c43d32454cbb15e4d1f9b7122ab288d7650129d010df54bc0b8e", - "https://deno.land/x/zipjs@v2.7.31/lib/core/util/cp437-decode.js": "d665ded184037ffe5d255be8f379f90416053e3d0d84fac95b28f4aeaab3d336", - "https://deno.land/x/zipjs@v2.7.31/lib/core/util/decode-text.js": "c04a098fa7c16470c48b6abd4eb4ac48af53547de65e7c8f39b78ae62330ad57", - "https://deno.land/x/zipjs@v2.7.31/lib/core/util/default-mime-type.js": "177ae00e1956d3d00cdefc40eb158cb591d3d24ede452c056d30f98d73d9cd73", - "https://deno.land/x/zipjs@v2.7.31/lib/core/util/encode-text.js": "c51a8947c15b7fe31b0036b69fd68817f54b30ce29502b5c9609d8b15e3b20d9", - "https://deno.land/x/zipjs@v2.7.31/lib/core/util/mime-type.js": "6c6dfa4daf98ef59cd65118073b74f327ceab2ef28140e38934b0d15eb2b5c29", - "https://deno.land/x/zipjs@v2.7.31/lib/core/util/stream-codec-shim.js": "1323016ec3c743942dc887215832badc7f2c1e8dbb37b71c94bf54276d2b281a", - "https://deno.land/x/zipjs@v2.7.31/lib/core/zip-entry.js": "d30a535cd1e75ef98094cd04120f178c103cdc4055d23ff747ffc6a154da8d2d", - "https://deno.land/x/zipjs@v2.7.31/lib/core/zip-fs-core.js": "6fbd3ad9dbf0d07e19e1a59863743d2069f2b5fca691bdd6cd8d052ee7ce0c06", - "https://deno.land/x/zipjs@v2.7.31/lib/core/zip-reader.js": "4e63d88e4eb5f7419e2dc3ccb741014240267a49fb80a9cbcb024149990b532b", - "https://deno.land/x/zipjs@v2.7.31/lib/core/zip-writer.js": "b78c099828ec3134983c259adc4d6118fbfda7f033a7e95de8176a470e9a5a54", - "https://deno.land/x/zipjs@v2.7.31/lib/z-worker-inline.js": "9869579df96d7b75a10c70f231837c418b0cdd0ac26df12f17dade6bbaa6c17a", - "https://deno.land/x/zipjs@v2.7.31/lib/zip-fs.js": "a733360302f5fbec9cc01543cb9fcfe7bae3f35a50d0006626ce42fe8183b63f", "https://deno.land/x/zod@v3.22.4/ZodError.ts": "4de18ff525e75a0315f2c12066b77b5c2ae18c7c15ef7df7e165d63536fdf2ea", "https://deno.land/x/zod@v3.22.4/errors.ts": "5285922d2be9700cc0c70c95e4858952b07ae193aa0224be3cbd5cd5567eabef", "https://deno.land/x/zod@v3.22.4/external.ts": "a6cfbd61e9e097d5f42f8a7ed6f92f93f51ff927d29c9fbaec04f03cbce130fe", @@ -676,10 +393,7 @@ "https://deno.land/x/zod@v3.22.4/locales/en.ts": "a7a25cd23563ccb5e0eed214d9b31846305ddbcdb9c5c8f508b108943366ab4c", "https://deno.land/x/zod@v3.22.4/mod.ts": "64e55237cb4410e17d968cd08975566059f27638ebb0b86048031b987ba251c4", "https://deno.land/x/zod@v3.22.4/types.ts": "724185522fafe43ee56a52333958764c8c8cd6ad4effa27b42651df873fc151e", - "https://esm.sh/extract-files@12.0.0/extractFiles.mjs": "58e5f1fcf50e3a362d9bde0e2c1a624a7d89d60ac2de005afd895d0d452cca62", - "https://esm.sh/extract-files@12.0.0/isExtractableFile.mjs": "2757358bbc9141b93cb27d7e0ba98c1e703172dd58fb7d533a31476419d2416f", - "https://esm.sh/v135/extract-files@12.0.0/denonext/extractFiles.js": "59b30d503c5d9ce135dd76c34ac92cf7e9f85cd129d6be2ff61553bb612ac75f", - "https://esm.sh/v135/extract-files@12.0.0/denonext/isExtractableFile.js": "93462c162e29fc0e8fd7e9de3665dfd7705f03d41d2721d654f177bc2a7fe305", - "https://esm.sh/v135/is-plain-obj@4.1.0/denonext/is-plain-obj.mjs": "d3d86a7174ad7935de7b00f904b6424c103bce530c502efb7f42114cbb1a555f" + "https://esm.sh/jszip@3.7.1": "f3872a819b015715edb05f81d973b5cd05d3d213d8eb28293ca5471fe7a71773", + "https://esm.sh/v135/jszip@3.7.1/denonext/jszip.mjs": "d31d7f9e0de9c6db3c07ca93f7301b756273d4dccb41b600461978fc313504c9" } } diff --git a/deps/ports.ts b/deps/ports.ts index 33d62761..f5eeeada 100644 --- a/deps/ports.ts +++ b/deps/ports.ts @@ -1,8 +1,8 @@ //! This contains dependencies used by plugins export * from "./common.ts"; -export * as zipjs from "https://deno.land/x/zipjs@v2.7.31/index.js"; export { Foras } from "https://deno.land/x/foras@v2.1.4/src/deno/mod.ts"; export * as std_tar from "https://deno.land/std@0.129.0/archive/tar.ts"; export * as std_streams from "https://deno.land/std@0.129.0/streams/mod.ts"; export * as std_io from "https://deno.land/std@0.129.0/io/mod.ts"; +export * as jszip from "https://deno.land/x/jszip@0.11.0/mod.ts"; diff --git a/ghjk.ts b/ghjk.ts index 21d41439..f9a9e74e 100644 --- a/ghjk.ts +++ b/ghjk.ts @@ -23,13 +23,15 @@ import whiz from "./ports/whiz.ts"; // wasm_opt({}); // cargo_insta({}); // jco({}); -// mold({}); -// act({}); +// mold({ +// replaceLd: true, +// }); +act({}); // asdf({ // pluginRepo: "https://github.com/asdf-community/asdf-cmake", // installType: "version", // }); -// protoc({ }); +// protoc({}); // earthly({}); // ruff({}); // whiz({}); diff --git a/host/mod.ts b/host/mod.ts index 0b368068..2699fd40 100644 --- a/host/mod.ts +++ b/host/mod.ts @@ -1,51 +1,33 @@ -import "../setup_logger.ts"; - import { std_path } from "../deps/common.ts"; import { cliffy_cmd } from "../deps/cli.ts"; -import logger from "../utils/logger.ts"; +import logger, { isColorfulTty } from "../utils/logger.ts"; // import { $ } from "../utils/mod.ts"; -import { envDirFromConfig, findConfig, isColorfulTty } from "../utils/mod.ts"; +import { envDirFromConfig } from "../utils/mod.ts"; import validators from "./types.ts"; import * as std_modules from "../modules/std.ts"; import * as deno from "./deno.ts"; -export async function main() { - const configPathIn = Deno.env.get("GHJK_CONFIG") ?? - await findConfig(Deno.cwd()); - if (!configPathIn) { - logger().error("ghjk did not find any `ghjk.ts` config."); - Deno.exit(2); - } - const configPath = std_path.resolve(Deno.cwd(), configPathIn); - const envDir = envDirFromConfig(configPath); +export interface CliArgs { + ghjkDir: string; + configPath: string; +} +export async function cli(args: CliArgs) { + const configPath = std_path.normalize( + std_path.resolve(Deno.cwd(), args.configPath), + ); + const ghjkDir = std_path.normalize( + std_path.resolve(Deno.cwd(), args.ghjkDir), + ); + const envDir = envDirFromConfig(ghjkDir, configPath); logger().debug({ configPath }); logger().debug({ envDir }); - let serializedJson; - switch (std_path.extname(configPath)) { - case "": - logger().warning("config file has no extension, assuming deno config"); - /* falls through */ - case ".ts": - serializedJson = await deno.getSerializedConfig( - std_path.toFileUrl(configPath).href, - ); - break; - // case ".jsonc": - case ".json": - serializedJson = JSON.parse(await Deno.readTextFile(configPath)); - break; - default: - throw new Error( - `unrecognized ghjk config type provided at path: ${configPath}`, - ); - } - const serializedConfig = validators.serializedConfig.parse(serializedJson); + const serializedConfig = await serializeConfig(configPath); const ctx = { configPath, envDir }; - let cmd: cliffy_cmd.Command = new cliffy_cmd.Command() + let cmd = new cliffy_cmd.Command() .name("ghjk") .version("0.1.0") // FIXME: better way to resolve version .description("Programmable runtime manager.") @@ -53,15 +35,51 @@ export async function main() { this.showHelp(); }) .command( - "config", + "print", new cliffy_cmd.Command() - .description("Print the extracted config from the ghjk.ts file") + .description("Emit different discovored and built values to stdout.") .action(function () { - console.log(Deno.inspect(serializedConfig, { - depth: 10, - colors: isColorfulTty(), - })); - }), + this.showHelp(); + }) + .command( + "ghjk-dir-path", + new cliffy_cmd.Command() + .description("Print the path where ghjk is installed in.") + .action(function () { + console.log(ghjkDir); + }), + ) + .command( + "config-path", + new cliffy_cmd.Command() + .description("Print the path of the ghjk.ts used") + .action(function () { + console.log(configPath); + }), + ) + .command( + "config", + new cliffy_cmd.Command() + .description( + "Print the extracted ans serialized config from the ghjk.ts file", + ) + .action(function () { + console.log(Deno.inspect(serializedConfig, { + depth: 10, + colors: isColorfulTty(), + })); + }), + ) + .command( + "env-dir-path", + new cliffy_cmd.Command() + .description( + "Print the directory the current config's env is housed in.", + ) + .action(function () { + console.log(envDir); + }), + ), ); for (const man of serializedConfig.modules) { const mod = std_modules.map[man.id]; @@ -74,7 +92,28 @@ export async function main() { cmd .command("completions", new cliffy_cmd.CompletionsCommand()) .parse(Deno.args); - // const serializedConfig = validators.serializedConfig.parse( - // serializedJson, - // ); +} + +async function serializeConfig(configPath: string) { + let serializedJson; + switch (std_path.extname(configPath)) { + case "": + logger().warning("config file has no extension, assuming deno config"); + /* falls through */ + case ".ts": + serializedJson = await deno.getSerializedConfig( + std_path.toFileUrl(configPath).href, + ); + break; + // case ".jsonc": + case ".json": + serializedJson = JSON.parse(await Deno.readTextFile(configPath)); + break; + default: + throw new Error( + `unrecognized ghjk config type provided at path: ${configPath}`, + ); + } + const serializedConfig = validators.serializedConfig.parse(serializedJson); + return serializedConfig; } diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..33fadc25 --- /dev/null +++ b/install.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +set -e -u + +GHJK_VERISON="${GHJK_VERISON:-v0.1.0-alpha}" +GHJK_INSTALLER_URL="${GHJK_INSTALLER_URL:-https://raw.github.com/metatypedev/ghjk/$GHJK_VERISON/install.ts}" +GHJK_DIR="${GHJK_DIR:-$HOME/.local/share/ghjk}" +SHELL="${SHELL:-bash}" +DENO_VERSION="${DENO_VERSION:-v1.38.5}" + +# make sure the version is prepended with v +if [ "${DENO_VERSION#"v"}" = "$DENO_VERSION" ]; then + DENO_VERSION="v$DENO_VERSION" +fi + +# if custom deno bin is not set, install one +if [ -z "${GHJK_INSTALL_DENO_EXEC+x}" ]; then + echo "GHJK_INSTALL_DENO_EXEC not set, installing deno $DENO_VERSION for ghjk" + if ! command -v curl >/dev/null; then + echo "Error: curl is required to install deno for ghjk." 1>&2 + exit 1 + fi + + DENO_INSTALL="$GHJK_DIR/tmp/deno-install" + curl -fsSL https://deno.land/x/install/install.sh | DENO_INSTALL="$DENO_INSTALL" sh -s "$DENO_VERSION" + + # disinterr the deno bin from the install dir + mv "$DENO_INSTALL/bin/deno" "$GHJK_DIR" + rm "$DENO_INSTALL" -r + + GHJK_INSTALL_DENO_EXEC="$GHJK_DIR/deno" +fi + +( + # pass all capitalized local vars as env vars + export $(set | grep "^[A-Z_][A-Z0-9_]*=" | cut -d= -f1); + "$GHJK_INSTALL_DENO_EXEC" run -A "$GHJK_INSTALLER_URL" +) diff --git a/install.ts b/install.ts index e23db5f9..da7b7e10 100644 --- a/install.ts +++ b/install.ts @@ -1,9 +1,32 @@ -//! Setup ghjk for the CWD +//! Install ghjk for the current user -import { install } from "./install/mod.ts"; +import "./setup_logger.ts"; +import { defaultInstallArgs, detectShell, install } from "./install/mod.ts"; if (import.meta.main) { - await install(); + const skipBinInstall = Deno.env.get("GHJK_INSTALL_SKIP_EXE"); + const noLockfile = Deno.env.get("GHJK_INSTALL_NO_LOCKFILE"); + + await install({ + ...defaultInstallArgs, + ghjkDir: Deno.env.get("GHJK_DIR") ?? defaultInstallArgs.ghjkDir, + skipExecInstall: !!skipBinInstall && skipBinInstall != "0" && + skipBinInstall != "false", + shellsToHook: Deno.env.get("GHJK_INSTALL_HOOK_SHELLS") + ?.split(",") + ?.map((str) => str.trim()) + ?.filter((str) => str.length > 0) ?? + [ + await detectShell(), + ], + ghjkExecInstallDir: Deno.env.get("GHJK_INSTALL_EXE_DIR") ?? + defaultInstallArgs.ghjkExecInstallDir, + ghjkExecDenoExec: Deno.env.get("GHJK_INSTALL_DENO_EXEC") ?? + defaultInstallArgs.ghjkExecDenoExec, + shellHookMarker: Deno.env.get("GHJK_INSTALL_HOOK_MARKER") ?? + defaultInstallArgs.shellHookMarker, + noLockfile: !!noLockfile && noLockfile != "0" && noLockfile != "false", + }); } else { throw new Error( "unexpected ctx: if you want to access the ghjk installer, import `install` from ./install/mod.ts", diff --git a/install/hooks/bash.sh b/install/hooks/bash.sh index ff44a681..3a767f79 100644 --- a/install/hooks/bash.sh +++ b/install/hooks/bash.sh @@ -10,16 +10,16 @@ ansi_yel='\033[0;33m' ansi_nc='\033[0m' # No Color init_ghjk() { - if [ -n "${GHJK_CLEANUP+x}" ]; then - eval "$GHJK_CLEANUP" + if [ -n "${GHJK_CLEANUP_POSIX+x}" ]; then + eval "$GHJK_CLEANUP_POSIX" fi - unset GHJK_CLEANUP + unset GHJK_CLEANUP_POSIX unset GHJK_LAST_LOADER_PATH unset GHJK_LAST_LOADER_TS cur_dir=$PWD while [ "$cur_dir" != "/" ]; do if [ -e "$cur_dir/ghjk.ts" ]; then - envDir="$HOME/.local/share/ghjk/envs/$(printf "$cur_dir" | tr '/' '.')" + envDir="__GHJK_DIR__/envs/$(printf "$cur_dir" | tr '/' '.')" if [ -d "$envDir" ]; then export GHJK_LAST_LOADER_PATH="$envDir/loader.sh" export GHJK_LAST_LOADER_TS=$(stat -c "%Y" "$GHJK_LAST_LOADER_PATH" | tr -d '\n') @@ -39,7 +39,7 @@ init_ghjk() { done } -# onlt load bash-prexec if we detect bash +# only load bash-prexec if we detect bash # bash-preexec itslef only executes if it detects bash # but even reliably resolving it's address # requires bash extensions. @@ -54,9 +54,8 @@ export LAST_PWD="$PWD" precmd() { if [ "$LAST_PWD" != "$PWD" ] || ( # if the last detected loader has been touched - [ -n "${GHJK_LAST_LOADER_PATH+x}" ] && [ $(stat -c "%Y" "$GHJK_LAST_LOADER_PATH" | tr -d '\n') != $(("$GHJK_LAST_LOADER_TS")) ] + [ -n "${GHJK_LAST_LOADER_PATH+x}" ] && [ $(stat -c "%Y" "$GHJK_LAST_LOADER_PATH" | tr -d '\n') != "$GHJK_LAST_LOADER_TS" ] ); then - echo "got here" init_ghjk export LAST_PWD="$PWD" fi diff --git a/install/hooks/fish.fish b/install/hooks/fish.fish index a12e3b9a..6068ab8b 100644 --- a/install/hooks/fish.fish +++ b/install/hooks/fish.fish @@ -1,14 +1,14 @@ function init_ghjk - if set --query GHJK_CLEANUP - eval $GHJK_CLEANUP + if set --query GHJK_CLEANUP_FISH + eval $GHJK_CLEANUP_FISH end - set --erase GHJK_CLEANUP + set --erase GHJK_CLEANUP_FISH set --erase GHJK_LAST_LOADER_PATH set --erase GHJK_LAST_LOADER_TS set --local cur_dir $PWD while test $cur_dir != "/" if test -e $cur_dir/ghjk.ts - set --local envDir $HOME/.local/share/ghjk/envs/(string replace --all / . $cur_dir) + set --local envDir __GHJK_DIR__/envs/(string replace --all / . $cur_dir) if test -d $envDir set -g -x GHJK_LAST_LOADER_PATH $envDir/loader.fish set -g -x GHJK_LAST_LOADER_TS (stat -c "%Y" $envDir/loader.fish | tr -d '\n') @@ -32,14 +32,18 @@ end function ghjk_prompt_hook --on-event fish_prompt # only init if the loader has been modified - if set --query GHJK_LAST_LOADER_PATH; and test (stat -c "%Y" $GHJK_LAST_LOADER_PATH | tr -d '\n') != $GHJK_LAST_LOADER_TS + if test ! $ghjk_prompt_init_flag; and set --query GHJK_LAST_LOADER_PATH; and test (stat -c "%Y" $GHJK_LAST_LOADER_PATH | tr -d '\n') != $GHJK_LAST_LOADER_TS init_ghjk + set -g ghjk_prompt_init_flag true end + set -g ghjk_prompt_init_flag false end # try to detect ghjk.ts on each change of PWD function ghjk_pwd_hook --on-variable PWD init_ghjk + # set the flag so that the prompt hook doesn't load it again + set -g ghjk_prompt_init_flag true end # try loading any relevant ghjk.ts right away diff --git a/install/mod.ts b/install/mod.ts index 5833acdb..548f4cee 100644 --- a/install/mod.ts +++ b/install/mod.ts @@ -3,11 +3,9 @@ // TODO: support for different environments to use different versions of ghjk -import "../setup_logger.ts"; import logger from "../utils/logger.ts"; import { std_fs, std_path } from "../deps/cli.ts"; -import { dirs, importRaw } from "../utils/mod.ts"; -import { spawnOutput } from "../utils/mod.ts"; +import { $, dirs, importRaw } from "../utils/mod.ts"; // null means it should be removed (for cleaning up old versions) const getHooksVfs = async () => ({ @@ -29,34 +27,36 @@ const getHooksVfs = async () => ({ ), }); -async function detectShell(): Promise { +export async function detectShell(): Promise { let path = Deno.env.get("SHELL"); if (!path) { try { - path = await spawnOutput([ - "ps", - "-p", - String(Deno.ppid), - "-o", - "comm=", - ]); + path = await $`ps -p ${Deno.ppid} -o comm=`.text(); } catch (err) { throw new Error(`cannot get parent process name: ${err}`); } } return std_path.basename(path, ".exe").toLowerCase().trim(); } -async function unpackVFS(baseDir: string): Promise { - await Deno.mkdir(baseDir, { recursive: true }); - const hookVfs = await getHooksVfs(); - for (const [subpath, content] of Object.entries(hookVfs)) { +async function unpackVFS( + vfs: Record, + baseDir: string, + replacements: [RegExp, string][], +): Promise { + await $.path(baseDir).ensureDir(); + + for (const [subpath, content] of Object.entries(vfs)) { const path = std_path.resolve(baseDir, subpath); if (content === null) { - await Deno.remove(path); + await $.path(baseDir).remove({ recursive: true }); } else { - await Deno.mkdir(std_path.dirname(path), { recursive: true }); - await Deno.writeTextFile(path, content.trim()); + let text = content.trim(); + for (const [re, repl] of replacements) { + text = text.replace(re, repl); + } + await $.path(std_path.dirname(path)).ensureDir(); + await $.path(path).writeText(text); } } } @@ -91,54 +91,121 @@ async function filterAddFile( await Deno.writeTextFile(path, lines.join("\n")); } -export async function install() { +export interface InstallArgs { + homeDir: string; + ghjkDir: string; + shellsToHook: string[]; + /// The mark used when adding the hook to the user's shell rcs + /// Override t + shellHookMarker: string; + /// The ghjk bin is optional, one can always invoke it + /// using `deno run --flags uri/to/ghjk/main.ts`; + skipExecInstall: boolean; + /// The directory in which to install the ghjk exec + /// Preferrably, one that's in PATH + ghjkExecInstallDir: string; + /// the deno exec to be used by the ghjk executable + /// by default will be "deno" i.e. whatever the shell resolves that to + ghjkExecDenoExec: string; + // Disable using a lockfile for the ghjk command + noLockfile: boolean; +} + +export const defaultInstallArgs: InstallArgs = { + ghjkDir: std_path.resolve(dirs().shareDir, "ghjk"), + homeDir: dirs().homeDir, + shellsToHook: [], + shellHookMarker: "ghjk-hook-default", + skipExecInstall: true, + // TODO: respect xdg dirs + ghjkExecInstallDir: std_path.resolve(dirs().homeDir, ".local", "bin"), + ghjkExecDenoExec: Deno.execPath(), + noLockfile: false, +}; +export async function install( + args: InstallArgs = defaultInstallArgs, +) { + logger().debug("installing", args); if (Deno.build.os == "windows") { throw new Error("windows is not yet supported :/"); } - const { homeDir, shareDir } = dirs(); - logger().debug("installing hooks", { shareDir }); - await unpackVFS(shareDir); - const shell = await detectShell(); - if (shell === "fish") { - await filterAddFile( - std_path.resolve(homeDir, ".config/fish/config.fish"), - /\.local\/share\/ghjk\/env/, - ". $HOME/.local/share/ghjk/env.fish", - ); - } else if (shell === "bash") { - await filterAddFile( - std_path.resolve(homeDir, ".bashrc"), - /\.local\/share\/ghjk\/env/, - ". $HOME/.local/share/ghjk/env.sh", - ); - } else if (shell === "zsh") { - await filterAddFile( - std_path.resolve(homeDir, ".zshrc"), - /\.local\/share\/ghjk\/env/, - // NOTE: we use the posix for zsh - ". $HOME/.local/share/ghjk/env.sh", - ); - } else { - throw new Error(`unsupported shell: ${shell}`); + const ghjkDir = std_path.resolve( + Deno.cwd(), + std_path.normalize(args.ghjkDir), + ); + // const hookVfs = ; + logger().debug("unpacking vfs", { ghjkDir }); + await unpackVFS( + await getHooksVfs(), + ghjkDir, + [[/__GHJK_DIR__/g, ghjkDir]], + ); + for (const shell of args.shellsToHook) { + const { homeDir } = args; + if (shell === "fish") { + const rcPath = std_path.resolve(homeDir, ".config/fish/config.fish"); + logger().debug("installing hook", { + ghjkDir, + shell, + marker: args.shellHookMarker, + rcPath, + }); + await filterAddFile( + rcPath, + new RegExp(args.shellHookMarker, "g"), + `. ${ghjkDir}/env.fish # ${args.shellHookMarker}`, + ); + } else if (shell === "bash") { + const rcPath = std_path.resolve(homeDir, ".bashrc"); + logger().debug("installing hook", { + ghjkDir, + shell, + marker: args.shellHookMarker, + rcPath, + }); + await filterAddFile( + rcPath, + new RegExp(args.shellHookMarker, "g"), + `. ${ghjkDir}/env.sh # ${args.shellHookMarker}`, + ); + } else if (shell === "zsh") { + const rcPath = std_path.resolve(homeDir, ".zshrc"); + logger().debug("installing hook", { + ghjkDir, + shell, + marker: args.shellHookMarker, + rcPath, + }); + await filterAddFile( + rcPath, + new RegExp(args.shellHookMarker, "g"), + // NOTE: we use the posix hook for zsh as well + `. ${ghjkDir}/env.sh # ${args.shellHookMarker}`, + ); + } else { + throw new Error(`unsupported shell: ${shell}`); + } } - const skipBinInstall = Deno.env.get("GHJK_SKIP_EXE_INSTALL"); - if (!skipBinInstall && skipBinInstall != "0" && skipBinInstall != "false") { + if (!args.skipExecInstall) { switch (Deno.build.os) { case "linux": case "freebsd": case "solaris": case "illumos": case "darwin": { - // TODO: respect xdg dirs - const exeDir = Deno.env.get("GHJK_EXE_INSTALL_DIR") ?? - std_path.resolve(homeDir, ".local", "bin"); - await std_fs.ensureDir(exeDir); - const exePath = std_path.resolve(exeDir, `ghjk`); + await std_fs.ensureDir(args.ghjkExecInstallDir); + const exePath = std_path.resolve(args.ghjkExecInstallDir, `ghjk`); logger().debug("installing executable", { exePath }); + const lockFlag = args.noLockfile + ? "--no-lock" + : `--lock $GHJK_DIR/deno.lock`; await Deno.writeTextFile( exePath, `#!/bin/sh -deno run --unstable-worker-options -A ${import.meta.resolve("../main.ts")} $*`, +GHJK_DIR="$\{GHJK_DIR:-${ghjkDir}}" +${args.ghjkExecDenoExec} run --unstable-worker-options -A ${lockFlag} ${ + import.meta.resolve("../main.ts") + } $*`, { mode: 0o700 }, ); break; diff --git a/main.ts b/main.ts index 3433dc66..c65e2c49 100755 --- a/main.ts +++ b/main.ts @@ -1,9 +1,23 @@ #! /usr/bin/env -S deno run --unstable-worker-options -A -import { main } from "./host/mod.ts"; +import "./setup_logger.ts"; +import { cli } from "./host/mod.ts"; +import { std_path } from "./deps/common.ts"; +import logger from "./utils/logger.ts"; +import { dirs, findConfig } from "./utils/mod.ts"; if (import.meta.main) { - await main(); + const configPath = Deno.env.get("GHJK_CONFIG") ?? + await findConfig(Deno.cwd()); + if (!configPath) { + logger().error("ghjk did not find any `ghjk.ts` config."); + Deno.exit(2); + } + await cli({ + ghjkDir: Deno.env.get("GHJK_DIR") ?? + std_path.resolve(dirs().shareDir, "ghjk"), + configPath: std_path.resolve(Deno.cwd(), configPath), + }); } else { throw new Error( "unexpected ctx: if you want to run the ghjk cli, import `main` from ./host/mod.ts", diff --git a/mod.ts b/mod.ts index 3c2f2857..ec044f97 100644 --- a/mod.ts +++ b/mod.ts @@ -10,6 +10,7 @@ import { } from "./modules/ports/types.ts"; import type { SerializedConfig } from "./host/types.ts"; import logger from "./utils/logger.ts"; +import { $ } from "./utils/mod.ts"; import * as std_ports from "./modules/ports/std.ts"; import { std_modules } from "./modules/mod.ts"; @@ -57,4 +58,4 @@ export const ghjk = Object.freeze({ getConfig: Object.freeze(getConfig), }); -export { logger }; +export { $, logger }; diff --git a/modules/ports/ambient.ts b/modules/ports/ambient.ts index 89ae033a..6efe032c 100644 --- a/modules/ports/ambient.ts +++ b/modules/ports/ambient.ts @@ -6,7 +6,7 @@ import { type ListBinPathsArgs, PortBase, } from "./types.ts"; -import { ChildError, spawnOutput } from "../../utils/mod.ts"; +import { $ } from "../../utils/mod.ts"; export class AmbientAccessPort extends PortBase { constructor(public manifest: AmbientAccessPortManifest) { @@ -21,20 +21,15 @@ export class AmbientAccessPort extends PortBase { const execPath = await this.pathToExec(); let versionOut; try { - versionOut = await spawnOutput([ - execPath, - this.manifest.versionExtractFlag, - ]); + versionOut = await $`${execPath} ${this.manifest.versionExtractFlag}` + .text(); } catch (err) { - if (err instanceof ChildError) { - new Error( - `error trying to get version output for "${this.manifest.name}@${this.manifest.version}" using command ${execPath} ${this.manifest.versionExtractFlag}: ${err}`, - { - cause: err, - }, - ); - } - throw err; + throw new Error( + `error trying to get version output for "${this.manifest.name}@${this.manifest.version}" using command ${execPath} ${this.manifest.versionExtractFlag}: ${err}`, + { + cause: err, + }, + ); } const extractionRegex = new RegExp( this.manifest.versionExtractRegex, @@ -68,18 +63,18 @@ export class AmbientAccessPort extends PortBase { } async pathToExec(): Promise { try { - const output = await spawnOutput(["which", this.manifest.execName]); - return output.trim(); - } catch (err) { - if (err instanceof ChildError) { - new Error( - `error trying to get exec path for "${this.manifest.name}@${this.manifest.version}" for exec name ${this.manifest.execName}: ${err}`, - { - cause: err, - }, - ); + const out = await $.which(this.manifest.execName); + if (!out) { + throw Error("not found"); } - throw err; + return out; + } catch (err) { + throw new Error( + `error trying to get exec path for "${this.manifest.name}@${this.manifest.version}" for exec name ${this.manifest.execName}: ${err}`, + { + cause: err, + }, + ); } } } diff --git a/modules/ports/asdf.ts b/modules/ports/asdf.ts index b88637be..0ca94b80 100644 --- a/modules/ports/asdf.ts +++ b/modules/ports/asdf.ts @@ -9,11 +9,10 @@ import { type TheAsdfPortManifest, } from "./types.ts"; import { + $, depBinShimPath, getInstallId, pathWithDepShims, - spawn, - spawnOutput, } from "../../utils/mod.ts"; // import * as std_ports from "../std.ts"; import { std_fs, std_path } from "../../deps/common.ts"; @@ -60,16 +59,9 @@ export class AsdfPort extends PortBase { const tmpCloneDirPath = await Deno.makeTempDir({ prefix: `ghjk_asdf_clone_${installId}_`, }); - await spawn( - [ - depBinShimPath(git_aa_id, "git", depShims), - "clone", - installConfig.pluginRepo, - "--depth", - "1", - tmpCloneDirPath, - ], - ); + await $`${ + depBinShimPath(git_aa_id, "git", depShims) + } clone ${installConfig.pluginRepo} --depth 1 ${tmpCloneDirPath}`; await std_fs.copy( tmpCloneDirPath, pluginDir, @@ -80,9 +72,8 @@ export class AsdfPort extends PortBase { } async listAll(_args: ListAllArgs): Promise { - const out = await spawnOutput([ - std_path.resolve(this.pluginDir, "bin", "list-all"), - ]); + const out = await $`${std_path.resolve(this.pluginDir, "bin", "list-all")}` + .text(); return out.split(" ").filter((str) => str.length > 0).map((str) => str.trim() ); @@ -93,15 +84,14 @@ export class AsdfPort extends PortBase { if (!await std_fs.exists(binPath)) { return super.latestStable(args); } - const out = await spawnOutput([binPath], { - env: { + const out = await $`${binPath}` + .env({ PATH: pathWithDepShims(args.depShims), ASDF_INSTALL_TYPE: this.config.installType, // FIXME: asdf requires these vars for latest-stable. this makes no sense! ASDF_INSTALL_VERSION: this.config.version ?? "", // ASDF_INSTALL_PATH: args.installPath, - }, - }); + }).text(); return out.trim(); } @@ -111,14 +101,13 @@ export class AsdfPort extends PortBase { return super.listBinPaths(args); } - const out = await spawnOutput([binPath], { - env: { + const out = await $`${binPath}` + .env({ PATH: pathWithDepShims(args.depShims), ASDF_INSTALL_TYPE: this.config.installType, ASDF_INSTALL_VERSION: args.installVersion, ASDF_INSTALL_PATH: args.installPath, - }, - }); + }).text(); return out.split(" ").filter((str) => str.length > 0).map((str) => str.trim() ); @@ -130,30 +119,24 @@ export class AsdfPort extends PortBase { if (!await std_fs.exists(binPath)) { return; } - await spawn([ - std_path.resolve(this.pluginDir, "bin", "download"), - ], { - env: { + await $`${std_path.resolve(this.pluginDir, "bin", "download")}` + .env({ PATH: pathWithDepShims(args.depShims), ASDF_INSTALL_TYPE: this.config.installType, ASDF_INSTALL_VERSION: args.installVersion, ASDF_INSTALL_PATH: args.installPath, ASDF_DOWNLOAD_PATH: args.downloadPath, - }, - }); + }); } async install(args: InstallArgs) { - await spawn([ - std_path.resolve(this.pluginDir, "bin", "install"), - ], { - env: { + await $`${std_path.resolve(this.pluginDir, "bin", "install")}` + .env({ PATH: pathWithDepShims(args.depShims), ASDF_INSTALL_TYPE: this.config.installType, ASDF_INSTALL_VERSION: args.installVersion, ASDF_INSTALL_PATH: args.installPath, ASDF_DOWNLOAD_PATH: args.downloadPath, ASDF_CONCURRENCY: args.availConcurrency.toString(), - }, - }); + }); } } diff --git a/modules/ports/cargo-binstall.ts b/modules/ports/cargo-binstall.ts new file mode 100644 index 00000000..30822a3a --- /dev/null +++ b/modules/ports/cargo-binstall.ts @@ -0,0 +1,65 @@ +import { type DownloadArgs, type InstallArgs, PortBase } from "./mod.ts"; +import { std_fs, std_path } from "../../deps/ports.ts"; +import logger from "../../utils/logger.ts"; +import { $, depBinShimPath } from "../../utils/mod.ts"; +import * as std_ports from "./std.ts"; + +// TODO: convert to `AsdfPort` like abstraction +// this is a temporarary DRY up + +export abstract class CargoBinstallPort extends PortBase { + abstract crateName: string; + + binName() { + return this.crateName; + } + + async listAll() { + const metadataText = await $.request( + `https://index.crates.io/${this.crateName.slice(0, 2)}/${ + this.crateName.slice(2, 4) + }/${this.crateName}`, + ).text(); + const versions = metadataText + .split("\n") + .filter((str) => str.length > 0) + .map((str) => + JSON.parse(str) as { + vers: string; + } + ); + return versions.map((ver) => ver.vers); + } + + async download(args: DownloadArgs) { + const fileName = this.binName(); + if (await std_fs.exists(std_path.resolve(args.downloadPath, fileName))) { + logger().debug( + `file ${fileName} already downloaded, skipping whole download`, + ); + return; + } + await $`${ + depBinShimPath(std_ports.cbin_ghrel, "cargo-binstall", args.depShims) + } + ${this.crateName} --version ${args.installVersion} + --install-path ${args.tmpDirPath} + --no-confirm --disable-strategies compile --no-track + `; + await std_fs.copy( + args.tmpDirPath, + args.downloadPath, + ); + } + + async install(args: InstallArgs) { + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); + } + await std_fs.copy( + args.downloadPath, + installPath.join("bin").toString(), + ); + } +} diff --git a/modules/ports/ghrel.ts b/modules/ports/ghrel.ts new file mode 100644 index 00000000..62b531d6 --- /dev/null +++ b/modules/ports/ghrel.ts @@ -0,0 +1,37 @@ +import { $ } from "../../utils/mod.ts"; +import { PortBase } from "./types.ts"; + +export abstract class GithubReleasePort extends PortBase { + abstract repoOwner: string; + abstract repoName: string; + + async latestStable(): Promise { + const metadata = await $.withRetries({ + count: 10, + delay: 100, + action: async () => + await $.request( + `https://api.github.com/repos/${this.repoOwner}/${this.repoName}/releases/latest`, + ).json() as { + tag_name: string; + }, + }); + + return metadata.tag_name; + } + + async listAll() { + const metadata = await $.withRetries({ + count: 10, + delay: 100, + action: async () => + await $.request( + `https://api.github.com/repos/${this.repoOwner}/${this.repoName}/releases`, + ).json() as [{ + tag_name: string; + }], + }); + + return metadata.map((rel) => rel.tag_name).reverse(); + } +} diff --git a/modules/ports/sync.ts b/modules/ports/sync.ts index 2628e055..9e9ced92 100644 --- a/modules/ports/sync.ts +++ b/modules/ports/sync.ts @@ -25,14 +25,14 @@ async function writeLoader( `export GHJK_CLEANUP_POSIX="";`, ...Object.entries(env).map(([k, v]) => // NOTE: single quote the port supplied envs to avoid any embedded expansion/execution - `GHJK_CLEANUP_POSIX+="export ${k}='$${k}';"; + `GHJK_CLEANUP_POSIX=$GHJK_CLEANUP_POSIX"export ${k}='$${k}';"; export ${k}='${v}';` ), ...Object.entries(pathVars).map(([k, v]) => // NOTE: double quote the path vars for expansion // single quote GHJK_CLEANUP additions to avoid expansion/exec before eval - `GHJK_CLEANUP_POSIX+='${k}=$(echo "$${k}" | tr ":" "\\n" | grep -vE "^$HOME/\\.local/share/ghjk/envs" | tr "\\n" ":");${k}="\${${k}%:}"'; -${k}="${v}:$${k}"; + `GHJK_CLEANUP_POSIX=$GHJK_CLEANUP_POSIX'${k}=$(echo "$${k}" | tr ":" "\\n" | grep -vE "^${envDir}" | tr "\\n" ":");${k}="\${${k}%:}"'; +export ${k}="${v}:$${k}"; ` ), ].join("\n"), @@ -43,7 +43,7 @@ ${k}="${v}:$${k}"; set --global --export ${k} '${v}';` ), ...Object.entries(pathVars).map(([k, v]) => - `set --global --append GHJK_CLEANUP_FISH 'set --global --path ${k} (string match --invert --regex "^$HOME\\/\\.local\\/share\\/ghjk\\/envs" $${k});'; + `set --global --append GHJK_CLEANUP_FISH 'set --global --path ${k} (string match --invert --regex "^${envDir}" $${k});'; set --global --prepend ${k} ${v}; ` ), @@ -195,6 +195,7 @@ export async function sync(envDir: string, cx: PortsModuleConfig) { C_INCLUDE_PATH: `${envDir}/shims/include`, CPLUS_INCLUDE_PATH: `${envDir}/shims/include`, }; + logger().debug("adding vars to loader", env); // FIXME: prevent malicious env manipulations await writeLoader( envDir, @@ -204,6 +205,7 @@ export async function sync(envDir: string, cx: PortsModuleConfig) { pathVars, ); } + function buildInstallGraph(cx: PortsModuleConfig) { const installs = { all: new Map(), diff --git a/modules/ports/types.ts b/modules/ports/types.ts index 331b876c..0dc30f72 100644 --- a/modules/ports/types.ts +++ b/modules/ports/types.ts @@ -75,7 +75,7 @@ const installConfigBase = zod.object({ portName: zod.string().min(1), }).passthrough(); -const stdInstallConfig = installConfigBase.merge(zod.object({})); +const stdInstallConfig = installConfigBase.merge(zod.object({})).passthrough(); const asdfInstallConfig = installConfigBase.merge( zod.object({ diff --git a/port.ts b/port.ts index 1106dea0..972bbb38 100644 --- a/port.ts +++ b/port.ts @@ -6,17 +6,17 @@ import { type DenoWorkerPortManifest, type DownloadArgs, type InstallConfig, - type PortBase, + PortBase, type PortsModuleConfigBase, registerAmbientPort, registerDenoPort, registerPort, } from "./modules/ports/mod.ts"; -import { std_fs, std_path, std_url } from "./deps/ports.ts"; +import { std_url } from "./deps/ports.ts"; import { initDenoWorkerPort } from "./modules/ports/worker.ts"; import * as asdf from "./modules/ports/asdf.ts"; import logger, { setup as setupLogger } from "./utils/logger.ts"; -import { inWorker } from "./utils/mod.ts"; +import { $, inWorker } from "./utils/mod.ts"; export * from "./modules/ports/mod.ts"; export * from "./utils/mod.ts"; @@ -25,6 +25,7 @@ export { default as logger } from "./utils/logger.ts"; export { initDenoWorkerPort } from "./modules/ports/worker.ts"; export * as asdf from "./modules/ports/asdf.ts"; export type * from "./modules/ports/mod.ts"; +export { GithubReleasePort } from "./modules/ports/ghrel.ts"; export * from "./utils/unarchive.ts"; if (inWorker()) { @@ -90,43 +91,20 @@ export async function downloadFile( mode: 0o666, ...options, }; - const fileDwnPath = std_path.resolve(env.downloadPath, fileName); - if (await std_fs.exists(fileDwnPath)) { + const fileDwnPath = $.path(env.downloadPath).join(fileName); + if (await fileDwnPath.exists()) { logger().debug(`file ${fileName} already downloaded, skipping`); return; } - const tmpFilePath = std_path.resolve( - env.tmpDirPath, - fileName, - ); + const tmpFilePath = $.path(env.tmpDirPath).join(fileName); - const resp = await fetch(url); + await $.request(url) + .showProgress() + .pipeToPath(tmpFilePath, { create: true, mode }); - if (!resp.ok) { - throw new Error( - `${resp.status}: ${resp.statusText} downloading file at ${url}`, - ); - } - const length = resp.headers.get("content-length"); - logger().debug( - `downloading file: `, - { - fileSize: length ? Number(length) / 1024 : "N/A", - url, - to: fileDwnPath, - }, - ); + await $.path(env.downloadPath).ensureDir(); - const dest = await Deno.open( - tmpFilePath, - { create: true, truncate: true, write: true, mode }, - ); - await resp.body!.pipeTo(dest.writable, { preventClose: false }); - await std_fs.ensureDir(env.downloadPath); - await std_fs.copy( - tmpFilePath, - fileDwnPath, - ); + await tmpFilePath.copyFile(fileDwnPath); } export const removeFile = Deno.remove; diff --git a/ports/act.ts b/ports/act.ts index d2bfbe1a..9bc8eda9 100644 --- a/ports/act.ts +++ b/ports/act.ts @@ -1,13 +1,13 @@ import { + $, addInstallGlobal, DownloadArgs, downloadFile, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, std_fs, std_path, std_url, @@ -34,37 +34,10 @@ const repoOwner = "nektos"; const repoName = "act"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; - - listBinPaths(): string[] { - return [ - "act", - ]; - } - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { await downloadFile(args, downloadUrl(args.installVersion, args.platform)); @@ -78,12 +51,13 @@ export class Port extends PortBase { await unarchive(fileDwnPath, args.tmpDirPath); - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( args.tmpDirPath, - args.installPath, + installPath.join("bin").toString(), ); } } diff --git a/ports/cargo-binstall.ts b/ports/cargo-binstall.ts index e1ef6d24..f383fd62 100644 --- a/ports/cargo-binstall.ts +++ b/ports/cargo-binstall.ts @@ -1,4 +1,5 @@ import { + $, addInstallGlobal, DownloadArgs, downloadFile, @@ -7,7 +8,6 @@ import { type PlatformInfo, PortBase, registerDenoPortGlobal, - removeFile, std_fs, std_path, std_url, @@ -38,10 +38,9 @@ export class Port extends PortBase { manifest = manifest; async listAll() { - const metadataRequest = await fetch( + const metadataText = await $.request( `https://index.crates.io/ca/rg/cargo-binstall`, - ); - const metadataText = await metadataRequest.text(); + ).text(); const versions = metadataText .split("\n") .filter((str) => str.length > 0) @@ -65,8 +64,9 @@ export class Port extends PortBase { await unarchive(fileDwnPath, args.tmpDirPath); - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( args.tmpDirPath, diff --git a/ports/cargo-insta.ts b/ports/cargo-insta.ts index 1d184c52..12d905f6 100644 --- a/ports/cargo-insta.ts +++ b/ports/cargo-insta.ts @@ -1,17 +1,9 @@ import { addInstallGlobal, - depBinShimPath, - type DownloadArgs, - InstallArgs, type InstallConfigSimple, - logger, - PortBase, registerDenoPortGlobal, - removeFile, - spawn, - std_fs, - std_path, } from "../port.ts"; +import { CargoBinstallPort } from "../modules/ports/cargo-binstall.ts"; import * as std_ports from "../modules/ports/std.ts"; const manifest = { @@ -33,60 +25,7 @@ export default function install(config: InstallConfigSimple = {}) { }); } -export class Port extends PortBase { +export class Port extends CargoBinstallPort { manifest = manifest; - - async listAll() { - const metadataRequest = await fetch( - `https://index.crates.io/ca/rg/cargo-insta`, - ); - const metadataText = await metadataRequest.text(); - const versions = metadataText - .split("\n") - .filter((str) => str.length > 0) - .map((str) => - JSON.parse(str) as { - vers: string; - } - ); - return versions.map((ver) => ver.vers); - } - - async download(args: DownloadArgs) { - const fileName = "cargo-insta"; - if ( - await std_fs.exists(std_path.resolve(args.downloadPath, fileName)) - ) { - logger().debug( - `file ${fileName} already downloaded, skipping whole download`, - ); - return; - } - await spawn([ - depBinShimPath(std_ports.cbin_ghrel, "cargo-binstall", args.depShims), - "cargo-insta", - `--version`, - args.installVersion, - `--install-path`, - args.tmpDirPath, - `--no-confirm`, - `--disable-strategies`, - `compile`, - `--no-track`, - ]); - await std_fs.copy( - args.tmpDirPath, - args.downloadPath, - ); - } - - async install(args: InstallArgs) { - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); - } - await std_fs.copy( - args.downloadPath, - std_path.resolve(args.installPath, "bin"), - ); - } + crateName = "cargo-insta"; } diff --git a/ports/earthly.ts b/ports/earthly.ts index 3ac4b167..a9492394 100644 --- a/ports/earthly.ts +++ b/ports/earthly.ts @@ -1,15 +1,14 @@ import { + $, addInstallGlobal, DownloadArgs, downloadFile, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, std_fs, - std_path, } from "../port.ts"; const manifest = { @@ -32,32 +31,10 @@ const repoOwner = "earthly"; const repoName = "earthly"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; - - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { const fileName = repoName; @@ -68,16 +45,13 @@ export class Port extends PortBase { } async install(args: InstallArgs) { - const fileName = repoName; - const fileDwnPath = std_path.resolve(args.downloadPath, fileName); - - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } - await std_fs.ensureDir(std_path.resolve(args.installPath, "bin")); await std_fs.copy( - fileDwnPath, - std_path.resolve(args.installPath, "bin", fileName), + args.downloadPath, + installPath.join("bin").toString(), ); } } diff --git a/ports/jco.ts b/ports/jco.ts index d13886d6..1ce4c105 100644 --- a/ports/jco.ts +++ b/ports/jco.ts @@ -1,17 +1,16 @@ import { + $, addInstallGlobal, depBinShimPath, - DownloadArgs, + type DownloadArgs, downloadFile, - InstallArgs, + type InstallArgs, type InstallConfigSimple, - ListAllArgs, + type ListAllArgs, pathWithDepShims, type PlatformInfo, PortBase, registerDenoPortGlobal, - removeFile, - spawn, std_fs, std_path, std_url, @@ -44,13 +43,12 @@ class Port extends PortBase { manifest = manifest; async listAll(_env: ListAllArgs) { - const metadataRequest = await fetch( + const metadataRequest = await $.request( `https://registry.npmjs.org/@bytecodealliance/jco`, + ).header( { - headers: { - // use abbreviated registry info which's still 500kb unzipped - "Accept": "application/vnd.npm.install-v1+json", - }, + // use abbreviated registry info which's still 500kb unzipped + "Accept": "application/vnd.npm.install-v1+json", }, ); const metadata = await metadataRequest.json() as { @@ -76,8 +74,9 @@ class Port extends PortBase { await unarchive(fileDwnPath, args.tmpDirPath); - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( @@ -87,21 +86,16 @@ class Port extends PortBase { ), args.installPath, ); - await spawn([ - depBinShimPath(std_ports.node_org, "npm", args.depShims), - "install", - "--no-fund", - ], { - cwd: args.installPath, - env: { + await $`${ + depBinShimPath(std_ports.node_org, "npm", args.depShims) + } install --no-fund` + .cwd(args.installPath) + .env({ PATH: pathWithDepShims(args.depShims), - }, - }); - await std_fs.ensureDir(std_path.resolve(args.installPath, "bin")); - await Deno.symlink( - std_path.resolve(args.installPath, "src", "jco.js"), - std_path.resolve(args.installPath, "bin", "jco"), - ); + }); + await installPath.join("bin").ensureDir(); + await installPath.join("bin", "jco") + .createSymlinkTo(installPath.join("src", "jco.js").toString()); } } diff --git a/ports/mold.ts b/ports/mold.ts index f0ae0e3a..7d5959fb 100644 --- a/ports/mold.ts +++ b/ports/mold.ts @@ -1,29 +1,39 @@ import { + $, addInstallGlobal, + depBinShimPath, DownloadArgs, downloadFile, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, std_fs, std_path, std_url, - unarchive, } from "../port.ts"; +import * as std_ports from "../modules/ports/std.ts"; const manifest = { ty: "denoWorker" as const, name: "mold@ghrel", version: "0.1.0", moduleSpecifier: import.meta.url, + deps: [ + // we have to use tar because their tarballs contain symlinks + std_ports.tar_aa, + ], }; registerDenoPortGlobal(manifest, () => new Port()); -export default function install(config: InstallConfigSimple = {}) { +export type MoldInstallConfig = InstallConfigSimple & { + replaceLd: boolean; +}; +export default function install( + config: MoldInstallConfig = { replaceLd: true }, +) { addInstallGlobal({ portName: manifest.name, ...config, @@ -34,32 +44,10 @@ const repoOwner = "rui314"; const repoName = "mold"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; - - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { await downloadFile(args, downloadUrl(args.installVersion, args.platform)); @@ -71,10 +59,13 @@ export class Port extends PortBase { ); const fileDwnPath = std_path.resolve(args.downloadPath, fileName); - await unarchive(fileDwnPath, args.tmpDirPath); + await $`${ + depBinShimPath(std_ports.tar_aa, "tar", args.depShims) + } xf ${fileDwnPath} --directory=${args.tmpDirPath}`; - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } const dirs = []; @@ -92,6 +83,10 @@ export class Port extends PortBase { dirs[0].path, args.installPath, ); + if ((args.config as unknown as MoldInstallConfig).replaceLd) { + await installPath.join("bin", "ld") + .createSymlinkTo(installPath.join("bin", "mold").toString()); + } } } diff --git a/ports/node.ts b/ports/node.ts index 0d133016..a3f3c8e7 100644 --- a/ports/node.ts +++ b/ports/node.ts @@ -1,4 +1,5 @@ import { + $, addInstallGlobal, depBinShimPath, DownloadArgs, @@ -10,27 +11,22 @@ import { type PlatformInfo, PortBase, registerDenoPortGlobal, - removeFile, - spawn, std_fs, std_path, std_url, } from "../port.ts"; -// import * as std_ports from "../std.ts"; +// FIXME: circular module resolution when one std_port imports another const tar_aa_id = { id: "tar@aa", }; - // TODO: sanity check exports of all ports export const manifest = { ty: "denoWorker" as const, name: "node@org", version: "0.1.0", moduleSpecifier: import.meta.url, - deps: [ - tar_aa_id, - ], + deps: [tar_aa_id], }; registerDenoPortGlobal(manifest, () => new Port()); @@ -59,8 +55,8 @@ export class Port extends PortBase { } async listAll(_env: ListAllArgs) { - const metadataRequest = await fetch(`https://nodejs.org/dist/index.json`); - const metadata = await metadataRequest.json() as { version: string }[]; + const metadata = await $.request(`https://nodejs.org/dist/index.json`) + .json() as { version: string }[]; const versions = metadata.map((v) => v.version); // sort them numerically to make sure version 0.10.0 comes after 0.2.9 @@ -78,15 +74,13 @@ export class Port extends PortBase { artifactUrl(args.installVersion, args.platform), ); const fileDwnPath = std_path.resolve(args.downloadPath, fileName); - await spawn([ - depBinShimPath(tar_aa_id, "tar", args.depShims), - "xf", - fileDwnPath, - `--directory=${args.tmpDirPath}`, - ]); + await $`${ + depBinShimPath(tar_aa_id, "tar", args.depShims) + } xf ${fileDwnPath} --directory=${args.tmpDirPath}`; - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( diff --git a/ports/pnpm.ts b/ports/pnpm.ts index 41b58a6d..76c4de84 100644 --- a/ports/pnpm.ts +++ b/ports/pnpm.ts @@ -1,16 +1,14 @@ import { + $, addInstallGlobal, DownloadArgs, downloadFile, - InstallArgs, + GithubReleasePort, + type InstallArgs, type InstallConfigSimple, - ListAllArgs, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, std_fs, - std_path, std_url, } from "../port.ts"; @@ -29,26 +27,14 @@ export default function install(config: InstallConfigSimple = {}) { }); } -class Port extends PortBase { - manifest = manifest; - - async listAll(_env: ListAllArgs) { - const metadataRequest = await fetch( - `https://registry.npmjs.org/@pnpm/exe`, - { - headers: { - // use abbreviated registry info which's still 500kb unzipped - "Accept": "application/vnd.npm.install-v1+json", - }, - }, - ); - const metadata = await metadataRequest.json() as { - versions: Record; - }; +const repoOwner = "pnpm"; +const repoName = "pnpm"; +const repoAddress = `https://github.com/${repoOwner}/${repoName}`; - const versions = Object.keys(metadata.versions); - return versions; - } +export class Port extends GithubReleasePort { + manifest = manifest; + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { await downloadFile( @@ -64,20 +50,18 @@ class Port extends PortBase { const fileName = std_url.basename( artifactUrl(args.installVersion, args.platform), ); - const fileDwnPath = std_path.resolve(args.downloadPath, fileName); - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } - - await std_fs.ensureDir(std_path.resolve(args.installPath, "bin")); await std_fs.copy( - fileDwnPath, - std_path.resolve( - args.installPath, - "bin", - args.platform.os == "windows" ? "pnpm.exe" : "pnpm", - ), + $.path(args.downloadPath).join(fileName).toString(), + ( + await installPath.join("bin").ensureDir() + ) + .join(args.platform.os == "windows" ? "pnpm.exe" : "pnpm") + .toString(), ); } } @@ -105,9 +89,9 @@ function artifactUrl(installVersion: string, platform: PlatformInfo) { break; case "windows": os = "win"; - return `https://github.com/pnpm/pnpm/releases/download/v${installVersion}/pnpm-${os}-${arch}.exe`; + return `${repoAddress}/releases/download/v${installVersion}/pnpm-${os}-${arch}.exe`; default: throw new Error(`unsupported os: ${platform.arch}`); } - return `https://github.com/pnpm/pnpm/releases/download/v${installVersion}/pnpm-${os}-${arch}`; + return `${repoAddress}/releases/download/${installVersion}/pnpm-${os}-${arch}`; } diff --git a/ports/protoc.ts b/ports/protoc.ts index cb81281f..ed514ee0 100644 --- a/ports/protoc.ts +++ b/ports/protoc.ts @@ -1,15 +1,14 @@ import { + $, addInstallGlobal, DownloadArgs, downloadFile, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, std_fs, - std_path, std_url, unarchive, } from "../port.ts"; @@ -34,47 +33,26 @@ const repoOwner = "protocolbuffers"; const repoName = "protobuf"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; - - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { - await downloadFile(args, downloadUrl(args.installVersion, args.platform)); + await downloadFile(args, artifactUrl(args.installVersion, args.platform)); } async install(args: InstallArgs) { const fileName = std_url.basename( - downloadUrl(args.installVersion, args.platform), + artifactUrl(args.installVersion, args.platform), ); - const fileDwnPath = std_path.resolve(args.downloadPath, fileName); + const fileDwnPath = $.path(args.downloadPath).join(fileName); - await unarchive(fileDwnPath, args.tmpDirPath); + await unarchive(fileDwnPath.toString(), args.tmpDirPath); - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( @@ -84,7 +62,7 @@ export class Port extends PortBase { } } -function downloadUrl(installVersion: string, platform: PlatformInfo) { +function artifactUrl(installVersion: string, platform: PlatformInfo) { let os; switch (platform.os) { case "linux": diff --git a/ports/ruff.ts b/ports/ruff.ts index 2455529d..02f57f31 100644 --- a/ports/ruff.ts +++ b/ports/ruff.ts @@ -1,24 +1,30 @@ import { + $, addInstallGlobal, + depBinShimPath, DownloadArgs, downloadFile, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, + semver, std_fs, std_path, std_url, - unarchive, } from "../port.ts"; +import * as std_ports from "../modules/ports/std.ts"; const manifest = { ty: "denoWorker" as const, name: "ruff@ghrel", version: "0.1.0", moduleSpecifier: import.meta.url, + deps: [ + // we have to use tar because their tarballs for darwin use gnu sparse + std_ports.tar_aa, + ], }; registerDenoPortGlobal(manifest, () => new Port()); @@ -34,57 +40,38 @@ const repoOwner = "astral-sh"; const repoName = "ruff"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; - - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { - await downloadFile(args, downloadUrl(args.installVersion, args.platform)); + await downloadFile(args, artifactUrl(args.installVersion, args.platform)); } async install(args: InstallArgs) { const fileName = std_url.basename( - downloadUrl(args.installVersion, args.platform), + artifactUrl(args.installVersion, args.platform), ); - const fileDwnPath = std_path.resolve(args.downloadPath, fileName); - await unarchive(fileDwnPath, args.tmpDirPath); + const fileDwnPath = std_path.resolve(args.downloadPath, fileName); + await $`${ + depBinShimPath(std_ports.tar_aa, "tar", args.depShims) + } xf ${fileDwnPath} --directory=${args.tmpDirPath}`; - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( args.tmpDirPath, - std_path.resolve(args.installPath, "bin"), + installPath.join("bin").toString(), ); // await Deno.chmod(std_path.resolve(args.installPath, "bin", "ruff"), 0o700); } } -function downloadUrl(installVersion: string, platform: PlatformInfo) { +function artifactUrl(installVersion: string, platform: PlatformInfo) { let arch; switch (platform.arch) { case "x86_64": @@ -114,5 +101,8 @@ function downloadUrl(installVersion: string, platform: PlatformInfo) { default: throw new Error(`unsupported arch: ${platform.arch}`); } - return `${repoAddress}/releases/download/${installVersion}/${repoName}-${arch}-${os}.${ext}`; + const prefix = semver.lt(semver.parse(installVersion), semver.parse("0.1.8")) + ? repoName + : `${repoName}-${installVersion.replace(/^v/, "")}`; + return `${repoAddress}/releases/download/${installVersion}/${prefix}-${arch}-${os}.${ext}`; } diff --git a/ports/wasm-opt.ts b/ports/wasm-opt.ts index 95f5bbe0..5d930d6b 100644 --- a/ports/wasm-opt.ts +++ b/ports/wasm-opt.ts @@ -1,17 +1,9 @@ import { addInstallGlobal, - depBinShimPath, - type DownloadArgs, - InstallArgs, type InstallConfigSimple, - logger, - PortBase, registerDenoPortGlobal, - removeFile, - spawn, - std_fs, - std_path, } from "../port.ts"; +import { CargoBinstallPort } from "../modules/ports/cargo-binstall.ts"; import * as std_ports from "../modules/ports/std.ts"; const manifest = { @@ -33,60 +25,7 @@ export default function install(config: InstallConfigSimple = {}) { }); } -export class Port extends PortBase { +export class Port extends CargoBinstallPort { manifest = manifest; - - async listAll() { - const metadataRequest = await fetch( - `https://index.crates.io/wa/sm/wasm-opt`, - ); - const metadataText = await metadataRequest.text(); - const versions = metadataText - .split("\n") - .filter((str) => str.length > 0) - .map((str) => - JSON.parse(str) as { - vers: string; - } - ); - return versions.map((ver) => ver.vers); - } - - async download(args: DownloadArgs) { - const fileName = "wasm-opt"; - if ( - await std_fs.exists(std_path.resolve(args.downloadPath, fileName)) - ) { - logger().debug( - `file ${fileName} already downloaded, skipping whole download`, - ); - return; - } - await spawn([ - depBinShimPath(std_ports.cbin_ghrel, "cargo-binstall", args.depShims), - "wasm-opt", - `--version`, - args.installVersion, - `--install-path`, - args.tmpDirPath, - `--no-confirm`, - `--disable-strategies`, - `compile`, - `--no-track`, - ]); - await std_fs.copy( - args.tmpDirPath, - args.downloadPath, - ); - } - - async install(args: InstallArgs) { - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); - } - await std_fs.copy( - args.downloadPath, - std_path.resolve(args.installPath, "bin"), - ); - } + crateName = "wasm-opt"; } diff --git a/ports/wasm-tools.ts b/ports/wasm-tools.ts index 6f5fe833..3de08062 100644 --- a/ports/wasm-tools.ts +++ b/ports/wasm-tools.ts @@ -1,17 +1,9 @@ import { addInstallGlobal, - depBinShimPath, - type DownloadArgs, - InstallArgs, type InstallConfigSimple, - logger, - PortBase, registerDenoPortGlobal, - removeFile, - spawn, - std_fs, - std_path, } from "../port.ts"; +import { CargoBinstallPort } from "../modules/ports/cargo-binstall.ts"; import * as std_ports from "../modules/ports/std.ts"; const manifest = { @@ -33,60 +25,7 @@ export default function install(config: InstallConfigSimple = {}) { }); } -export class Port extends PortBase { +export class Port extends CargoBinstallPort { manifest = manifest; - - async listAll() { - const metadataRequest = await fetch( - `https://index.crates.io/wa/sm/wasm-tools`, - ); - const metadataText = await metadataRequest.text(); - const versions = metadataText - .split("\n") - .filter((str) => str.length > 0) - .map((str) => - JSON.parse(str) as { - vers: string; - } - ); - return versions.map((ver) => ver.vers); - } - - async download(args: DownloadArgs) { - const fileName = "wasm-tools"; - if ( - await std_fs.exists(std_path.resolve(args.downloadPath, fileName)) - ) { - logger().debug( - `file ${fileName} already downloaded, skipping whole download`, - ); - return; - } - await spawn([ - depBinShimPath(std_ports.cbin_ghrel, "cargo-binstall", args.depShims), - "wasm-tools", - `--version`, - args.installVersion, - `--install-path`, - args.tmpDirPath, - `--no-confirm`, - `--disable-strategies`, - `compile`, - `--no-track`, - ]); - await std_fs.copy( - args.tmpDirPath, - args.downloadPath, - ); - } - - async install(args: InstallArgs) { - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); - } - await std_fs.copy( - args.downloadPath, - std_path.resolve(args.installPath, "bin"), - ); - } + crateName = "wasm-tools"; } diff --git a/ports/wasmedge.ts b/ports/wasmedge.ts index 7fc779da..f58eaa83 100644 --- a/ports/wasmedge.ts +++ b/ports/wasmedge.ts @@ -1,16 +1,15 @@ import { + $, addInstallGlobal, depBinShimPath, DownloadArgs, downloadFile, ExecEnvArgs, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, - spawn, std_fs, std_path, std_url, @@ -63,12 +62,16 @@ const repoOwner = "WasmEdge"; const repoName = "WasmEdge"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; + repoName = repoName; + repoOwner = repoOwner; execEnv(args: ExecEnvArgs) { return { - WASMEDGE_LIB_DIR: std_path.resolve(args.installPath, "lib"), + WASMEDGE_DIR: args.installPath, + // WASMEDGE_LIB_DIR: std_path.resolve(args.installPath, "lib64"), + // WASMEDGE_INCLUDE_DIR: std_path.resolve(args.installPath, "include"), }; } @@ -76,31 +79,6 @@ export class Port extends PortBase { return ["lib*/*"]; } - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - // NOTE: this downloads a 1+ meg json - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } - async download(args: DownloadArgs) { await downloadFile(args, downloadUrl(args.installVersion, args.platform)); } @@ -111,15 +89,13 @@ export class Port extends PortBase { ); const fileDwnPath = std_path.resolve(args.downloadPath, fileName); - await spawn([ - depBinShimPath(std_ports.tar_aa, "tar", args.depShims), - "xf", - fileDwnPath, - `--directory=${args.tmpDirPath}`, - ]); + await $`${ + depBinShimPath(std_ports.tar_aa, "tar", args.depShims) + } xf ${fileDwnPath} --directory=${args.tmpDirPath}`; - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } const dirs = []; diff --git a/ports/whiz.ts b/ports/whiz.ts index ab68f32e..e9fdde8d 100644 --- a/ports/whiz.ts +++ b/ports/whiz.ts @@ -1,24 +1,29 @@ import { + $, addInstallGlobal, + depBinShimPath, DownloadArgs, downloadFile, + GithubReleasePort, InstallArgs, type InstallConfigSimple, type PlatformInfo, - PortBase, registerDenoPortGlobal, - removeFile, std_fs, std_path, std_url, - unarchive, } from "../port.ts"; +import * as std_ports from "../modules/ports/std.ts"; const manifest = { ty: "denoWorker" as const, name: "whiz@ghrel", version: "0.1.0", moduleSpecifier: import.meta.url, + deps: [ + // we have to use tar because their tarballs for darwin use gnu sparse + std_ports.tar_aa, + ], }; registerDenoPortGlobal(manifest, () => new Port()); @@ -34,32 +39,10 @@ const repoOwner = "zifeo"; const repoName = "whiz"; const repoAddress = `https://github.com/${repoOwner}/${repoName}`; -export class Port extends PortBase { +export class Port extends GithubReleasePort { manifest = manifest; - - async latestStable(): Promise { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`, - ); - - const metadata = await metadataRequest.json() as { - tag_name: string; - }; - - return metadata.tag_name; - } - - async listAll() { - const metadataRequest = await fetch( - `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, - ); - - const metadata = await metadataRequest.json() as [{ - tag_name: string; - }]; - - return metadata.map((rel) => rel.tag_name).reverse(); - } + repoName = repoName; + repoOwner = repoOwner; async download(args: DownloadArgs) { await downloadFile(args, downloadUrl(args.installVersion, args.platform)); @@ -70,15 +53,17 @@ export class Port extends PortBase { downloadUrl(args.installVersion, args.platform), ); const fileDwnPath = std_path.resolve(args.downloadPath, fileName); + await $`${ + depBinShimPath(std_ports.tar_aa, "tar", args.depShims) + } xf ${fileDwnPath} --directory=${args.tmpDirPath}`; - await unarchive(fileDwnPath, args.tmpDirPath); - - if (await std_fs.exists(args.installPath)) { - await removeFile(args.installPath, { recursive: true }); + const installPath = $.path(args.installPath); + if (await installPath.exists()) { + await installPath.remove({ recursive: true }); } await std_fs.copy( args.tmpDirPath, - std_path.resolve(args.installPath, "bin"), + installPath.join("bin").toString(), ); } } diff --git a/tests/ambient.ts b/tests/ambient.ts index 10973f06..d0cc2cdc 100644 --- a/tests/ambient.ts +++ b/tests/ambient.ts @@ -9,14 +9,6 @@ import * as curl from "../ports/curl.ts"; import * as unzip from "../ports/unzip.ts"; const manifests = [ - { - name: "ls", - execName: "ls", - version: "0.1.0", - versionExtractFlag: "--version", - versionExtractRegex: "(\\d+\\.\\d+)", - versionExtractRegexFlags: "", - }, tar.manifest, git.manifest, curl.manifest, diff --git a/tests/e2e.ts b/tests/e2e.ts index 806aea1b..1a15f42a 100644 --- a/tests/e2e.ts +++ b/tests/e2e.ts @@ -1,7 +1,21 @@ -import { dockerE2eTest } from "./utils.ts"; +import { dockerE2eTest, E2eTestCase, localE2eTest } from "./utils.ts"; // order tests by download size to make failed runs less expensive -await dockerE2eTest([ +const cases: E2eTestCase[] = [ + ...(Deno.build.os == "linux" + ? [ + // 8 megs + { + name: "mold", + imports: `import port from "$ghjk/ports/mold.ts"`, + confFn: `async () => { + port({ }); + }`, + ePoint: `mold -V`, + }, + ] + : []), + // 3 megs { name: "protoc", @@ -47,15 +61,6 @@ await dockerE2eTest([ }`, ePoint: `cargo-binstall -V`, }, - // 8 megs - { - name: "mold", - imports: `import port from "$ghjk/ports/mold.ts"`, - confFn: `async () => { - port({ }); - }`, - ePoint: `mold -V`, - }, // 16 megs { name: "wasmedge", @@ -103,7 +108,7 @@ await dockerE2eTest([ }, // 42 megs { - name: "pnpm", + name: "earthly", imports: `import port from "$ghjk/ports/earthly.ts"`, confFn: `async () => { port({ }); @@ -152,4 +157,20 @@ await dockerE2eTest([ // }`, // ePoint: `python --version`, // }, -]); +]; + +if (Deno.env.get("GHJK_E2E_TYPE") == "both") { + localE2eTest(cases); + await dockerE2eTest(cases); +} else if (Deno.env.get("GHJK_TEST_E2E_TYPE") == "local") { + localE2eTest(cases); +} else if ( + Deno.env.get("GHJK_TEST_E2E_TYPE") == "docker" || + !Deno.env.has("GHJK_TEST_E2E_TYPE") +) { + await dockerE2eTest(cases); +} else { + throw new Error( + `unexpected GHJK_TEST_E2E_TYPE: ${Deno.env.get("GHJK_TEST_E2E_TYPE")}`, + ); +} diff --git a/tests/test.Dockerfile b/tests/test.Dockerfile index af549eb7..765a1a96 100644 --- a/tests/test.Dockerfile +++ b/tests/test.Dockerfile @@ -35,19 +35,17 @@ RUN ln -s ./main.ts /bin/ghjk WORKDIR /app -ENV GHJK_EXE_INSTALL_DIR=/usr/bin -# explicitly set the shell var as detection fails otherwise -# because ps program is not present in this image -RUN SHELL=/bin/bash deno run -A /ghjk/install.ts -RUN SHELL=/bin/fish deno run -A /ghjk/install.ts -RUN SHELL=/bin/zsh deno run -A /ghjk/install.ts +ENV GHJK_INSTALL_EXE_DIR=/usr/bin +ENV GHJK_INSTALL_HOOK_SHELLS=fish,bash,zsh +RUN deno run -A /ghjk/install.ts RUN cat > ghjk.ts < Promise); @@ -9,7 +12,72 @@ export type DockerE2eTestCase = { ePoint: string; }; -export async function dockerE2eTest(cases: DockerE2eTestCase[]) { +export function localE2eTest(cases: E2eTestCase[]) { + const defaultEnvs: Record = {}; + for (const { name, envs: testEnvs, confFn, ePoint, imports } of cases) { + Deno.test(`localE2eTest - ${name}`, async () => { + const tmpDir = $.path( + await Deno.makeTempDir({ + prefix: "ghjk_le2e_", + }), + ); + + const ghjkDir = await tmpDir.join("ghjk").ensureDir(); + await install({ + ...defaultInstallArgs, + skipExecInstall: false, + ghjkExecInstallDir: ghjkDir.toString(), + ghjkDir: ghjkDir.toString(), + shellsToHook: [], + }); + await tmpDir.join("ghjk.ts").writeText( + `export { ghjk } from "$ghjk/mod.ts"; +${imports} + +await (${confFn.toString()})()` + .replaceAll( + "$ghjk", + std_url.dirname(import.meta.resolve("../mod.ts")).href, + ), + ); + const env: Record = { + ...defaultEnvs, + ...testEnvs, + BASH_ENV: `${ghjkDir.toString()}/env.sh`, + ZDOTDIR: ghjkDir.toString(), + GHJK_DIR: ghjkDir.toString(), + }; + { + const confHome = await ghjkDir.join(".config").ensureDir(); + const fishConfDir = await confHome.join("fish").ensureDir(); + await fishConfDir.join("config.fish").createSymlinkTo( + ghjkDir.join("env.fish").toString(), + ); + env["XDG_CONFIG_HOME"] = confHome.toString(); + } + await $`${ghjkDir.join("ghjk").toString()} print config` + .cwd(tmpDir.toString()) + .env(env); + await $`${ghjkDir.join("ghjk").toString()} ports sync` + .cwd(tmpDir.toString()) + .env(env); + const ghjkDirLen = ghjkDir.toString().length; + for await (const entry of ghjkDir.walk()) { + logger().debug(entry.path.toString().slice(ghjkDirLen), { + ty: entry.isDirectory ? "dir" : entry.isSymlink ? "link" : "file", + }); + } + for (const shell of ["bash -c", "fish -c", "zsh -c"]) { + await $.raw`env ${shell} '${ePoint}'` + .cwd(tmpDir.toString()) + .env(env); + } + await tmpDir.remove({ recursive: true }); + }); + } +} + +export async function dockerE2eTest(cases: E2eTestCase[]) { // const socket = Deno.env.get("DOCKER_SOCK") ?? "/var/run/docker.sock"; // const docker = new Docker(socket); const dockerCmd = (Deno.env.get("DOCKER_CMD") ?? "docker").split(/\s/); @@ -22,7 +90,7 @@ export async function dockerE2eTest(cases: DockerE2eTestCase[]) { const defaultEnvs: Record = {}; for (const { name, envs: testEnvs, confFn, ePoint, imports } of cases) { - Deno.test(`dockerTest - ${name}`, async () => { + Deno.test(`dockerE2eTest - ${name}`, async () => { const tag = `ghjk_test_${name}`; const env = { ...defaultEnvs, @@ -37,37 +105,21 @@ await (${confFn.toString()})()`; templateStrings.addConfig, configFile, ); - await spawn([ - ...dockerCmd, - "buildx", - "build", - "--tag", - tag, - "--network=host", - // add to images list - "--output", - "type=docker", - "-f-", - ".", - ], { env, pipeInput: dFile }); + await $ + .raw`${dockerCmd} buildx build --tag '${tag}' --network=host --output type=docker -f- .` + .env(env) + .stdinText(dFile); for (const shell of ["bash", "fish", "zsh"]) { - await spawn([ - ...dockerCmd, - "run", - "--rm", - ...Object.entries(env).map(([key, val]) => ["-e", `${key}=${val}`]) - .flat(), - tag, - shell, - "-c", - ePoint, - ], { env }); + await $ + .raw`${dockerCmd} run --rm ${ + Object.entries(env).map(([key, val]) => ["-e", `${key}=${val}`]) + .flat() + } ${tag} ${shell} -c '${ePoint}'` + .env(env); } - await spawn([ - ...dockerCmd, - "rmi", - tag, - ]); + await $ + .raw`${dockerCmd} rmi '${tag}'` + .env(env); }); } } diff --git a/utils/logger.ts b/utils/logger.ts index 4322020a..9c3eb746 100644 --- a/utils/logger.ts +++ b/utils/logger.ts @@ -16,7 +16,13 @@ function formatter(lr: log.LogRecord) { let msg = `[${lr.levelName}${loggerName}] ${lr.msg}`; lr.args.forEach((arg, _index) => { - msg += `, ${JSON.stringify(arg)}`; + msg += `, ${ + Deno.inspect(arg, { + colors: isColorfulTty(), + depth: 10, + strAbbreviateSize: 256, + }) + }`; }); return msg; @@ -77,3 +83,25 @@ export class ConsoleErrHandler extends log.handlers.BaseHandler { return msg; } } + +let colorEnvFlagSet = false; +Deno.permissions.query({ + name: "env", + variable: "CLICOLOR_FORCE", +}).then((perm) => { + if (perm.state == "granted") { + const val = Deno.env.get("CLICOLOR_FORCE"); + colorEnvFlagSet = !!val && val != "0" && val != "false"; + } +}); + +export function isColorfulTty(outFile = Deno.stdout) { + if (colorEnvFlagSet) { + return true; + } + if (Deno.isatty(outFile.rid)) { + const { columns } = Deno.consoleSize(); + return columns > 0; + } + return false; +} diff --git a/utils/mod.ts b/utils/mod.ts index 2de7f445..2464d118 100644 --- a/utils/mod.ts +++ b/utils/mod.ts @@ -11,81 +11,6 @@ export function dbg(val: T) { return val; } -export class ChildError extends Error { - constructor( - public code: number, - public output: string, - ) { - super(`ChildError - ${code} - ${output}`); - } -} - -export type SpawnOptions = { - cwd?: string; - env?: Record; - pipeInput?: string; - // pipeOut?: WritableStream; - // pipeErr?: WritableStream; -}; - -/// This is deprecated, please use the dax lib -// as exposed from this module by the `$` object -export async function spawn( - cmd: string[], - options: SpawnOptions = {}, -) { - const { cwd, env, pipeInput } = { - ...options, - }; - logger().debug("spawning", cmd); - const child = new Deno.Command(cmd[0], { - args: cmd.slice(1), - cwd, - ...(pipeInput - ? { - stdin: "piped", - } - : {}), - env, - }).spawn(); - - if (pipeInput) { - const writer = child.stdin.getWriter(); - await writer.write(new TextEncoder().encode(pipeInput)); - writer.releaseLock(); - await child.stdin.close(); - } - const { code, success } = await child.status; - if (!success) { - throw new Error(`child failed with code ${code}`); - } -} - -export async function spawnOutput( - cmd: string[], - options: Omit = {}, -): Promise { - const { cwd, env } = { - ...options, - }; - logger().debug("spawning", cmd); - const child = new Deno.Command(cmd[0], { - args: cmd.slice(1), - cwd, - stdout: "piped", - stderr: "piped", - env, - }).spawn(); - - const { code, success, stdout, stderr } = await child.output(); - if (!success) { - throw new Error( - `child failed with code ${code} - ${new TextDecoder().decode(stderr)}`, - ); - } - return new TextDecoder().decode(stdout); -} - export function pathWithDepShims( depShims: DepShims, ) { @@ -117,8 +42,8 @@ export function depBinShimPath( } export function getInstallId(install: InstallConfig) { - if ("pluginRepo" in install) { - const url = new URL(install.pluginRepo); + if ("pluginRepo" in install && install.portName == "asdf@asdf") { + const url = new URL(install.pluginRepo as string); const pluginId = `${url.hostname}-${url.pathname.replaceAll("/", ".")}`; return `asdf-${pluginId}`; } @@ -135,28 +60,6 @@ export function inWorker() { self instanceof WorkerGlobalScope; } -let colorEnvFlagSet = false; -Deno.permissions.query({ - name: "env", - variable: "CLICOLOR_FORCE", -}).then((perm) => { - if (perm.state == "granted") { - const val = Deno.env.get("CLICOLOR_FORCE"); - colorEnvFlagSet = !!val && val != "0" && val != "false"; - } -}); - -export function isColorfulTty(outFile = Deno.stdout) { - if (colorEnvFlagSet) { - return true; - } - if (Deno.isatty(outFile.rid)) { - const { columns } = Deno.consoleSize(); - return columns > 0; - } - return false; -} - export async function findConfig(path: string) { let current = path; while (current !== "/") { @@ -169,12 +72,11 @@ export async function findConfig(path: string) { return null; } -export function envDirFromConfig(config: string) { - const { shareDir } = dirs(); +export function envDirFromConfig(ghjkDir: string, configPath: string) { return std_path.resolve( - shareDir, + ghjkDir, "envs", - std_path.dirname(config).replaceAll("/", "."), + std_path.dirname(configPath).replaceAll("/", "."), ); } @@ -195,7 +97,10 @@ export function dirs() { if (!home) { throw new Error("cannot find home dir"); } - return { homeDir: home, shareDir: `${home}/.local/share/ghjk` }; + return { + homeDir: home, + shareDir: std_path.resolve(home, ".local", "share"), + }; } export const AVAIL_CONCURRENCY = Number.parseInt( diff --git a/utils/unarchive.ts b/utils/unarchive.ts index 6172fcc5..f4c57492 100644 --- a/utils/unarchive.ts +++ b/utils/unarchive.ts @@ -1,15 +1,16 @@ import { Foras, + jszip, std_fs, std_io, std_path, std_streams, std_tar, - zipjs, } from "../deps/ports.ts"; /// Uses file extension to determine type -/// Does not support symlinks +/// Does not support extracting symlinks +/// Does not support tarballs using [GnuSparse](https://www.gnu.org/software/tar/manual/html_node/Sparse-Recovery.html) export async function unarchive( path: string, dest = "./", @@ -98,30 +99,21 @@ export async function unzip( path: string, dest = "./", ) { - const zipFile = await Deno.open(path, { read: true }); - const zipReader = new zipjs.ZipReader(zipFile.readable); - try { - await Promise.allSettled( - (await zipReader.getEntries()).map(async (entry) => { - const filePath = std_path.resolve(dest, entry.filename); - if (entry.directory) { - await std_fs.ensureDir(filePath); - return; - } - await std_fs.ensureDir(std_path.dirname(filePath)); - const file = await Deno.open(filePath, { - create: true, - truncate: true, - write: true, - mode: entry.externalFileAttribute >> 16, - }); - if (!entry.getData) throw Error("impossible"); - await entry.getData(file.writable); - }), - ); - } catch (err) { - throw err; - } finally { - zipReader.close(); - } + const zipArc = await jszip.readZip(path); + await Promise.allSettled( + Object.entries(zipArc.files()).map(async ([_, entry]) => { + const filePath = std_path.resolve(dest, entry.name); + if (entry.dir) { + await std_fs.ensureDir(filePath); + return; + } + await std_fs.ensureDir(std_path.dirname(filePath)); + const buf = await entry.async("uint8array"); + await Deno.writeFile(filePath, buf, { + create: true, + // FIXME: windows support + mode: Number(entry.unixPermissions ?? 0o666), + }); + }), + ); }