From a1425ae66ae11477cf5990f584810dc128f5e579 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 30 Sep 2023 08:14:58 -0400 Subject: [PATCH] pkgx --- .github/deno-to-node.ts | 25 +---- .github/workflows/cd.npm.yml | 35 ++++++ .github/workflows/cd.vx.yml | 22 ++++ .github/workflows/cd.yml | 89 +++++---------- .github/workflows/ci.yml | 30 +++-- LICENSE.txt | 2 +- README.md | 124 ++++++--------------- deno.json | 2 +- examples/awscli/index.mjs | 4 +- examples/awscli/package.json | 4 +- examples/whisper.js | 4 +- examples/whisper.mjs | 4 +- examples/whisper.ts | 6 +- fixtures/npm-integration-test/index.js | 6 +- fixtures/npm-integration-test/package.json | 2 +- mod.ts | 11 +- scripts/publish-release.sh | 84 ++++++++++++++ scripts/run-coverage.sh | 16 +++ src/hooks/useCache.test.ts | 2 +- src/hooks/useCellar.test.ts | 4 +- src/hooks/useCellar.ts | 8 +- src/hooks/useConfig.test.ts | 26 ++--- src/hooks/useConfig.ts | 49 +++++--- src/hooks/useDownload.test.ts | 6 +- src/hooks/useDownload.ts | 4 +- src/hooks/useInventory.ts | 2 +- src/hooks/useMoustaches.test.ts | 6 +- src/hooks/useMoustaches.ts | 10 +- src/hooks/useOffLicense.ts | 2 +- src/hooks/usePantry.test.ts | 62 +++++++++-- src/hooks/usePantry.ts | 33 ++++-- src/hooks/useShellEnv.test.ts | 5 +- src/hooks/useShellEnv.ts | 13 +-- src/hooks/useSync.test.ts | 16 +-- src/hooks/useSync.ts | 6 +- src/hooks/useTestConfig.ts | 9 +- src/plumbing/hydrate.test.ts | 2 +- src/plumbing/hydrate.ts | 2 +- src/plumbing/install.test.ts | 12 +- src/plumbing/install.ts | 12 +- src/plumbing/link.test.ts | 6 +- src/plumbing/resolve.test.ts | 8 +- src/plumbing/resolve.ts | 4 +- src/plumbing/which.test.ts | 10 +- src/porcelain/install.test.ts | 18 +-- src/porcelain/run.test.ts | 2 +- src/porcelain/run.ts | 4 +- src/utils/Path.test.ts | 24 ++-- src/utils/error.test.ts | 2 +- src/utils/error.ts | 2 +- src/utils/hacks.test.ts | 40 ------- src/utils/hacks.ts | 38 ------- src/utils/host.test.ts | 2 +- src/utils/misc.test.ts | 17 +-- src/utils/misc.ts | 16 +-- src/utils/pkg.test.ts | 2 +- src/utils/semver.test.ts | 2 +- src/utils/semver.ts | 2 +- 58 files changed, 502 insertions(+), 458 deletions(-) create mode 100644 .github/workflows/cd.npm.yml create mode 100644 .github/workflows/cd.vx.yml create mode 100755 scripts/publish-release.sh create mode 100755 scripts/run-coverage.sh delete mode 100644 src/utils/hacks.test.ts delete mode 100644 src/utils/hacks.ts diff --git a/.github/deno-to-node.ts b/.github/deno-to-node.ts index 6ca4204..7debc19 100755 --- a/.github/deno-to-node.ts +++ b/.github/deno-to-node.ts @@ -1,19 +1,6 @@ -#!/usr/bin/env -S tea -E +#!/usr/bin/env -S pkgx +npm deno run --allow-env --allow-read --allow-write --allow-net --allow-run -/*--- -args: - - deno - - run - - --allow-env - - --allow-read - - --allow-write - - --allow-net - - --allow-run -dependencies: - npmjs.com: '*' ----*/ - -import { build, emptyDir } from "https://deno.land/x/dnt@0.38.0/mod.ts"; +import { build, emptyDir } from "https://deno.land/x/dnt@0.38.1/mod.ts"; import SemVer from "../src/utils/semver.ts"; await emptyDir("./dist"); @@ -46,16 +33,16 @@ await build({ "./src/utils/flock.deno.ts": "./src/utils/flock.node.ts" }, package: { - name: "@teaxyz/lib", + name: "libpkgx", version, description: "pkging primitives", license: "Apache-2.0", repository: { type: "git", - url: "git://github.com/teaxyz/lib.git", + url: "git://github.com/pkgxdev/lib.git", }, bugs: { - url: "https://github.com/teaxyz/lib/issues", + url: "https://github.com/pkgxdev/lib/issues", }, dependencies: { "is-what": "^4", @@ -63,7 +50,7 @@ await build({ }, exports: { "./src/src/utils/semver": { - //TODO remove when gui is updated to use `@teaxyz/lib/semver` + //TODO remove when gui is updated to use `libpkgx/semver` import: "./src/src/utils/semver.ts" }, "./semver": { diff --git a/.github/workflows/cd.npm.yml b/.github/workflows/cd.npm.yml new file mode 100644 index 0000000..3e9ba8d --- /dev/null +++ b/.github/workflows/cd.npm.yml @@ -0,0 +1,35 @@ +name: cd·npm + +on: + release: + types: + - published + +concurrency: + group: cd/vx/${{ github.event.release.tag_name }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + npm-publish: + runs-on: ubuntu-latest + steps: + - uses: robinraju/release-downloader@v1.8 + with: + releaseId: ${{ github.event.release.id }} + + - run: | + mkdir dist + tar xf *.tar.xz --directory=dist --strip-components=1 + + # if we don’t do this the npm publish step doesn’t work + - uses: actions/setup-node@v2 + with: + registry-url: https://registry.npmjs.org + + - run: npm publish --access public + working-directory: dist + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/cd.vx.yml b/.github/workflows/cd.vx.yml new file mode 100644 index 0000000..9883ad1 --- /dev/null +++ b/.github/workflows/cd.vx.yml @@ -0,0 +1,22 @@ +name: cd·vx + +on: + release: + types: + - published + +concurrency: + group: cd/vx/${{ github.event.release.tag_name }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + retag: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: fischerscode/tagger@v0 + with: + prefix: v diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 24f66fd..16e64ae 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,86 +1,53 @@ -name: CD +name: cd on: - release: - types: - - created - - edited + workflow_dispatch: + inputs: + version: + required: true permissions: contents: read concurrency: - group: ${{ github.event.release.tag_name }} + group: v${{ inputs.version }} cancel-in-progress: true jobs: - check: - if: ${{ github.event.release.prerelease }} - uses: ./.github/workflows/ci.yml - - dnt: - needs: [check] + qa: runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: - [self-hosted, macOS, X64] - [self-hosted, linux, X64] - [self-hosted, macOS, ARM64] - [self-hosted, linux, ARM64] - - ubuntu-latest # ∵ resilience: PRs verify dnt with this env steps: - - uses: actions/checkout@v3 - - uses: teaxyz/setup@v0 - - run: deno task dnt ${{ github.event.release.name }} - - - uses: actions/upload-artifact@v3 - if: ${{ matrix.os == 'ubuntu-latest' }} - with: - path: dist + - uses: actions/checkout@v4 + - uses: pkgxdev/dev@v0 + - run: deno task dnt - publish-npm: - needs: [dnt] + attach: runs-on: ubuntu-latest + needs: qa steps: - - uses: actions/download-artifact@v3 - - # if we don’t do this the npm publish step doesn’t work - - uses: actions/setup-node@v2 - with: - registry-url: https://registry.npmjs.org + - uses: actions/checkout@v4 + - uses: pkgxdev/dev@v0 - - run: npm publish --access public - working-directory: artifact - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - run: deno task dnt ${{ inputs.version }} - publish: - needs: [publish-npm] - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Convert pre-release to release - run: | - curl -fX PATCH \ - -H "Authorization: Bearer ${{ github.token }}" \ - -H "Accept: application/vnd.github.v3+json" \ - -d '{"draft": false, "prerelease": false, "make_latest": true, "tag_name": "${{ github.event.release.name }}"}' \ - "https://api.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}" - - # delete pre-release tag - run: | - git push origin :${{ github.event.release.tag_name }} - git tag -d ${{ github.event.release.tag_name }} - - # we need the new tag for the next step - - run: git fetch origin --tags - - - uses: fischerscode/tagger@v0 - with: - prefix: v - tag: ${{ github.event.release.name }} + mv dist libpkgx-${{inputs.version}} + tar cJf libpkgx-${{ inputs.version }}.tar.xz libpkgx-${{inputs.version}} + + - name: attach to release + run: + gh release upload + --clobber + libpkgx-${{ inputs.version }}.tar.xz + env: + # using this token rather than github.token due to `release not found` bug + # https://github.com/cli/cli/issues/5252 + GH_TOKEN: ${{ secrets.TEMP_JACOBS_GITHUB_PAT }} + GH_REPO: pkgxdev/libpkgx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59acac6..87d2f3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,42 +7,39 @@ on: - '**/*.ts' - .github/workflows/ci.yml workflow_dispatch: - workflow_call: concurrency: - group: ${{ github.ref || github.event.release.name }} + group: ${{ github.ref }} cancel-in-progress: true jobs: tests: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: - macos-latest - ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: teaxyz/setup@v0 + - uses: actions/checkout@v4 + - uses: denoland/setup-deno@v1 - run: deno cache mod.ts - run: deno task test --coverage=cov_profile --no-check # ⬆signal∶noise & ∵ we have `jobs.typecheck` - - run: deno coverage cov_profile --lcov --exclude=tests/ --output=cov_profile.lcov - if: ${{ github.event_name != 'release' }} + - run: deno coverage cov_profile --lcov --output=cov_profile.lcov - uses: coverallsapp/github-action@v1 - if: ${{ github.event_name != 'release' }} with: path-to-lcov: cov_profile.lcov parallel: true flag-name: ${{ matrix.platform.id }} upload-coverage: - if: ${{ github.event_name != 'release' }} needs: tests runs-on: ubuntu-latest steps: @@ -54,7 +51,7 @@ jobs: # we’re checking no import-map type imports snuck in runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: src - uses: denoland/setup-deno@v1 @@ -62,11 +59,10 @@ jobs: dnt: runs-on: ubuntu-latest - if: ${{ github.event_name != 'release' }} steps: - - uses: actions/checkout@v3 - - uses: teaxyz/setup@v0 - - run: deno task dnt + - uses: actions/checkout@v4 + - uses: denoland/setup-deno@v1 + - run: deno run -A --unstable .github/deno-to-node.ts - run: | npm i @@ -77,13 +73,13 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: teaxyz/setup@v0 + - uses: actions/checkout@v4 + - uses: denoland/setup-deno@v1 - run: deno lint typecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: teaxyz/setup@v0 + - uses: actions/checkout@v4 + - uses: denoland/setup-deno@v1 - run: deno task typecheck diff --git a/LICENSE.txt b/LICENSE.txt index 59f1dfe..98f8fb4 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 tea.inc. + Copyright 2023 pkgx inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 64cfd7e..da0bf57 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,35 @@ -![tea](https://tea.xyz/banner.png) - -

- - Twitter - - - Discord - - - Coverage Status - - - Documentation & Manual - -

- - -# libtea - -tea aims to provide packaging primitives. This library is a route to that -goal. libtea can install and provide sandboxed environments for packages that -have no effect on the wider system without you or your user needing to install -[tea/cli]. +![pkgx.dev](https://pkgx.dev/banner.png) + +[![coverage][]][coveralls] + +# libpkgx + +pkgx aims to provide packaging primitives. This library is a route to that +goal. libpkgx can create sandboxed environments for +packages that have no effect on the wider system without you or your user +needing to install [pkgx]. ## Getting Started ```sh -$ npm install @teaxyz/lib -# ^^ https://npmjs.com/@teaxyz/lib +$ npm install libpkgx +# ^^ https://npmjs.com/libpkgx ``` Or with [Deno]: ```ts -import * as tea from "https://deno.land/x/libtea/mod.ts" +import * as pkgx from "https://deno.land/x/libpkgx/mod.ts" ``` ## Usage ```ts -import { porcelain } from "@teaxyz/lib"; +import { porcelain } from "libpkgx"; const { run } = porcelain; await run(`python -c 'print("Hello, World!")'`); -// ^^ installs python and its deps (into ~/.tea/python.org/v3.x.y) +// ^^ installs python and its deps (into ~/.pkgx/python.org/v3.x.y) // ^^ runs the command // ^^ output goes to the terminal // ^^ throws on execution error or non-zero exit code @@ -70,7 +56,7 @@ assert(status == 7); // ^^ didn’t throw! > The run function’s options also takes `env` if you need to supplement or > replace the inherited environment (which is passed by default). -Need a specific version of something? [tea][tea/cli] can install any version +Need a specific version of something? [pkgx] can install any version of any package: ```ts @@ -82,16 +68,16 @@ await run(["node^16", "-e", "console.log(process.version)"]); > preferable since shell quoting rules can be tricky. If you pass `string[]` > we execute the command directly rather than via `/bin/sh`. -All of tea’s packages are relocatable so you can configure libtea to install +All of pkgx’s packages are relocatable so you can configure pkgx to install wherever you want: ```ts -import { hooks, Path, porcelain } from "tea"; +import { hooks, Path, porcelain } from "libpkgx"; const { install } = porcelain; const { useConfig } = hooks; useConfig({ prefix: Path.home().join(".local/share/my-app") }); -// ^^ must be done **before** any other libtea calls +// ^^ must be done **before** any other pkgx calls const go = await install("go.dev"); // ^^ go.path = /home/you/.local/share/my-app/go.dev/v1.20.4 @@ -113,13 +99,13 @@ Perhaps what you create should go into the porcelain? If so, please open a PR. ### Logging Most functions take an optional `logger` parameter so you can output logging -information if you so choose. `tea/cli` has a fairly sophisticated logger, so +information if you so choose. `pkgx` (cli) has a fairly sophisticated logger, so go check that out if you want. For our porcelain functions we provide a simple debug-friendly logger (`ConsoleLogger`) that will output everything via `console.error`: ```ts -import { porcelain, plumbing, utils } from "tea" +import { porcelain, plumbing, utils } from "libpkgx" const { ConsoleLogger } = utils const { run } = porcelain @@ -131,7 +117,7 @@ await run("youtube-dl youtu.be/xiq5euezOEQ", { logger }).exec() We have our own implementation of semver because open source has existed for decades and Semantic Versioning is much newer than that. Our implementation is -quite compatible but not completely so. Use our semver with libtea. +quite compatible but not completely so. Use our semver with libpkgx. Our implementation is 100% compatible with strings output from node’s own semver. @@ -144,14 +130,14 @@ appreciate your help in fixing it. The plumbing has no magic. Libraries need well defined behavior. You’ll need to read the docs to use them effectively. -libtea almost certainly will not work in a browser. Potentially it's possible. +libpkgx almost certainly will not work in a browser. Potentially it's possible. The first step would be compiling our bottles to WASM. We could use your help with that… We use a hook-like pattern because it is great. This library is not itself designed for React. -We support the same platforms as [tea/cli]. +We support the same platforms as [pkgx]. ## What Packages are Available? @@ -162,7 +148,7 @@ be an easy and enjoyable process. Your contribution is both welcome and desired! To see what is available refer to the [pantry] docs or you can run: -`tea pkg search foo`. +`pkgx pkg search foo`.   @@ -186,8 +172,8 @@ We would be thrilled to hear your ideas† or receive your pull requests. ## Anatomy -The code is written with Deno (just like [tea/cli]) but is compiled to a -node package for wider accessibility (and ∵ [tea/gui] is node/electron). +The code is written with Deno (just like [pkgx]) but is compiled to a +node package for wider accessibility (and ∵ [pkgx] is node/electron). The library is architected into hooks, plumbing and porcelain. Where the hooks represent the low level primitives of pkging, the plumbing glues those @@ -199,7 +185,7 @@ friendly *façade* pattern for the plumbing. We would love to port this code to every language. We are deliberately keeping the scope *tight*. Probably we would prefer to have one repo per language. -tea has sensible rules for how packages are defined and installed so writing +pkgx has sensible rules for how packages are defined and installed so writing a port should be simple. We would love to explore how possible writing this in rust and then compiling @@ -207,55 +193,11 @@ to WASM for all other languages would be. Can you help? Open a [discussion] to start. -[discussion]: https://github.com/orgs/teaxyz/discussions -[tea/cli]: https://github.com/teaxyz/cli -[tea/gui]: https://github.com/teaxyz/gui +[discussion]: https://github.com/orgs/pkgxdev/discussions +[pkgx]: https://github.com/pkgxdev/pkgx [Deno]: https://deno.land -[pantry]: https://github.com/teaxyz/pantry +[pantry]: https://github.com/pkgxdev/pantry [plumbing]: ./plumbing/ [porcelain]: ./porcelain/ - -  - - -# Tasks - -Run eg. `xc coverage` or `xc bump patch`. - -## Coverage - -```sh -deno task test --coverage=cov_profile -deno coverage cov_profile --lcov --output=cov_profile.lcov -tea genhtml -o cov_profile/html cov_profile.lcov -open cov_profile/html/index.html -``` - -## Bump - -Bumps version by creating a pre-release which then engages the deployment -infra in GitHub Actions. - -Inputs: LEVEL - -```sh -if ! git diff-index --quiet HEAD --; then - echo "error: dirty working tree" >&2 - exit 1 -fi - -if [ "$(git rev-parse --abbrev-ref HEAD)" != "main" ]; then - echo "error: requires main branch" >&2 - exit 1 -fi - -git fetch origin --tags - -V=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*") -V=$(tea semverator bump $V $LEVEL) - -git push origin main - -# not tagging with a version because deno.land will immutably publish it :/ -tea gh release create prerelease --prerelease --generate-notes --title "v$V" -``` +[coverage]: https://coveralls.io/repos/github/pkgxdev/lib/badge.svg +[coveralls]: https://coveralls.io/github/pkgxdev/lib diff --git a/deno.json b/deno.json index 3c22ccd..58996d2 100644 --- a/deno.json +++ b/deno.json @@ -10,7 +10,7 @@ ] } }, - "tea": { + "pkgx": { "dependencies": { "deno.land": "^1.33.3" } diff --git a/examples/awscli/index.mjs b/examples/awscli/index.mjs index 6ecd398..ea5b0f5 100644 --- a/examples/awscli/index.mjs +++ b/examples/awscli/index.mjs @@ -1,8 +1,8 @@ -import * as tea from '@teaxyz/lib'; +import * as pkgx from 'libpkgx'; import * as awsclijs from 'aws-cli-js'; const { Options, Aws } = awsclijs -const { porcelain: { install }, hooks: { useShellEnv } } = tea +const { porcelain: { install }, hooks: { useShellEnv } } = pkgx const opts = new Options(process.env.AWS_ACCESS_KEY_ID, process.env.AWS_SECRET_ACCESS_KEY) diff --git a/examples/awscli/package.json b/examples/awscli/package.json index e5f730a..e9a226e 100644 --- a/examples/awscli/package.json +++ b/examples/awscli/package.json @@ -1,8 +1,8 @@ { - "name": "tea-aws-cli", + "name": "pkgx-aws-cli", "main": "index.mjs", "dependencies": { - "@teaxyz/lib": "^0.9.1", + "libpkgx": "^0.14", "aws-cli-js": "^2.2.3" } } diff --git a/examples/whisper.js b/examples/whisper.js index bf5c773..4fd6b36 100644 --- a/examples/whisper.js +++ b/examples/whisper.js @@ -1,9 +1,9 @@ /* - npm install @teaxyz/lib + npm install libpkgx node whisper.js */ -const { porcelain } = require("@teaxyz/lib") +const { porcelain } = require("libpkgx") const https = require("node:https") const { run } = porcelain const fs = require("node:fs") diff --git a/examples/whisper.mjs b/examples/whisper.mjs index fac539e..f0c8fdb 100644 --- a/examples/whisper.mjs +++ b/examples/whisper.mjs @@ -1,9 +1,9 @@ /* - npm install @teaxyz/lib + npm install libpkgx node whisper.mjs */ -import { porcelain } from "@teaxyz/lib" +import { porcelain } from "libpkgx" import https from "node:https" const { run } = porcelain import fs from "node:fs" diff --git a/examples/whisper.ts b/examples/whisper.ts index 7230e75..9ea9973 100755 --- a/examples/whisper.ts +++ b/examples/whisper.ts @@ -1,10 +1,10 @@ -#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run --allow-env --unstable --allow-net +#!/usr/bin/env -S pkgx deno run --allow-read --allow-write --allow-run --allow-env --unstable --allow-net /* - sh <(curl tea.xyz) https://raw.githubusercontent.com/teaxyz/lib/main/examples/whisper.ts + sh <(curl pkgx.sh) deno https://raw.githubusercontent.com/pkgxdev/lib/main/examples/whisper.ts */ -import { porcelain } from "https://raw.github.com/teaxyz/lib/v0/mod.ts" +import { porcelain } from "https://raw.github.com/pkgxdev/lib/v0/mod.ts" import { green } from "https://deno.land/std/fmt/colors.ts" const { run } = porcelain diff --git a/fixtures/npm-integration-test/index.js b/fixtures/npm-integration-test/index.js index db1ac8c..81bcd9d 100644 --- a/fixtures/npm-integration-test/index.js +++ b/fixtures/npm-integration-test/index.js @@ -1,6 +1,6 @@ -const { porcelain: { run } } = require("@teaxyz/lib") -const { ConfigDefault } = require("@teaxyz/lib/hooks/useConfig") -const { ConsoleLogger } = require("@teaxyz/lib/plumbing/install") +const { porcelain: { run } } = require("libpkgx") +const { ConfigDefault } = require("libpkgx/hooks/useConfig") +const { ConsoleLogger } = require("libpkgx/plumbing/install") console.log(ConfigDefault(), ConsoleLogger()) diff --git a/fixtures/npm-integration-test/package.json b/fixtures/npm-integration-test/package.json index 359e5d8..8b16fd9 100644 --- a/fixtures/npm-integration-test/package.json +++ b/fixtures/npm-integration-test/package.json @@ -7,6 +7,6 @@ "start": "node index.js" }, "dependencies": { - "@teaxyz/lib": "file:../../dist" + "libpkgx": "file:../../dist" } } diff --git a/mod.ts b/mod.ts index c12e8ce..7dc61e9 100644 --- a/mod.ts +++ b/mod.ts @@ -8,7 +8,7 @@ import Path from "./src/utils/Path.ts" export * as types from "./src/types.ts" import * as pkg from "./src/utils/pkg.ts" -import { panic, TeaError } from "./src/utils/error.ts" +import { panic, PkgxError } from "./src/utils/error.ts" import useConfig from "./src/hooks/useConfig.ts" import useOffLicense from "./src/hooks/useOffLicense.ts" import useCache from "./src/hooks/useCache.ts" @@ -24,7 +24,6 @@ import which from "./src/plumbing/which.ts" import link from "./src/plumbing/link.ts" import install, { ConsoleLogger } from "./src/plumbing/install.ts" import resolve, { ResolveError } from "./src/plumbing/resolve.ts" -import { validatePackageRequirement } from "./src/utils/hacks.ts" import useSync from "./src/hooks/useSync.ts" import run, { RunError } from "./src/porcelain/run.ts" import porcelain_install from "./src/porcelain/install.ts" @@ -60,14 +59,10 @@ const porcelain = { run } -const hacks = { - validatePackageRequirement -} - export { - utils, hooks, plumbing, porcelain, hacks, + utils, hooks, plumbing, porcelain, semver, - TeaError, + PkgxError, RunError, ResolveError, PantryError, PantryParseError, PantryNotFoundError, PackageNotFoundError, diff --git a/scripts/publish-release.sh b/scripts/publish-release.sh new file mode 100755 index 0000000..6bf0e79 --- /dev/null +++ b/scripts/publish-release.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env -S pkgx +git +gh +jq +gum +npx +deno bash -eo pipefail + +# the back and forth here is because draft releases cannot trigger +# GitHub Actions 😒 + +if ! git diff-index --quiet HEAD --; then + echo "error: dirty working tree" >&2 + exit 1 +fi + +if [ "$(git rev-parse --abbrev-ref HEAD)" != main ]; then + echo "error: requires main branch" >&2 + exit 1 +fi + +# ensure we have the latest version tags +git fetch origin -pft + +versions="$(git tag | grep '^v[0-9]\+\.[0-9]\+\.[0-9]\+')" +v_latest="$(npx -- semver --include-prerelease $versions | tail -n1)" + +_is_prerelease() { + deno run - <" >&2 + exit 1;; +*) + v_new=$1 + ;; +esac + +if [ $v_new = $v_latest ]; then + echo "$v_new already exists!" >&2 + exit 1 +fi + +gum confirm "prepare draft release for $v_new?" || exit 1 + +git push origin main + +is_prerelease=$(_is_prerelease $v_new) + +gh release create \ + v$v_new \ + --draft=true \ + --prerelease=$is_prerelease \ + --generate-notes \ + --notes-start-tag=v$v_latest \ + --title=$v_new + +gh workflow run cd.yml --raw-field version="$v_new" +# ^^ infuriatingly does not tell us the ID of the run + +gum spin --title 'sleeping 5s because GitHub API is slow' -- sleep 5 + +run_id=$(gh run list --json databaseId --workflow=cd.yml | jq '.[0].databaseId') + +if ! gh run watch --exit-status $run_id; then + foo=$? + gum format -- "> gh run view --web $run_id" + exit $foo +fi + +gh release view v$v_new + +gum confirm "draft prepared, release $v_new?" || exit 1 + +gh release edit \ + v$v_new \ + --verify-tag \ + $(if [ $is_prerelease = false ]; then echo --latest; fi) \ + --draft=false \ + --discussion-category=Announcements + +gh release view v$v_new --web diff --git a/scripts/run-coverage.sh b/scripts/run-coverage.sh new file mode 100755 index 0000000..5dc7941 --- /dev/null +++ b/scripts/run-coverage.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env -S pkgx +genhtml +deno bash + +# TODO use git to determine file changes and open those reports + +set -eo pipefail + +rm -rf cov_profile +deno task test --fail-fast --coverage=cov_profile +deno coverage cov_profile --lcov --output=cov_profile/coverage_file.lcov +genhtml -o cov_profile cov_profile/coverage_file.lcov + +if test "$1" != "--reload"; then + open cov_profile/index.html +else + osascript -e 'tell application "Safari" to tell the current tab of the front window to do JavaScript "location.reload()"' +fi diff --git a/src/hooks/useCache.test.ts b/src/hooks/useCache.test.ts index e58b261..78b6044 100644 --- a/src/hooks/useCache.test.ts +++ b/src/hooks/useCache.test.ts @@ -1,5 +1,5 @@ import { Stowage, StowageNativeBottle } from "../types.ts" -import { assertEquals } from "deno/testing/asserts.ts" +import { assertEquals } from "deno/assert/mod.ts" import SemVer from "../utils/semver.ts" import useConfig from "./useConfig.ts" import useCache from "./useCache.ts" diff --git a/src/hooks/useCellar.test.ts b/src/hooks/useCellar.test.ts index 753c881..03d415d 100644 --- a/src/hooks/useCellar.test.ts +++ b/src/hooks/useCellar.test.ts @@ -1,4 +1,4 @@ -import { assertEquals, assertRejects } from "deno/testing/asserts.ts" +import { assertEquals, assertRejects } from "deno/assert/mod.ts" import SemVer, * as semver from "../utils/semver.ts" import { useTestConfig } from "./useTestConfig.ts" import install from "../plumbing/install.ts" @@ -15,7 +15,7 @@ Deno.test("useCellar.resolve()", async () => { await useCellar().resolve({ project: "python.org", constraint: new semver.Range("^3") }) await useCellar().resolve(installation.path) - assertRejects(() => useCellar().resolve({ project: "python.org", constraint: new semver.Range("@300")})) + await assertRejects(() => useCellar().resolve({ project: "python.org", constraint: new semver.Range("@300")})) }) Deno.test("useCellar.has()", async () => { diff --git a/src/hooks/useCellar.ts b/src/hooks/useCellar.ts index ea4e016..c63933f 100644 --- a/src/hooks/useCellar.ts +++ b/src/hooks/useCellar.ts @@ -1,11 +1,11 @@ import { Package, PackageRequirement, Installation } from "../types.ts" -import { TeaError } from "../utils/error.ts" +import { PkgxError } from "../utils/error.ts" import * as pkgutils from "../utils/pkg.ts" import SemVer from "../utils/semver.ts" import useConfig from "./useConfig.ts" import Path from "../utils/Path.ts" -export class InstallationNotFoundError extends TeaError { +export class InstallationNotFoundError extends PkgxError { pkg: Package | PackageRequirement constructor(pkg: Package | PackageRequirement) { @@ -17,10 +17,10 @@ export class InstallationNotFoundError extends TeaError { export default function useCellar() { const config = useConfig() - /// eg. ~/.tea/deno.land + /// eg. ~/.pkgx/deno.land const shelf = (project: string) => config.prefix.join(project) - /// eg. ~/.tea/deno.land/v1.2.3 + /// eg. ~/.pkgx/deno.land/v1.2.3 const keg = (pkg: Package) => shelf(pkg.project).join(`v${pkg.version}`) /// returns the `Installation` if the pkg is installed diff --git a/src/hooks/useConfig.test.ts b/src/hooks/useConfig.test.ts index 709a449..37070ad 100644 --- a/src/hooks/useConfig.test.ts +++ b/src/hooks/useConfig.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals, assertFalse, assertThrows, assertMatch } from "deno/testing/asserts.ts" +import { assert, assertEquals, assertFalse, assertThrows, assertMatch } from "deno/assert/mod.ts" import { _internals, ConfigDefault } from "./useConfig.ts" import { useTestConfig } from "./useTestConfig.ts" import Path from "../utils/Path.ts" @@ -7,12 +7,12 @@ Deno.test("useConfig", () => { let config = useTestConfig() if (Deno === undefined) { - assertMatch(config.UserAgent!, /tea.lib\/\d+\.\d+.\d+/) + assertMatch(config.UserAgent!, /libpkgx\/\d+\.\d+.\d+/) } else { - assertEquals(config.UserAgent, "tea.lib") + assertEquals(config.UserAgent, "libpkgx") } - config = ConfigDefault({ TEA_PANTRY_PATH: "/foo:/bar", CI: "true" }) + config = ConfigDefault({ PKGX_PANTRY_PATH: "/foo:/bar", CI: "true" }) assertEquals(config.pantries.map(x => x.string), ["/foo", "/bar"]) assertEquals(config.options.compression, "gz") @@ -25,15 +25,15 @@ Deno.test("useConfig", () => { assert(_internals.initialized()) }) -Deno.test("useConfig empty TEA_PREFIX is ignored", () => { - assertEquals(ConfigDefault({ TEA_PREFIX: "" }).prefix, Path.home().join(".tea")) - assertEquals(ConfigDefault({ TEA_PREFIX: " " }).prefix, Path.home().join(".tea")) - assertEquals(ConfigDefault({ TEA_PREFIX: " / " }).prefix, Path.root) - assertThrows(() => ConfigDefault({ TEA_PREFIX: " foo " })) - assertThrows(() => ConfigDefault({ TEA_PREFIX: "foo" })) +Deno.test("useConfig empty PKGX_DIR is ignored", () => { + assertEquals(ConfigDefault({ PKGX_DIR: "" }).prefix, Path.home().join(".pkgx")) + assertEquals(ConfigDefault({ PKGX_DIR: " " }).prefix, Path.home().join(".pkgx")) + assertEquals(ConfigDefault({ PKGX_DIR: " / " }).prefix, Path.root) + assertThrows(() => ConfigDefault({ PKGX_DIR: " foo " })) + assertThrows(() => ConfigDefault({ PKGX_DIR: "foo" })) }) -Deno.test("useConfig empty TEA_PANTRY_PATH is ignored", () => { - assertEquals(ConfigDefault({ TEA_PANTRY_PATH: "" }).pantries, []) - assertEquals(ConfigDefault({ TEA_PANTRY_PATH: " : :" }).pantries, []) +Deno.test("useConfig empty PKGX_PANTRY_PATH is ignored", () => { + assertEquals(ConfigDefault({ PKGX_PANTRY_PATH: "" }).pantries, []) + assertEquals(ConfigDefault({ PKGX_PANTRY_PATH: " : :" }).pantries, []) }) diff --git a/src/hooks/useConfig.ts b/src/hooks/useConfig.ts index 5a0fe37..ba0016d 100644 --- a/src/hooks/useConfig.ts +++ b/src/hooks/useConfig.ts @@ -6,6 +6,7 @@ export interface Config { prefix: Path pantries: Path[] cache: Path + data: Path options: { /// prefer xz or gz for bottle downloads @@ -17,12 +18,31 @@ export interface Config { git?: Path } +function platform_cache_default() { + if (host().platform == 'darwin') { + return Path.home().join('Library/Caches') + } else { + return Path.home().join('.cache') + } +} + export function ConfigDefault(env = Deno.env.toObject()): Config { - const prefix = flatmap(env['TEA_PREFIX']?.trim(), x => new Path(x)) ?? Path.home().join('.tea') - const pantries = env['TEA_PANTRY_PATH']?.split(":").compact(x => flatmap(x.trim(), x => Path.abs(x) ?? Path.cwd().join(x))) ?? [] - const cache = Path.abs(env['TEA_CACHE_DIR']) ?? prefix.join('tea.xyz/var/www') + const prefix = flatmap(env['PKGX_DIR']?.trim(), x => new Path(x)) ?? Path.home().join('.pkgx') + const pantries = env['PKGX_PANTRY_PATH']?.split(":").compact(x => flatmap(x.trim(), x => Path.abs(x) ?? Path.cwd().join(x))) ?? [] + const cache = (flatmap(env["XDG_CACHE_HOME"], x => new Path(x)) ?? platform_cache_default()).join("pkgx") const isCI = boolize(env['CI']) ?? false - const UserAgent = flatmap(getv(), v => `tea.lib/${v}`) ?? 'tea.lib' + const UserAgent = flatmap(getv(), v => `libpkgx/${v}`) ?? 'libpkgx' + + const data = (() => { + const xdg = env["XDG_DATA_HOME"] + if (xdg) { + return new Path(xdg) + } else if (host().platform == "darwin") { + return Path.home().join("Library/Application Support") + } else { + return Path.home().join(".local/share") + } + })().join("pkgx") //TODO prefer 'xz' on Linux (as well) if supported const compression = !isCI && host().platform == 'darwin' ? 'xz' : 'gz' @@ -31,6 +51,7 @@ export function ConfigDefault(env = Deno.env.toObject()): Config { prefix, pantries, cache, + data, UserAgent, options: { compression, @@ -50,15 +71,15 @@ function getv(): string | undefined { } } -const gt = globalThis as unknown as {xyz_tea_config?: Config} +const gt = globalThis as unknown as {sh_pkgx_config?: Config} export default function useConfig(input?: Config): Config { // storing on globalThis so our config is shared across - // potentially multiple versions of libtea being loaded in the same process - if (!gt.xyz_tea_config || input) { - gt.xyz_tea_config = input ?? ConfigDefault() + // potentially multiple versions of libpkgx being loaded in the same process + if (!gt.sh_pkgx_config || input) { + gt.sh_pkgx_config = input ?? ConfigDefault() } - return {...gt.xyz_tea_config} // copy to prevent mutation + return {...gt.sh_pkgx_config} // copy to prevent mutation } function boolize(input: string | undefined): boolean | undefined { @@ -75,21 +96,21 @@ function boolize(input: string | undefined): boolean | undefined { } function reset() { - return delete gt.xyz_tea_config + return delete gt.sh_pkgx_config } function initialized() { - return gt.xyz_tea_config !== undefined + return gt.sh_pkgx_config !== undefined } export const _internals = { reset, initialized, boolize } -/// we support a tea installed or system installed git, nothing else -/// eg. `git` could be a symlink in `PATH` to tea, which would cause a fork bomb +/// we support a pkgx installed or system installed git, nothing else +/// eg. `git` could be a symlink in `PATH` to pkgx, which would cause a fork bomb /// on darwin if xcode or xcode/clt is not installed this will fail to our http fallback above //TODO be able to use our own git if installed -//NOTE however we don’t want to have to fully hydrate its env when libtea is initialized only when needed so… +//NOTE however we don’t want to have to fully hydrate its env when libpkgx is initialized only when needed so… function git(_prefix: Path, PATH?: string): Path | undefined { return usr() diff --git a/src/hooks/useDownload.test.ts b/src/hooks/useDownload.test.ts index 927aa23..7ddc6f5 100644 --- a/src/hooks/useDownload.test.ts +++ b/src/hooks/useDownload.test.ts @@ -1,11 +1,11 @@ import { useTestConfig } from "./useTestConfig.ts" -import { assert } from "deno/testing/asserts.ts" +import { assert } from "deno/assert/mod.ts" import useDownload from "./useDownload.ts" Deno.test("etag-mtime-check", async runner => { - useTestConfig({ TEA_CACHE_DIR: Deno.makeTempDirSync() }) + useTestConfig({ PKGX_DIR: Deno.makeTempDirSync() }) - const src = new URL("https://dist.tea.xyz/ijg.org/versions.txt") + const src = new URL("https://dist.pkgx.dev/ijg.org/versions.txt") const { download, cache } = useDownload() await runner.step("download", async () => { diff --git a/src/hooks/useDownload.ts b/src/hooks/useDownload.ts index 0dae3f2..de5b3cd 100644 --- a/src/hooks/useDownload.ts +++ b/src/hooks/useDownload.ts @@ -1,7 +1,7 @@ import { deno } from "../deps.ts" const { crypto: crypto_, streams: { writeAll } } = deno const { toHashString, crypto } = crypto_ -import { TeaError, panic } from "../utils/error.ts" +import { PkgxError, panic } from "../utils/error.ts" import useConfig from "./useConfig.ts" import useFetch from "./useFetch.ts" import Path from "../utils/Path.ts" @@ -15,7 +15,7 @@ interface DownloadOptions { logger?: (info: {src: URL, dst: Path, rcvd?: number, total?: number }) => void } -export class DownloadError extends TeaError { +export class DownloadError extends PkgxError { status: number src: URL headers?: Record diff --git a/src/hooks/useInventory.ts b/src/hooks/useInventory.ts index 903fad4..4aaa380 100644 --- a/src/hooks/useInventory.ts +++ b/src/hooks/useInventory.ts @@ -27,7 +27,7 @@ const select = async (rq: PackageRequirement | Package) => { const get = async (rq: PackageRequirement | Package) => { const { platform, arch } = host() - const url = new URL('https://dist.tea.xyz') + const url = new URL('https://dist.pkgx.dev') url.pathname = Path.root.join(rq.project, platform, arch, 'versions.txt').string const rsp = await useFetch(url) diff --git a/src/hooks/useMoustaches.test.ts b/src/hooks/useMoustaches.test.ts index e32636e..e0dd96a 100644 --- a/src/hooks/useMoustaches.test.ts +++ b/src/hooks/useMoustaches.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "deno/testing/asserts.ts" +import { assertEquals } from "deno/assert/mod.ts" import { useTestConfig } from "./useTestConfig.ts" import useMoustaches from "./useMoustaches.ts" import { Package } from "../types.ts" @@ -9,10 +9,10 @@ Deno.test("useMoustaches", () => { const moustaches = useMoustaches() const pkg: Package = { - project: "tea.xyz/test", + project: "pkgx.sh/test", version: new SemVer("1.0.0") } const tokens = moustaches.tokenize.all(pkg, []) - assertEquals(tokens[0].to, conf.prefix.join(`tea.xyz/test/v${pkg.version}`).string) + assertEquals(tokens[0].to, conf.prefix.join(`pkgx.sh/test/v${pkg.version}`).string) }) diff --git a/src/hooks/useMoustaches.ts b/src/hooks/useMoustaches.ts index 49902cf..261bb43 100644 --- a/src/hooks/useMoustaches.ts +++ b/src/hooks/useMoustaches.ts @@ -1,7 +1,7 @@ -import useConfig from "./useConfig.ts" +import { Package, Installation } from "../types.ts" import SemVer from "../utils/semver.ts" +import useConfig from "./useConfig.ts" import useCellar from "./useCellar.ts" -import { Package, Installation } from "../types.ts" import host from "../utils/host.ts" import * as os from "node:os" @@ -62,12 +62,12 @@ export default function() { return map } - const tea = () => [{ from: "tea.prefix", to: config.prefix.string }] + const pkgx = () => [{ from: "pkgx.prefix", to: config.prefix.string }] const all = (pkg: Package, deps_: Installation[]) => [ ...deps(deps_), ...tokenizePackage(pkg), - ...tea(), + ...pkgx(), ...base.tokenize.version(pkg.version), ...base.tokenize.host(), ] @@ -76,7 +76,7 @@ export default function() { apply: base.apply, tokenize: { ...base.tokenize, - deps, tea, all + deps, pkgx, all } } } diff --git a/src/hooks/useOffLicense.ts b/src/hooks/useOffLicense.ts index a3d3ffb..d826f67 100644 --- a/src/hooks/useOffLicense.ts +++ b/src/hooks/useOffLicense.ts @@ -24,5 +24,5 @@ function key(stowage: Stowage) { } function url(stowage: Stowage) { - return new URL(`https://dist.tea.xyz/${key(stowage)}`) + return new URL(`https://dist.pkgx.dev/${key(stowage)}`) } diff --git a/src/hooks/usePantry.test.ts b/src/hooks/usePantry.test.ts index 56d08c1..62cd75d 100644 --- a/src/hooks/usePantry.test.ts +++ b/src/hooks/usePantry.test.ts @@ -1,37 +1,54 @@ -import { assert, assertEquals, assertFalse } from "deno/testing/asserts.ts" +import { assert, assertEquals, assertThrows } from "deno/assert/mod.ts" +import usePantry, { validatePackageRequirement } from "./usePantry.ts" +import { _internals as _config_internals } from "./useConfig.ts" import { useTestConfig } from "./useTestConfig.ts" import { _internals } from "../utils/host.ts" import { stub } from "deno/testing/mock.ts" import SemVer from "../utils/semver.ts" -import usePantry from "./usePantry.ts" import Path from "../utils/Path.ts" -import { _internals as _config_internals } from "./useConfig.ts" +import useSync from "./useSync.ts" Deno.test("provides()", async () => { + useTestConfig() + await useSync() const exenames = await usePantry().project("python.org").provides() assert(exenames.includes("python")) }) Deno.test("which()", async () => { + useTestConfig() + await useSync() const pkg = await usePantry().which({ interprets: ".py" }) assertEquals(pkg?.project, "python.org") }) Deno.test("provider()", async () => { + useTestConfig() + await useSync() const provides = await usePantry().project("npmjs.com").provider() const foo = provides!('truffle') assertEquals(foo![0], 'npx') }) Deno.test("available()", async () => { + useTestConfig() + await useSync() const stubber = stub(_internals, 'platform', () => "darwin" as "darwin" | "linux") assert(await usePantry().project("agpt.co").available()) stubber.restore() }) Deno.test("runtime.env", async () => { - const TEA_PANTRY_PATH = new Path(Deno.env.get("SRCROOT")!).join("fixtures").string - const { prefix } = useTestConfig({ TEA_PANTRY_PATH }) + const srcroot = (() => { + // because when running via dnt the path of this file is different + if (Path.cwd().parent().parent().join("fixtures").isDirectory()) { + return Path.cwd().parent().parent() + } else { + return new Path(new URL(import.meta.url).pathname).parent().parent().parent() + } + })() + const PKGX_PANTRY_PATH = srcroot.join("fixtures").string + const { prefix } = useTestConfig({ PKGX_PANTRY_PATH }) const deps = [{ pkg: { @@ -49,11 +66,38 @@ Deno.test("runtime.env", async () => { }) Deno.test("missing()", () => { - useTestConfig({TEA_PANTRY_PATH: "/a"}) + useTestConfig({PKGX_PANTRY_PATH: "/a"}) assert(usePantry().missing()) }) -Deno.test("!missing()", () => { - useTestConfig() - assertFalse(usePantry().missing()) + +Deno.test("validatePackageRequirement - valid input", () => { + const result = validatePackageRequirement("pkgx.sh/test", "^1.0.0") + assertEquals(result?.project, "pkgx.sh/test") + assertEquals(result?.constraint.toString(), "^1") +}) + +Deno.test("validatePackageRequirement - invalid constraint", () => { + assertThrows(() => validatePackageRequirement("pkgx.sh/test", "nonsense")) +}) + +Deno.test("validatePackageRequirement - number constraint", () => { + const result = validatePackageRequirement("pkgx.sh/test", 1) + assertEquals(result?.constraint.toString(), "^1") +}) + + +Deno.test("validatePackageRequirement - valid input", () => { + const result = validatePackageRequirement("pkgx.sh/test", "^1.0.0") + assertEquals(result?.project, "pkgx.sh/test") + assertEquals(result?.constraint.toString(), "^1") +}) + +Deno.test("validatePackageRequirement - invalid constraint", () => { + assertThrows(() => validatePackageRequirement("pkgx.sh/test", "nonsense")) +}) + +Deno.test("validatePackageRequirement - number constraint", () => { + const result = validatePackageRequirement("pkgx.sh/test", 1) + assertEquals(result?.constraint.toString(), "^1") }) diff --git a/src/hooks/usePantry.ts b/src/hooks/usePantry.ts index c03bcf5..d83f1e7 100644 --- a/src/hooks/usePantry.ts +++ b/src/hooks/usePantry.ts @@ -1,11 +1,10 @@ import { is_what, PlainObject } from "../deps.ts" const { isNumber, isPlainObject, isString, isArray, isPrimitive, isBoolean } = is_what -import { validatePackageRequirement } from "../utils/hacks.ts" -import { Package, Installation } from "../types.ts" +import { Package, Installation, PackageRequirement } from "../types.ts" +import SemVer, * as semver from "../utils/semver.ts" import useMoustaches from "./useMoustaches.ts" -import { TeaError } from "../utils/error.ts" +import { PkgxError } from "../utils/error.ts" import { validate } from "../utils/misc.ts" -import SemVer from "../utils/semver.ts" import useConfig from "./useConfig.ts" import host from "../utils/host.ts" import Path from "../utils/Path.ts" @@ -15,7 +14,7 @@ export interface Interpreter { args: string[] } -export class PantryError extends TeaError +export class PantryError extends PkgxError {} export class PantryParseError extends PantryError { @@ -45,8 +44,7 @@ export class PantryNotFoundError extends PantryError { } export default function usePantry() { - const config = useConfig() - const prefix = config.prefix.join('tea.xyz/var/pantry/projects') + const prefix = useConfig().data.join("pantry/projects") async function* ls(): AsyncGenerator { const seen = new Set() @@ -243,7 +241,7 @@ export default function usePantry() { if (prefix.isDirectory()) { rv.push(prefix) } - for (const path of config.pantries.reverse()) { + for (const path of useConfig().pantries.reverse()) { rv.unshift(path.join("projects")) } @@ -266,6 +264,24 @@ export function parse_pkgs_node(node: any) { validatePackageRequirement(project, constraint)) } +export function validatePackageRequirement(project: string, constraint: unknown): PackageRequirement { + if (isNumber(constraint)) { + constraint = `^${constraint}` + } else if (!isString(constraint)) { + throw new Error(`invalid constraint for ${project}: ${constraint}`) + } + + constraint = semver.Range.parse(constraint as string) + if (!constraint) { + throw new PkgxError("invalid constraint for " + project + ": " + constraint) + } + + return { + project, + constraint: constraint as semver.Range + } +} + /// expands platform specific keys into the object /// expands inplace because JS is nuts and you have to suck it up function platform_reduce(env: PlainObject) { @@ -331,7 +347,6 @@ export function expand_env_obj(env_: PlainObject, pkg: Package, deps: Installati const mm = useMoustaches() const home = Path.home().string const obj = [ - { from: 'env.HOME', to: home }, // historic, should be removed at v1 { from: 'home', to: home } // remove, stick with just ~ ] obj.push(...mm.tokenize.all(pkg, deps)) diff --git a/src/hooks/useShellEnv.test.ts b/src/hooks/useShellEnv.test.ts index b9aa2bc..6a718ac 100644 --- a/src/hooks/useShellEnv.test.ts +++ b/src/hooks/useShellEnv.test.ts @@ -5,11 +5,14 @@ import useShellEnv from "./useShellEnv.ts" import hydrate from "../plumbing/hydrate.ts" import resolve from "../plumbing/resolve.ts" import install from "../plumbing/install.ts" +import useSync from "./useSync.ts" Deno.test("useShellEnv", async () => { const { map, flatten } = useShellEnv() useTestConfig() + await useSync() + const rv1 = await hydrate({ project: "python.org", constraint: new semver.Range("^3.11") }) const rv2 = await resolve(rv1.pkgs) @@ -22,7 +25,7 @@ Deno.test("useShellEnv", async () => { const env = await map({ installations }) // test that we installed the correct platform binaries - // ※ https://github.com/teaxyz/lib/pull/11/checks + // ※ https://github.com/pkgxdev/lib/pull/11/checks execSync("python --version", { env: flatten(env) }) //NOTE ^^ using execSync rather than Deno.run as the shim doesn’t behave consistently between deno and node }) diff --git a/src/hooks/useShellEnv.ts b/src/hooks/useShellEnv.ts index 35fe290..e5dcbbe 100644 --- a/src/hooks/useShellEnv.ts +++ b/src/hooks/useShellEnv.ts @@ -1,5 +1,4 @@ import { Installation } from "../types.ts" -import useConfig from "./useConfig.ts" import usePantry from "./usePantry.ts" import host from "../utils/host.ts" @@ -15,7 +14,7 @@ export const EnvKeys = [ 'DYLD_FALLBACK_LIBRARY_PATH', 'SSL_CERT_FILE', 'LDFLAGS', - 'TEA_PREFIX', + 'PKGX_DIR', 'ACLOCAL_PATH' ] as const export type EnvKey = typeof EnvKeys[number] @@ -47,7 +46,7 @@ async function map({installations}: Options): Promise> for (const installation of installations) { if (!seen.insert(installation.pkg.project).inserted) { - console.warn("tea: env is being duped:", installation.pkg.project) + console.warn("pkgx: env is being duped:", installation.pkg.project) } for (const key of EnvKeys) { @@ -105,12 +104,6 @@ async function map({installations}: Options): Promise> rv[key] = vars[key]!.toArray() } - if (isMac) { - // required to link to our libs - // tea.xyz/gx/cc automatically adds this, but use of any other compilers will not - rv["LDFLAGS"] = [`-Wl,-rpath,${useConfig().prefix}`] - } - // don’t break `man` lol rv["MANPATH"]?.push("/usr/share/man") @@ -134,7 +127,7 @@ function suffixes(key: EnvKey) { case 'CMAKE_PREFIX_PATH': case 'SSL_CERT_FILE': case 'LDFLAGS': - case 'TEA_PREFIX': + case 'PKGX_DIR': case 'ACLOCAL_PATH': return [] // we handle these specially default: { diff --git a/src/hooks/useSync.test.ts b/src/hooks/useSync.test.ts index 5d4983f..21c6d1e 100644 --- a/src/hooks/useSync.test.ts +++ b/src/hooks/useSync.test.ts @@ -1,19 +1,19 @@ import { useTestConfig } from "./useTestConfig.ts" -import { assert } from "deno/testing/asserts.ts" +import { assert } from "deno/assert/mod.ts" import usePantry from "./usePantry.ts" import useSync from "./useSync.ts" Deno.test("useSync", async runner => { await runner.step("w/o git", async () => { - const TEA_PREFIX = Deno.makeTempDirSync() - const conf = useTestConfig({ TEA_PREFIX, TEA_PANTRY_PATH: `${TEA_PREFIX}/tea.xyz/var/pantry` }) + const PKGX_DIR = Deno.makeTempDirSync() + const conf = useTestConfig({ PKGX_DIR, HOME: `${PKGX_DIR}/home` }) assert(conf.git === undefined) await test() }) await runner.step("w/git", async () => { - const TEA_PREFIX = Deno.makeTempDirSync() - const conf = useTestConfig({ TEA_PREFIX, TEA_PANTRY_PATH: `${TEA_PREFIX}/tea.xyz/var/pantry`, PATH: "/usr/bin" }) + const PKGX_DIR = Deno.makeTempDirSync() + const conf = useTestConfig({ PKGX_DIR, HOME: `${PKGX_DIR}/home`, PATH: "/usr/bin" }) assert(conf.git !== undefined) await test() @@ -24,14 +24,14 @@ Deno.test("useSync", async runner => { async function test() { let errord = false try { - await usePantry().project("tea.xyz/brewkit").available() + await usePantry().project("python.org").available() } catch { errord = true } - assert(errord) + assert(errord, `should be no pantry but there is! ${usePantry().prefix}`) await useSync() - assert(await usePantry().project("tea.xyz/brewkit").available()) + assert(await usePantry().project("python.org").available()) } }) diff --git a/src/hooks/useSync.ts b/src/hooks/useSync.ts index 8fa5b5d..9258413 100644 --- a/src/hooks/useSync.ts +++ b/src/hooks/useSync.ts @@ -27,12 +27,12 @@ export default async function(logger?: Logger) { try { //TODO if there was already a lock, just wait on it, don’t do the following stuff - const git_dir = pantry_dir.parent().join("pantries/teaxyz/pantry") + const git_dir = pantry_dir.parent().join("pantries/pkgxdev/pantry") if (git_dir.join("HEAD").isFile()) { await git("-C", git_dir, "fetch", "--quiet", "origin", "--force", "main:main") } else { - await git("clone", "--quiet", "--bare", "--depth=1", "https://github.com/teaxyz/pantry", git_dir) + await git("clone", "--quiet", "--bare", "--depth=1", "https://github.com/pkgxdev/pantry", git_dir) } await git("--git-dir", git_dir, "--work-tree", pantry_dir, "checkout", "--quiet", "--force") @@ -41,7 +41,7 @@ export default async function(logger?: Logger) { // git failure or no git installed // ∴ download the latest tarball and uncompress over the top //FIXME deleted packages will not be removed with this method - const src = new URL(`https://github.com/teaxyz/pantry/archive/refs/heads/main.tar.gz`) + const src = new URL(`https://github.com/pkgxdev/pantry/archive/refs/heads/main.tar.gz`) const proc = Deno.run({ cmd: ["tar", "xz", "--strip-components=1"], cwd: pantry_dir.string, diff --git a/src/hooks/useTestConfig.ts b/src/hooks/useTestConfig.ts index ead4f6b..78be9a5 100644 --- a/src/hooks/useTestConfig.ts +++ b/src/hooks/useTestConfig.ts @@ -5,11 +5,10 @@ export function useTestConfig(env?: Record) { env ??= {} /// always prefer a new prefix - env.TEA_PREFIX ??= Path.mktemp().string - - /// reuse these unless the test overrides them to speed up testing - env.TEA_CACHE_DIR ??= Path.home().join(".tea/tea.xyz/var/www").string - env.TEA_PANTRY_PATH ??= Path.home().join(".tea/tea.xyz/var/pantry").string + env.HOME ??= Path.mktemp().string + env.PKGX_DIR ??= Path.mktemp().string + env.XDG_DATA_HOME ??= Path.mktemp().string + env.XDG_CACHE_HOME ??= Path.mktemp().string return useConfig(ConfigDefault(env)) } diff --git a/src/plumbing/hydrate.test.ts b/src/plumbing/hydrate.test.ts index 8001f18..b71c84b 100644 --- a/src/plumbing/hydrate.test.ts +++ b/src/plumbing/hydrate.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "deno/testing/asserts.ts" +import { assertEquals } from "deno/assert/mod.ts" import { describe, it } from "deno/testing/bdd.ts" import { PackageRequirement } from "../types.ts" import * as semver from "../utils/semver.ts" diff --git a/src/plumbing/hydrate.ts b/src/plumbing/hydrate.ts index dff956a..7f97ad6 100644 --- a/src/plumbing/hydrate.ts +++ b/src/plumbing/hydrate.ts @@ -74,7 +74,7 @@ export default async function hydrate( for (const dep of await get_deps(current_node.pkg, initial_set.has(current_node.project))) { if (children.has(dep.project)) { if (!bootstrap.has(dep.project)) { - console.warn(`tea: cyclic dep: ${dep.project}: ${current_node.project}`) + console.warn(`pkgx: cyclic dep: ${dep.project}: ${current_node.project}`) bootstrap.add(dep.project) } } else { diff --git a/src/plumbing/install.test.ts b/src/plumbing/install.test.ts index 1b0a034..1c6d012 100644 --- a/src/plumbing/install.test.ts +++ b/src/plumbing/install.test.ts @@ -1,5 +1,5 @@ import { useTestConfig as useTestConfigBase } from "../hooks/useTestConfig.ts" -import { assert, assertEquals, assertFalse } from "deno/testing/asserts.ts" +import { assert, assertEquals, assertFalse } from "deno/assert/mod.ts" import install, { ConsoleLogger, Logger } from "./install.ts" import { stub } from "deno/testing/mock.ts" import SemVer from "../utils/semver.ts" @@ -7,8 +7,8 @@ import { Package } from "../types.ts" Deno.test("install()", async runner => { const pkg: Package = { - project: "tea.xyz/brewkit", - version: new SemVer("0.30.0") + project: "darwinsys.com/file", + version: new SemVer("5.43.0") } const conf = useTestConfig() @@ -43,8 +43,8 @@ Deno.test("install()", async runner => { Deno.test("install locks", async () => { const pkg: Package = { - project: "tea.xyz/brewkit", - version: new SemVer("0.30.0") + project: "darwinsys.com/file", + version: new SemVer("5.43.0") } const conf = useTestConfig() @@ -71,5 +71,5 @@ Deno.test("install locks", async () => { }) function useTestConfig() { - return useTestConfigBase({ TEA_CACHE_DIR: Deno.makeTempDirSync() }) + return useTestConfigBase({ XDG_CACHE_HOME: Deno.makeTempDirSync() }) } diff --git a/src/plumbing/install.ts b/src/plumbing/install.ts index f9bbb99..ace84ab 100644 --- a/src/plumbing/install.ts +++ b/src/plumbing/install.ts @@ -19,11 +19,11 @@ export default async function install(pkg: Package, logger?: Logger): Promise { const pkg: Package = { - project: "tea.xyz/brewkit", - version: new SemVer("0.30.0") + project: "darwinsys.com/file", + version: new SemVer("5.43.0") } await runner.step("link()", async () => { diff --git a/src/plumbing/resolve.test.ts b/src/plumbing/resolve.test.ts index 9fc500d..52dc448 100644 --- a/src/plumbing/resolve.test.ts +++ b/src/plumbing/resolve.test.ts @@ -120,7 +120,7 @@ Deno.test("resolve cellar.has", { const permissions = { net: false, read: true, env: ["TMPDIR", "HOME", "TMP", "TEMP"], write: true /*FIXME*/ } -// https://github.com/teaxyz/cli/issues/655 +// https://github.com/pkgxdev/cli/issues/655 Deno.test("postgres@500 fails", { permissions }, async () => { useTestConfig() @@ -140,14 +140,14 @@ Deno.test("postgres@500 fails", { permissions }, async () => { ] try { - // https://github.com/teaxyz/cli/issues/655 + // https://github.com/pkgxdev/cli/issues/655 await assertRejects(() => resolve(pkgs)) } finally { stub1.restore() } }) -// https://github.com/teaxyz/cli/issues/655 +// https://github.com/pkgxdev/cli/issues/655 Deno.test("postgres@500 fails if installed", { permissions }, async () => { const pkg = { project: "posqtgres.org", @@ -180,7 +180,7 @@ Deno.test("postgres@500 fails if installed", { permissions }, async () => { ] try { - // https://github.com/teaxyz/cli/issues/655 + // https://github.com/pkgxdev/cli/issues/655 await assertRejects(() => resolve(pkgs)) } finally { stub1.restore() diff --git a/src/plumbing/resolve.ts b/src/plumbing/resolve.ts index d3106b7..aaefc60 100644 --- a/src/plumbing/resolve.ts +++ b/src/plumbing/resolve.ts @@ -2,7 +2,7 @@ import { Package, PackageRequirement, Installation } from "../types.ts" import useInventory from "../hooks/useInventory.ts" import { str as pkgstr } from "../utils/pkg.ts" import useCellar from "../hooks/useCellar.ts" -import { TeaError } from "../utils/error.ts" +import { PkgxError } from "../utils/error.ts" /// NOTE resolves to bottles /// NOTE contract there are no duplicate projects in input @@ -18,7 +18,7 @@ export interface Resolution { pending: Package[] } -export class ResolveError extends TeaError { +export class ResolveError extends PkgxError { pkg: Package | PackageRequirement constructor(pkg: Package | PackageRequirement) { diff --git a/src/plumbing/which.test.ts b/src/plumbing/which.test.ts index 8b71c63..1bf3a01 100644 --- a/src/plumbing/which.test.ts +++ b/src/plumbing/which.test.ts @@ -1,14 +1,20 @@ -import { assert, assertEquals } from "deno/testing/asserts.ts" +import { assert, assertEquals } from "deno/assert/mod.ts" import { isArray } from "is-what" import which from "./which.ts" +import { useTestConfig } from "../hooks/useTestConfig.ts"; +import useSync from "../hooks/useSync.ts"; Deno.test("which('ls')", async () => { + useTestConfig() + await useSync() const foo = await which('ls') assert(!isArray(foo)) assert(foo) }) Deno.test("which('kill-port')", async () => { + useTestConfig() + await useSync() const foo = await which('kill-port') assert(!isArray(foo)) assert(foo) @@ -18,6 +24,8 @@ Deno.test("which('kill-port')", async () => { }) Deno.test("which('nvim')", async () => { + useTestConfig() + await useSync() const foo = await which('kill-port', { all: true }) assert(isArray(foo)) assert(foo.length) diff --git a/src/porcelain/install.test.ts b/src/porcelain/install.test.ts index b07a0eb..185cd8e 100644 --- a/src/porcelain/install.test.ts +++ b/src/porcelain/install.test.ts @@ -1,4 +1,4 @@ -import { assert, assertArrayIncludes } from "deno/testing/asserts.ts" +import { assert, assertArrayIncludes } from "deno/assert/mod.ts" import { useTestConfig } from "../hooks/useTestConfig.ts" import type { Resolution } from "../plumbing/resolve.ts" import install, { ConsoleLogger } from "./install.ts" @@ -7,27 +7,27 @@ import type { Package } from "../types.ts" Deno.test("porcelain.install.1", async () => { useTestConfig() - const installations = await install("tea.xyz/brewkit") + const installations = await install("darwinsys.com/file") const projects = new Set(installations.map(x => x.pkg.project)) - assert(projects.has("tea.xyz/brewkit")) + assert(projects.has("darwinsys.com/file")) }) Deno.test("porcelain.install.2", async () => { useTestConfig() - await install("tea.xyz/brewkit^0.32") + await install("darwinsys.com/file^5") }) Deno.test("porcelain.install.3", async () => { useTestConfig() - const installations = await install(["tea.xyz/brewkit@0.31", "zlib.net"]) + const installations = await install(["darwinsys.com/file@5.45", "zlib.net"]) const projects = new Set(installations.map(x => x.pkg.project)) - assert(projects.has("tea.xyz/brewkit")) + assert(projects.has("darwinsys.com/file")) assert(projects.has("zlib.net")) }) Deno.test("porcelain.install.4", async () => { useTestConfig() - await install([{ project: 'tea.xyz/brewkit', constraint: new semver.Range("^0.31") }]) + await install([{ project: 'darwinsys.com/file', constraint: new semver.Range("^5.45") }]) }) Deno.test("porcelain.install.resolved", async () => { @@ -39,8 +39,8 @@ Deno.test("porcelain.install.resolved", async () => { resolved: (r: Resolution) => resolution = r } - await install("tea.xyz/brewkit^0.32", logger) + await install("curl.se", logger) const resolvedProjects = resolution.pkgs.map((p: Package) => p.project) - assertArrayIncludes(resolvedProjects, [ "deno.land", "gnu.org/bash", "tea.xyz", "tea.xyz/brewkit"]) + assertArrayIncludes(resolvedProjects, [ "curl.se/ca-certs", "openssl.org", "curl.se"]) }) diff --git a/src/porcelain/run.test.ts b/src/porcelain/run.test.ts index 7c634e6..e1d4501 100644 --- a/src/porcelain/run.test.ts +++ b/src/porcelain/run.test.ts @@ -1,4 +1,4 @@ -import { assertEquals, assertMatch, assertRejects } from "deno/testing/asserts.ts" +import { assertEquals, assertMatch, assertRejects } from "deno/assert/mod.ts" import { useTestConfig } from "../hooks/useTestConfig.ts" import run from "./run.ts" diff --git a/src/porcelain/run.ts b/src/porcelain/run.ts index 2f239a9..28a594d 100644 --- a/src/porcelain/run.ts +++ b/src/porcelain/run.ts @@ -3,7 +3,7 @@ import useShellEnv from '../hooks/useShellEnv.ts' import usePantry from '../hooks/usePantry.ts' import hydrate from "../plumbing/hydrate.ts" import resolve from "../plumbing/resolve.ts" -import { TeaError } from "../utils/error.ts" +import { PkgxError } from "../utils/error.ts" import { spawn } from "node:child_process" import useSync from "../hooks/useSync.ts" import which from "../plumbing/which.ts" @@ -128,7 +128,7 @@ async function setup(cmd: string, env: Record, logge type RunErrorCode = 'ENOENT' | 'EUSAGE' | 'EIO' -export class RunError extends TeaError { +export class RunError extends PkgxError { code: RunErrorCode constructor(code: RunErrorCode, message: string) { diff --git a/src/utils/Path.test.ts b/src/utils/Path.test.ts index 673ec57..2302184 100644 --- a/src/utils/Path.test.ts +++ b/src/utils/Path.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals, assertFalse, assertThrows } from "deno/testing/asserts.ts" +import { assert, assertEquals, assertFalse, assertThrows } from "deno/assert/mod.ts" import Path from "./Path.ts" Deno.test("test Path", async test => { @@ -6,7 +6,7 @@ Deno.test("test Path", async test => { assertEquals(new Path("/a/b/c").components(), ["", "a", "b", "c"]) assertEquals(new Path("/a/b/c").split(), [new Path("/a/b"), "c"]) - const tmp = Path.mktemp({prefix: "tea-"}) + const tmp = Path.mktemp({prefix: "pkgx-"}) assert(tmp.isEmpty()) const child = tmp.join("a/b/c") @@ -28,7 +28,7 @@ Deno.test("test Path", async test => { }) await test.step("write and read", async () => { - const tmp = Path.mktemp({prefix: "tea-"}) + const tmp = Path.mktemp({prefix: "pkgx-"}) const data = tmp.join("test.dat") data.write({text: "hello\nworld"}) @@ -44,7 +44,7 @@ Deno.test("test Path", async test => { }) await test.step("test walk", async () => { - const tmp = Path.mktemp({prefix: "tea-"}) + const tmp = Path.mktemp({prefix: "pkgx-"}) const a = tmp.join("a").mkdir() a.join("a1").touch() @@ -81,7 +81,7 @@ Deno.test("test Path", async test => { }) await test.step("test symlink created", () => { - const tmp = Path.mktemp({prefix: "tea-"}).join("foo").mkdir() + const tmp = Path.mktemp({prefix: "pkgx-"}).join("foo").mkdir() const a = tmp.join("a").touch() const b = tmp.join("b") b.ln('s', { target: a }) @@ -114,7 +114,7 @@ Deno.test("Path.join()", () => { }) Deno.test("Path.isExecutableFile()", () => { - const tmp = Path.mktemp({prefix: "tea-"}).mkdir() + const tmp = Path.mktemp({prefix: "pkgx-"}).mkdir() const executable = tmp.join("executable").touch() executable.chmod(0o755) const notExecutable = tmp.join("not-executable").touch() @@ -129,7 +129,7 @@ Deno.test("Path.extname()", () => { }) Deno.test("Path.mv()", () => { - const tmp = Path.mktemp({prefix: "tea-"}) + const tmp = Path.mktemp({prefix: "pkgx-"}) const a = tmp.join("a").touch() const b = tmp.join("b") @@ -150,7 +150,7 @@ Deno.test("Path.mv()", () => { }) Deno.test("Path.cp()", () => { - const tmp = Path.mktemp({prefix: "tea-"}).mkdir() + const tmp = Path.mktemp({prefix: "pkgx-"}).mkdir() const a = tmp.join("a").touch() const b = tmp.join("b").mkdir() @@ -167,7 +167,7 @@ Deno.test("Path.relative()", () => { }) Deno.test("Path.realpath()", () => { - const tmp = Path.mktemp({prefix: "tea-"}).mkdir() + const tmp = Path.mktemp({prefix: "pkgx-"}).mkdir() const a = tmp.join("a").touch() const b = tmp.join("b").ln('s', { target: a }) @@ -175,8 +175,8 @@ Deno.test("Path.realpath()", () => { }) Deno.test("Path.prettyLocalString()", () => { - const path = Path.home().join(".config/tea/config.toml") - assertEquals(path.prettyLocalString(), "~/.config/tea/config.toml") + const path = Path.home().join(".config/pkgx/config.toml") + assertEquals(path.prettyLocalString(), "~/.config/pkgx/config.toml") assertEquals(new Path("/a/b").prettyLocalString(), "/a/b") }) @@ -190,7 +190,7 @@ Deno.test("Path.chuzzle()", () => { }) Deno.test("Path.ls()", async () => { - const tmp = Path.mktemp({prefix: "tea-"}).mkdir() + const tmp = Path.mktemp({prefix: "pkgx-"}).mkdir() tmp.join("a").touch() tmp.join("b").touch() tmp.join("c").mkdir() diff --git a/src/utils/error.test.ts b/src/utils/error.test.ts index cf71e9a..b3fac02 100644 --- a/src/utils/error.test.ts +++ b/src/utils/error.test.ts @@ -1,4 +1,4 @@ -import { assertRejects, assertThrows } from "deno/testing/asserts.ts" +import { assertRejects, assertThrows } from "deno/assert/mod.ts" import { panic } from "../utils/error.ts" Deno.test("errors", async test => { diff --git a/src/utils/error.ts b/src/utils/error.ts index 15b5bc4..81030a8 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -18,7 +18,7 @@ Promise.prototype.swallow = function(errorClass?: new (...args: any) => any) { }) } -export class TeaError extends Error { +export class PkgxError extends Error { ctx: any constructor(msg: string, ctx?: any) { super(msg) diff --git a/src/utils/hacks.test.ts b/src/utils/hacks.test.ts deleted file mode 100644 index e76176e..0000000 --- a/src/utils/hacks.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { assertEquals, assertThrows } from "deno/testing/asserts.ts" -import { validatePackageRequirement } from "./hacks.ts" -import host from "./host.ts" - -Deno.test("validatePackageRequirement - valid input", () => { - const result = validatePackageRequirement("tea.xyz/test", "^1.0.0") - assertEquals(result?.project, "tea.xyz/test") - assertEquals(result?.constraint.toString(), "^1") -}) - -Deno.test("validatePackageRequirement - invalid constraint", () => { - assertThrows(() => validatePackageRequirement("tea.xyz/test", "nonsense")) -}) - -Deno.test("validatePackageRequirement - number constraint", () => { - const result = validatePackageRequirement("tea.xyz/test", 1) - assertEquals(result?.constraint.toString(), "^1") -}) - -Deno.test("validatePackageRequirement - hacks", () => { - const result = validatePackageRequirement("tea.xyz/gx/cc", "c99") - assertEquals(result?.constraint.toString(), "~0.1") -}) - -Deno.test({ - name: "validatePackageRequirement - darwin hack", - ignore: Deno.build.os !== "darwin", - fn: () => { - const result = validatePackageRequirement("apple.com/xcode/clt", "*") - assertEquals(result, undefined) -}}) - -Deno.test("validatePackageRequirement - linux hack", () => { - if (host().platform !== "linux") return - - const result = validatePackageRequirement("tea.xyz/gx/make", "*") - - assertEquals(result?.project, "gnu.org/make") - assertEquals(result?.constraint.toString(), "*") -}) diff --git a/src/utils/hacks.ts b/src/utils/hacks.ts deleted file mode 100644 index fbf4ce1..0000000 --- a/src/utils/hacks.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { PackageRequirement } from "../types.ts" -import { is_what } from "../deps.ts" -const { isString, isNumber } = is_what -import * as semver from "./semver.ts" -import host from "./host.ts" -import { TeaError } from "../../mod.ts" - -export function validatePackageRequirement(project: string, constraint: unknown): PackageRequirement | undefined -{ - if (host().platform == 'darwin' && (project == "apple.com/xcode/clt" || project == "tea.xyz/gx/make")) { - // Apple will error out and prompt the user to install when the tool is used - return // compact this dep away - } - if (host().platform == 'linux' && project == "tea.xyz/gx/make") { - project = "gnu.org/make" - constraint = '*' - } - - if (constraint == 'c99' && project == 'tea.xyz/gx/cc') { - constraint = '^0.1' - } - - if (isNumber(constraint)) { - constraint = `^${constraint}` - } else if (!isString(constraint)) { - throw new Error(`invalid constraint for ${project}: ${constraint}`) - } - - constraint = semver.Range.parse(constraint as string) - if (!constraint) { - throw new TeaError("invalid constraint for " + project + ": " + constraint) - } - - return { - project, - constraint: constraint as semver.Range - } -} diff --git a/src/utils/host.test.ts b/src/utils/host.test.ts index f590648..365ec06 100644 --- a/src/utils/host.test.ts +++ b/src/utils/host.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals, assertThrows, fail } from "deno/testing/asserts.ts" +import { assert, assertEquals, assertThrows, fail } from "deno/assert/mod.ts" import host, { _internals, SupportedPlatform } from "./host.ts" import { stub } from "deno/testing/mock.ts" diff --git a/src/utils/misc.test.ts b/src/utils/misc.test.ts index 577d092..f9824c6 100644 --- a/src/utils/misc.test.ts +++ b/src/utils/misc.test.ts @@ -34,21 +34,14 @@ Deno.test("flatmap", () => { }) Deno.test("async flatmap", async () => { - const producer = (value?: T, err?: Error): Promise => { - if (err) { - return Promise.reject(err) - } - return Promise.resolve(value) - } - const add = (n: number) => Promise.resolve(n + 1) - assertEquals(await flatmap(producer(1), add), 2) - assertEquals(await flatmap(producer(undefined), add), undefined) - assertEquals(await flatmap(producer(1), (_n) => undefined), undefined) + assertEquals(await flatmap(Promise.resolve(1), add), 2) + assertEquals(await flatmap(Promise.resolve(undefined), add), undefined) + assertEquals(await flatmap(Promise.resolve(1), (_n) => undefined), undefined) - assertEquals(await flatmap(producer(1, Error()), add, {rescue: true}), undefined) - await assertRejects(() => flatmap(producer(1, Error()), add, undefined)) + assertEquals(await flatmap(Promise.resolve(1), () => Promise.reject(new Error()), {rescue: true}), undefined) + await assertRejects(() => flatmap(Promise.resolve(1), () => Promise.reject("new Error()")) ?? Promise.resolve()) }) Deno.test("chuzzle", () => { diff --git a/src/utils/misc.ts b/src/utils/misc.ts index b32a4a0..a51dd85 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -65,19 +65,21 @@ Array.prototype.compact = function(body?: (t: T) => S | Falsy, opts?: { re return rv } -export function flatmap(t: T | Falsy, body: (t: T) => S | Falsy, opts?: {rescue?: boolean}): S | undefined; -export function flatmap(t: Promise, body: (t: T) => Promise, opts?: {rescue?: boolean}): Promise; -export function flatmap(t: Promise | (T | Falsy), body: (t: T) => (S | Falsy) | Promise, opts?: {rescue?: boolean}): Promise | (S | undefined) { +export function flatmap(t: T | Falsy, body: (t: T) => S | Falsy, opts?: {rescue: boolean}): S | undefined; +export function flatmap(t: Promise, body: (t: T) => Promise, opts?: {rescue: boolean}): Promise; +export function flatmap(t: Promise | (T | Falsy), body: (t: T) => (S | Falsy) | Promise, opts?: {rescue: boolean}): Promise | (S | undefined) { try { if (t instanceof Promise) { const foo = t.then(t => { if (!t) return const s = body(t) as Promise if (!s) return - const bar = s - .then(body => body || undefined) - .catch(err => { if (!opts?.rescue) throw err; else return undefined } ) - return bar + const bar = s.then(body => body || undefined) + if (opts?.rescue) { + return bar.catch(() => { return undefined }) + } else { + return bar + } }) return foo } else { diff --git a/src/utils/pkg.test.ts b/src/utils/pkg.test.ts index 4f25468..d8cb426 100644 --- a/src/utils/pkg.test.ts +++ b/src/utils/pkg.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals, assertFalse, assertThrows } from "deno/testing/asserts.ts" +import { assert, assertEquals, assertFalse, assertThrows } from "deno/assert/mod.ts" import SemVer, { Range } from "./semver.ts" import * as pkg from "./pkg.ts" diff --git a/src/utils/semver.test.ts b/src/utils/semver.test.ts index bfe453c..b15f726 100644 --- a/src/utils/semver.test.ts +++ b/src/utils/semver.test.ts @@ -46,7 +46,7 @@ Deno.test("semver", async test => { await test.step("satisfies", () => { assertEquals(new semver.Range("=3.1.0").max([new SemVer("3.1.0")]), new SemVer("3.1.0")) - // the following two test for https://github.com/teaxyz/lib/pull/36 + // the following two test for https://github.com/pkgxdev/lib/pull/36 assertEquals(new semver.Range("^300").max([new SemVer("3.1.0")]), undefined) assertEquals(new semver.Range("@300").max([new SemVer("3.1.0")]), undefined) }) diff --git a/src/utils/semver.ts b/src/utils/semver.ts index ed6109a..83c9ff5 100644 --- a/src/utils/semver.ts +++ b/src/utils/semver.ts @@ -1,4 +1,4 @@ -//HEY YOU! DO NOT CHANGE THIS TO USE deps.ts since it breaks tea/gui +//HEY YOU! DO NOT CHANGE THIS TO USE deps.ts since it breaks pkgx/gui import { isArray, isString } from "https://deno.land/x/is_what@v4.1.15/src/index.ts" /**