Add licensing and distribution metadata
This commit is contained in:
parent
30ae88f8a3
commit
5fb0c9c57b
11 changed files with 9231 additions and 63 deletions
10
.dockerignore
Normal file
10
.dockerignore
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
.git
|
||||||
|
dist
|
||||||
|
dist-newstyle
|
||||||
|
docs/journal
|
||||||
|
scratchpad
|
||||||
|
tests/kind/.runtime
|
||||||
|
web/dist
|
||||||
|
web/node_modules
|
||||||
|
web/playwright-report
|
||||||
|
web/test-results
|
||||||
|
|
@ -18,7 +18,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY nuthatch.cabal cabal.project ./
|
COPY nuthatch.cabal cabal.project cabal.project.freeze LICENSE ./
|
||||||
RUN cabal update \
|
RUN cabal update \
|
||||||
&& cabal build --only-dependencies all
|
&& cabal build --only-dependencies all
|
||||||
|
|
||||||
|
|
@ -57,6 +57,9 @@ RUN apt-get update \
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY sql ./sql
|
COPY sql ./sql
|
||||||
COPY --from=frontend /web/dist ./web/dist
|
COPY --from=frontend /web/dist ./web/dist
|
||||||
|
COPY LICENSE THIRD_PARTY_NOTICES.txt /usr/share/doc/nuthatch/
|
||||||
|
RUN dpkg-query --show --showformat='${binary:Package}\t${Version}\n' \
|
||||||
|
| sort > /usr/share/doc/nuthatch/debian-packages.txt
|
||||||
USER 10001:10001
|
USER 10001:10001
|
||||||
|
|
||||||
FROM runtime-base AS test-runtime
|
FROM runtime-base AS test-runtime
|
||||||
|
|
|
||||||
26
LICENSE
Normal file
26
LICENSE
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
Copyright 2026 ms2k
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
51
Makefile
51
Makefile
|
|
@ -1,71 +1,76 @@
|
||||||
.PHONY: test check-dependencies check-fixture-lib check-kind-matrix check-storage-foundation check-auth check-app check-app-boundaries check-streaming check-idempotency check-recovery check-model-correspondence check-protocol check-product check-browser check-runtime check-v1 check-specs check-web-contract generate-web-contract app-up app-down test-frontend check-frontend build-frontend
|
.PHONY: test check-dependencies check-fixture-lib check-kind-matrix check-storage-foundation check-auth check-app check-app-boundaries check-streaming check-idempotency check-recovery check-model-correspondence check-protocol check-product check-browser check-runtime check-v1 check-specs check-web-contract generate-web-contract third-party-notices app-up app-down test-frontend check-frontend build-frontend
|
||||||
|
|
||||||
test:
|
test:
|
||||||
./tests/run-host-tests.sh
|
bash ./tests/run-host-tests.sh
|
||||||
|
|
||||||
check-dependencies:
|
check-dependencies:
|
||||||
./tests/kind/run.sh
|
bash ./tests/kind/run.sh
|
||||||
|
|
||||||
check-fixture-lib:
|
check-fixture-lib:
|
||||||
./tests/kind/lib-test.sh
|
bash ./tests/kind/lib-test.sh
|
||||||
|
|
||||||
check-kind-matrix:
|
check-kind-matrix:
|
||||||
./tests/kind/matrix-run.sh
|
bash ./tests/kind/matrix-run.sh
|
||||||
|
|
||||||
check-storage-foundation:
|
check-storage-foundation:
|
||||||
./tests/kind/storage-foundation-run.sh
|
bash ./tests/kind/storage-foundation-run.sh
|
||||||
|
|
||||||
check-auth:
|
check-auth:
|
||||||
./tests/kind/auth-run.sh
|
bash ./tests/kind/auth-run.sh
|
||||||
|
|
||||||
check-app:
|
check-app:
|
||||||
./tests/kind/auth-run.sh
|
bash ./tests/kind/auth-run.sh
|
||||||
|
|
||||||
check-app-boundaries:
|
check-app-boundaries:
|
||||||
./tests/application-boundaries.sh
|
bash ./tests/application-boundaries.sh
|
||||||
|
|
||||||
check-streaming:
|
check-streaming:
|
||||||
./tests/kind/streaming-run.sh
|
bash ./tests/kind/streaming-run.sh
|
||||||
|
|
||||||
check-idempotency:
|
check-idempotency:
|
||||||
./tests/kind/idempotency-run.sh
|
bash ./tests/kind/idempotency-run.sh
|
||||||
|
|
||||||
check-recovery:
|
check-recovery:
|
||||||
./tests/kind/recovery-run.sh
|
bash ./tests/kind/recovery-run.sh
|
||||||
|
|
||||||
check-model-correspondence:
|
check-model-correspondence:
|
||||||
./tests/model-correspondence/run.sh
|
bash ./tests/model-correspondence/run.sh
|
||||||
|
|
||||||
check-protocol: check-model-correspondence
|
check-protocol: check-model-correspondence
|
||||||
./tests/kind/protocol-conformance-run.sh
|
bash ./tests/kind/protocol-conformance-run.sh
|
||||||
|
|
||||||
check-product: test-frontend check-frontend build-frontend check-web-contract
|
check-product: test-frontend check-frontend build-frontend check-web-contract
|
||||||
./tests/kind/product-run.sh
|
bash ./tests/kind/product-run.sh
|
||||||
|
|
||||||
check-browser: test-frontend check-frontend build-frontend
|
check-browser: test-frontend check-frontend build-frontend
|
||||||
./tests/kind/browser-run.sh
|
bash ./tests/kind/browser-run.sh
|
||||||
|
|
||||||
check-web-contract:
|
check-web-contract:
|
||||||
./tests/web-contract.sh
|
bash ./tests/web-contract.sh
|
||||||
|
|
||||||
generate-web-contract:
|
generate-web-contract:
|
||||||
cabal run nuthatch-generate-web-contract --offline -- web/src/generated/contract.ts web/test/fixtures/contract.json
|
cabal run nuthatch-generate-web-contract --offline -- web/src/generated/contract.ts web/test/fixtures/contract.json
|
||||||
|
|
||||||
|
third-party-notices:
|
||||||
|
cabal build exe:nuthatch --dry-run --offline --builddir=dist-newstyle/third-party
|
||||||
|
npm --prefix web ci
|
||||||
|
node scripts/generate-third-party-notices.mjs dist-newstyle/third-party/cache/plan.json web/package-lock.json web/node_modules THIRD_PARTY_NOTICES.txt
|
||||||
|
|
||||||
check-runtime: test check-recovery
|
check-runtime: test check-recovery
|
||||||
./tests/runtime-static.sh
|
bash ./tests/runtime-static.sh
|
||||||
./tests/kind/runtime-resilience-run.sh
|
bash ./tests/kind/runtime-resilience-run.sh
|
||||||
|
|
||||||
check-v1:
|
check-v1:
|
||||||
./tests/check-v1.sh
|
bash ./tests/check-v1.sh
|
||||||
|
|
||||||
check-specs:
|
check-specs:
|
||||||
./tests/spec-contracts.sh
|
bash ./tests/spec-contracts.sh
|
||||||
|
|
||||||
app-up:
|
app-up:
|
||||||
NUTHATCH_APP_INTERACTIVE=1 ./tests/kind/auth-run.sh
|
NUTHATCH_APP_INTERACTIVE=1 bash ./tests/kind/auth-run.sh
|
||||||
|
|
||||||
app-down:
|
app-down:
|
||||||
./tests/kind/destroy.sh
|
bash ./tests/kind/destroy.sh
|
||||||
|
|
||||||
test-frontend:
|
test-frontend:
|
||||||
npm --prefix web test
|
npm --prefix web test
|
||||||
|
|
|
||||||
32
README.md
Normal file
32
README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Nuthatch
|
||||||
|
|
||||||
|
Small web UI for personal and shared S3-compatible storage.
|
||||||
|
|
||||||
|
Made for a personal homelab. Shared as-is.
|
||||||
|
|
||||||
|
## Build and test
|
||||||
|
|
||||||
|
Install the [required tools](docs/spec/testing.md), then run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cabal build exe:nuthatch
|
||||||
|
make test
|
||||||
|
npm --prefix web ci
|
||||||
|
make test-frontend check-frontend build-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
Full check, including local kind clusters:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make check-v1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [Behavior and design](docs/spec/nuthatch.md)
|
||||||
|
- [Configuration and interfaces](docs/spec/contracts.md)
|
||||||
|
- [Mutation protocol](docs/protocol/Nuthatch.tla)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[BSD 3-Clause](LICENSE). See [`THIRD_PARTY_NOTICES.txt`](THIRD_PARTY_NOTICES.txt) for bundled dependencies.
|
||||||
8474
THIRD_PARTY_NOTICES.txt
Normal file
8474
THIRD_PARTY_NOTICES.txt
Normal file
File diff suppressed because it is too large
Load diff
308
cabal.project.freeze
Normal file
308
cabal.project.freeze
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
active-repositories: hackage.haskell.org:merge
|
||||||
|
constraints: any.Cabal ==3.12.1.0,
|
||||||
|
any.Cabal-syntax ==3.12.1.0,
|
||||||
|
any.HTTP ==4000.5.0,
|
||||||
|
HTTP -conduit10 -warn-as-error -warp-tests,
|
||||||
|
any.HUnit ==1.6.2.0,
|
||||||
|
any.OneTuple ==0.4.3,
|
||||||
|
OneTuple +base-ge-4-15 +base-ge-4-16,
|
||||||
|
any.Only ==0.1,
|
||||||
|
any.QuickCheck ==2.16.0.0,
|
||||||
|
QuickCheck -old-random +templatehaskell,
|
||||||
|
any.StateVar ==1.2.2,
|
||||||
|
any.adjunctions ==4.4.4,
|
||||||
|
any.aeson ==2.2.5.0,
|
||||||
|
aeson +ordered-keymap,
|
||||||
|
any.ansi-terminal ==1.1.5,
|
||||||
|
ansi-terminal -example,
|
||||||
|
any.ansi-terminal-types ==1.1.3,
|
||||||
|
any.appar ==0.1.8,
|
||||||
|
any.array ==0.5.8.0,
|
||||||
|
any.assoc ==1.1.1,
|
||||||
|
assoc -tagged,
|
||||||
|
any.async ==2.2.6,
|
||||||
|
async -bench -debug-auto-label,
|
||||||
|
any.attoparsec ==0.14.4,
|
||||||
|
attoparsec -developer,
|
||||||
|
any.attoparsec-aeson ==2.2.2.0,
|
||||||
|
any.auto-update ==0.2.6,
|
||||||
|
any.aws ==0.25.2,
|
||||||
|
aws -examples,
|
||||||
|
any.barbies ==2.1.1.0,
|
||||||
|
any.base ==4.20.2.0,
|
||||||
|
any.base-compat ==0.15.0,
|
||||||
|
any.base-orphans ==0.9.4,
|
||||||
|
any.base16 ==1.0,
|
||||||
|
any.base16-bytestring ==1.0.2.0,
|
||||||
|
any.base64 ==1.0,
|
||||||
|
any.base64-bytestring ==1.2.1.0,
|
||||||
|
any.basement ==0.0.16,
|
||||||
|
any.bifunctors ==5.6.3,
|
||||||
|
bifunctors +tagged,
|
||||||
|
any.binary ==0.8.9.3,
|
||||||
|
any.bitvec ==1.1.6.0,
|
||||||
|
bitvec +simd,
|
||||||
|
any.blaze-builder ==0.4.4.1,
|
||||||
|
any.blaze-html ==0.9.2.0,
|
||||||
|
any.blaze-markup ==0.8.3.0,
|
||||||
|
any.boring ==0.2.2.1,
|
||||||
|
boring +tagged,
|
||||||
|
any.bsb-http-chunked ==0.0.0.4,
|
||||||
|
any.byteable ==0.1.1,
|
||||||
|
any.byteorder ==1.0.4,
|
||||||
|
any.bytestring ==0.12.2.0,
|
||||||
|
any.bytestring-lexing ==0.5.0.15,
|
||||||
|
any.cabal-doctest ==1.0.12,
|
||||||
|
any.call-stack ==0.4.0,
|
||||||
|
any.case-insensitive ==1.2.1.0,
|
||||||
|
any.cborg ==0.2.10.0,
|
||||||
|
cborg +optimize-gmp,
|
||||||
|
any.cereal ==0.5.8.3,
|
||||||
|
cereal -bytestring-builder,
|
||||||
|
any.character-ps ==0.1,
|
||||||
|
any.clock ==0.8.4,
|
||||||
|
clock -llvm,
|
||||||
|
any.colour ==2.3.7,
|
||||||
|
any.comonad ==5.0.10,
|
||||||
|
comonad +containers +distributive +indexed-traversable,
|
||||||
|
any.concise ==0.1.0.1,
|
||||||
|
any.concurrent-output ==1.10.21,
|
||||||
|
any.conduit ==1.3.6.1,
|
||||||
|
any.conduit-extra ==1.3.8,
|
||||||
|
any.constraints ==0.14.4,
|
||||||
|
any.containers ==0.7,
|
||||||
|
any.contravariant ==1.5.6,
|
||||||
|
contravariant +statevar,
|
||||||
|
any.cookie ==0.5.1,
|
||||||
|
any.crypto-token ==0.1.2,
|
||||||
|
any.cryptohash-md5 ==0.11.101.0,
|
||||||
|
any.cryptohash-sha1 ==0.11.101.0,
|
||||||
|
any.crypton ==1.0.6,
|
||||||
|
crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
|
||||||
|
any.crypton-asn1-encoding ==0.10.0,
|
||||||
|
any.crypton-asn1-parse ==0.10.0,
|
||||||
|
any.crypton-asn1-types ==0.4.1,
|
||||||
|
any.crypton-connection ==0.4.5,
|
||||||
|
any.crypton-pem ==0.3.0,
|
||||||
|
any.crypton-socks ==0.6.2,
|
||||||
|
crypton-socks -example +network-3-0-0-0,
|
||||||
|
any.crypton-x509 ==1.8.0,
|
||||||
|
any.crypton-x509-store ==1.8.0,
|
||||||
|
any.crypton-x509-system ==1.8.0,
|
||||||
|
any.crypton-x509-validation ==1.8.0,
|
||||||
|
any.data-default ==0.8.0.2,
|
||||||
|
any.data-default-class ==0.2.0.0,
|
||||||
|
any.data-fix ==0.3.4,
|
||||||
|
any.dec ==0.0.6,
|
||||||
|
any.deepseq ==1.5.0.0,
|
||||||
|
any.directory ==1.3.8.5,
|
||||||
|
any.distributive ==0.6.3,
|
||||||
|
distributive +tagged,
|
||||||
|
any.dlist ==1.0,
|
||||||
|
dlist -werror,
|
||||||
|
any.easy-file ==0.2.5,
|
||||||
|
any.ech-config ==0.0.1,
|
||||||
|
ech-config -devel,
|
||||||
|
any.entropy ==0.4.1.11,
|
||||||
|
entropy -donotgetentropy,
|
||||||
|
any.envparse ==0.6.0,
|
||||||
|
any.erf ==2.0.0.0,
|
||||||
|
any.errors ==2.3.0,
|
||||||
|
any.exceptions ==0.10.9,
|
||||||
|
any.fail ==4.9.0.0,
|
||||||
|
any.fast-logger ==3.2.6,
|
||||||
|
any.file-embed ==0.0.16.0,
|
||||||
|
any.filepath ==1.5.4.0,
|
||||||
|
any.free ==5.2,
|
||||||
|
any.generics-sop ==0.5.1.4,
|
||||||
|
any.ghc-bignum ==1.3,
|
||||||
|
any.ghc-boot-th ==9.10.3,
|
||||||
|
any.ghc-internal ==9.1003.0,
|
||||||
|
any.ghc-prim ==0.12.0,
|
||||||
|
any.half ==0.3.3,
|
||||||
|
any.happy ==2.2,
|
||||||
|
any.happy-lib ==2.2,
|
||||||
|
any.hashable ==1.5.1.0,
|
||||||
|
hashable -arch-native -random-initial-seed,
|
||||||
|
any.haskell-lexer ==1.2.1,
|
||||||
|
any.hedgehog ==1.7,
|
||||||
|
any.hedis ==0.15.2,
|
||||||
|
hedis -dev,
|
||||||
|
any.hpke ==0.0.0,
|
||||||
|
any.hsc2hs ==0.68.10,
|
||||||
|
hsc2hs -in-ghc-tree,
|
||||||
|
any.http-api-data ==0.7,
|
||||||
|
http-api-data -use-text-show,
|
||||||
|
any.http-client ==0.7.19,
|
||||||
|
http-client +network-uri,
|
||||||
|
any.http-client-tls ==0.3.6.4,
|
||||||
|
any.http-conduit ==2.3.9.1,
|
||||||
|
http-conduit +aeson,
|
||||||
|
any.http-date ==0.0.11,
|
||||||
|
any.http-media ==0.8.1.1,
|
||||||
|
any.http-semantics ==0.4.1,
|
||||||
|
any.http-types ==0.12.5,
|
||||||
|
any.http2 ==5.4.3,
|
||||||
|
http2 -devel -h2spec,
|
||||||
|
any.indexed-traversable ==0.1.5,
|
||||||
|
indexed-traversable +base-ge-4-18,
|
||||||
|
any.indexed-traversable-instances ==0.1.2.1,
|
||||||
|
any.integer-conversion ==0.1.1,
|
||||||
|
any.integer-gmp ==1.1,
|
||||||
|
any.integer-logarithms ==1.0.5,
|
||||||
|
integer-logarithms -check-bounds +integer-gmp,
|
||||||
|
any.invariant ==0.6.5,
|
||||||
|
any.iproute ==1.7.15,
|
||||||
|
any.jose ==0.12,
|
||||||
|
jose -demos,
|
||||||
|
any.kan-extensions ==5.2.8,
|
||||||
|
any.lens ==5.3.6,
|
||||||
|
lens -benchmark-uniplate -dump-splices +inlining -j +test-hunit +test-properties +test-templates +trustworthy,
|
||||||
|
any.lifted-async ==0.11.0,
|
||||||
|
any.lifted-base ==0.2.3.12,
|
||||||
|
any.megaparsec ==9.8.1,
|
||||||
|
megaparsec -dev,
|
||||||
|
any.memory ==0.18.0,
|
||||||
|
memory +support_bytestring +support_deepseq,
|
||||||
|
any.mime-types ==0.1.2.2,
|
||||||
|
any.mmorph ==1.2.2,
|
||||||
|
any.modern-uri ==0.3.6.1,
|
||||||
|
modern-uri -dev,
|
||||||
|
any.monad-control ==1.0.3.1,
|
||||||
|
any.monad-time ==0.4.0.0,
|
||||||
|
any.mono-traversable ==1.0.21.0,
|
||||||
|
any.mtl ==2.3.1,
|
||||||
|
any.mtl-compat ==0.2.2,
|
||||||
|
mtl-compat -two-point-one -two-point-two,
|
||||||
|
any.network ==3.2.8.0,
|
||||||
|
network -devel,
|
||||||
|
any.network-bsd ==2.8.1.0,
|
||||||
|
any.network-byte-order ==0.1.8,
|
||||||
|
any.network-control ==0.1.7,
|
||||||
|
any.network-info ==0.2.1,
|
||||||
|
any.network-uri ==2.6.4.2,
|
||||||
|
any.old-locale ==1.0.0.7,
|
||||||
|
any.old-time ==1.1.1.0,
|
||||||
|
any.optparse-applicative ==0.19.0.0,
|
||||||
|
optparse-applicative +process,
|
||||||
|
any.os-string ==2.0.7,
|
||||||
|
any.parallel ==3.3.0.0,
|
||||||
|
any.parsec ==3.1.18.0,
|
||||||
|
any.parser-combinators ==1.3.1,
|
||||||
|
parser-combinators -dev,
|
||||||
|
any.postgresql-libpq ==0.11.0.0,
|
||||||
|
postgresql-libpq -use-pkg-config,
|
||||||
|
any.postgresql-libpq-configure ==0.11,
|
||||||
|
any.postgresql-migration ==0.2.1.8,
|
||||||
|
any.postgresql-simple ==0.7.0.1,
|
||||||
|
any.pretty ==1.1.3.6,
|
||||||
|
any.pretty-show ==1.10,
|
||||||
|
any.prettyprinter ==1.7.2,
|
||||||
|
prettyprinter -buildreadme +text,
|
||||||
|
any.prettyprinter-ansi-terminal ==1.1.4,
|
||||||
|
prettyprinter-ansi-terminal +text,
|
||||||
|
any.primitive ==0.9.1.0,
|
||||||
|
any.process ==1.6.26.1,
|
||||||
|
any.profunctors ==5.6.3,
|
||||||
|
any.psqueues ==0.2.8.3,
|
||||||
|
any.random ==1.3.1,
|
||||||
|
any.recv ==0.1.1,
|
||||||
|
any.reflection ==2.1.9,
|
||||||
|
reflection -slow +template-haskell,
|
||||||
|
any.resource-pool ==0.4.0.0,
|
||||||
|
any.resourcet ==1.3.0,
|
||||||
|
any.retry ==0.9.3.1,
|
||||||
|
retry -lib-werror,
|
||||||
|
any.rts ==1.0.2,
|
||||||
|
any.safe ==0.3.21,
|
||||||
|
any.safe-exceptions ==0.1.7.4,
|
||||||
|
any.scanner ==0.3.1,
|
||||||
|
any.scientific ==0.3.8.1,
|
||||||
|
scientific -integer-simple,
|
||||||
|
any.semialign ==1.4,
|
||||||
|
semialign +semigroupoids,
|
||||||
|
any.semigroupoids ==6.0.2,
|
||||||
|
semigroupoids +comonad +containers +contravariant +tagged +unordered-containers,
|
||||||
|
any.serialise ==0.2.6.1,
|
||||||
|
serialise +newtime15,
|
||||||
|
any.servant ==0.20.3.0,
|
||||||
|
any.servant-server ==0.20.3.0,
|
||||||
|
any.simple-sendfile ==0.2.32,
|
||||||
|
simple-sendfile +allow-bsd -fallback,
|
||||||
|
any.singleton-bool ==0.1.8,
|
||||||
|
any.some ==1.1,
|
||||||
|
some +newtype-unsafe,
|
||||||
|
any.sop-core ==0.5.0.2,
|
||||||
|
any.split ==0.2.5,
|
||||||
|
any.splitmix ==0.1.3.2,
|
||||||
|
splitmix -optimised-mixer,
|
||||||
|
any.stm ==2.5.3.1,
|
||||||
|
any.streaming-commons ==0.2.3.1,
|
||||||
|
streaming-commons -use-bytestring-builder,
|
||||||
|
any.strict ==0.5.1,
|
||||||
|
any.tagged ==0.8.10,
|
||||||
|
tagged +deepseq +template-haskell,
|
||||||
|
any.tasty ==1.5.4,
|
||||||
|
tasty +unix,
|
||||||
|
any.tasty-hedgehog ==1.4.0.2,
|
||||||
|
any.tasty-hunit ==0.10.2,
|
||||||
|
any.template-haskell ==2.22.0.0,
|
||||||
|
any.temporary ==1.3,
|
||||||
|
any.terminal-size ==0.3.4,
|
||||||
|
any.text ==2.1.3,
|
||||||
|
any.text-iso8601 ==0.1.1.1,
|
||||||
|
any.text-short ==0.1.6.1,
|
||||||
|
text-short -asserts,
|
||||||
|
any.th-abstraction ==0.7.2.0,
|
||||||
|
any.th-compat ==0.1.7,
|
||||||
|
any.these ==1.2.1,
|
||||||
|
any.time ==1.12.2,
|
||||||
|
any.time-compat ==1.9.9,
|
||||||
|
any.time-hourglass ==0.3.0,
|
||||||
|
any.time-manager ==0.3.2,
|
||||||
|
any.tls ==2.2.2,
|
||||||
|
tls -devel,
|
||||||
|
any.tls-session-manager ==0.0.9,
|
||||||
|
any.transformers ==0.6.1.1,
|
||||||
|
any.transformers-base ==0.4.6.1,
|
||||||
|
transformers-base +orphaninstances,
|
||||||
|
any.transformers-compat ==0.7.2,
|
||||||
|
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
|
||||||
|
any.typed-process ==0.2.13.0,
|
||||||
|
any.unicode-data ==0.8.0,
|
||||||
|
unicode-data -dev-has-icu,
|
||||||
|
any.unicode-transforms ==0.4.0.1,
|
||||||
|
unicode-transforms -bench-show -dev -has-icu -has-llvm -use-gauge,
|
||||||
|
any.unix ==2.8.7.0,
|
||||||
|
any.unix-compat ==0.7.4.1,
|
||||||
|
any.unix-time ==0.4.17,
|
||||||
|
any.unliftio-core ==0.2.1.0,
|
||||||
|
any.unordered-containers ==0.2.21,
|
||||||
|
unordered-containers -debug,
|
||||||
|
any.utf8-string ==1.0.2,
|
||||||
|
any.uuid ==1.3.16.1,
|
||||||
|
any.uuid-types ==1.0.6.1,
|
||||||
|
any.vault ==0.3.2.0,
|
||||||
|
vault +useghc,
|
||||||
|
any.vector ==0.13.2.0,
|
||||||
|
vector +boundschecks -internalchecks -unsafechecks -wall,
|
||||||
|
any.vector-algorithms ==0.9.1.0,
|
||||||
|
vector-algorithms +bench +boundschecks -internalchecks -llvm -unsafechecks,
|
||||||
|
any.vector-stream ==0.1.0.1,
|
||||||
|
any.wai ==3.2.4,
|
||||||
|
any.wai-app-static ==3.2.1,
|
||||||
|
wai-app-static -print,
|
||||||
|
any.wai-extra ==3.1.18,
|
||||||
|
wai-extra -build-example,
|
||||||
|
any.wai-logger ==2.5.0,
|
||||||
|
any.warp ==3.4.14,
|
||||||
|
warp +allow-sendfilefd +include-warp-version -network-bytestring -warp-debug +x509,
|
||||||
|
any.warp-tls ==3.4.14,
|
||||||
|
any.witherable ==0.5,
|
||||||
|
any.wl-pprint-annotated ==0.1.0.1,
|
||||||
|
any.word8 ==0.1.3,
|
||||||
|
any.xml-conduit ==1.10.1.0,
|
||||||
|
any.xml-types ==0.3.8,
|
||||||
|
any.zlib ==0.7.1.1,
|
||||||
|
zlib -bundled-c-zlib +non-blocking-ffi +pkg-config
|
||||||
|
index-state: hackage.haskell.org 2026-07-17T09:45:02Z
|
||||||
121
nuthatch.cabal
121
nuthatch.cabal
|
|
@ -2,18 +2,69 @@ cabal-version: 3.0
|
||||||
name: nuthatch
|
name: nuthatch
|
||||||
version: 0.1.0.0
|
version: 0.1.0.0
|
||||||
synopsis: User-facing S3 BFF
|
synopsis: User-facing S3 BFF
|
||||||
license: NONE
|
description:
|
||||||
author: Nuthatch
|
A user-facing web application and API for small S3-compatible object stores.
|
||||||
maintainer: nuthatch@localhost
|
Nuthatch gives OIDC-authenticated users identity-scoped personal roots and a
|
||||||
|
shared root while keeping object-store credentials behind the service.
|
||||||
|
license: BSD-3-Clause
|
||||||
|
license-file: LICENSE
|
||||||
|
copyright: 2026 ms2k
|
||||||
|
author: ms2k
|
||||||
|
maintainer: [email protected]
|
||||||
|
homepage: https://code.quorate.dev/ms2k/nuthatch
|
||||||
|
bug-reports: mailto:[email protected]
|
||||||
|
tested-with: GHC == 9.10.3
|
||||||
category: Network
|
category: Network
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
|
extra-doc-files:
|
||||||
|
README.md
|
||||||
extra-source-files:
|
extra-source-files:
|
||||||
|
.dockerignore
|
||||||
|
Dockerfile
|
||||||
|
Makefile
|
||||||
|
THIRD_PARTY_NOTICES.txt
|
||||||
|
cabal.project
|
||||||
|
cabal.project.freeze
|
||||||
|
deploy/kubernetes/*.yaml.in
|
||||||
|
docs/protocol/*.cfg
|
||||||
|
docs/protocol/*.tla
|
||||||
|
docs/spec/*.md
|
||||||
sql/001_initial_schema.sql
|
sql/001_initial_schema.sql
|
||||||
web/src/generated/contract.ts
|
scripts/*.mjs
|
||||||
web/src/transport.ts
|
tests/*.sh
|
||||||
web/test/contract-integration.mjs
|
tests/kind/*.env
|
||||||
web/test/fixtures/contract.json
|
tests/kind/*.sh
|
||||||
web/test/fixtures/upload-content-sha256.json
|
tests/kind/*.yaml
|
||||||
|
tests/kind/*.yaml.in
|
||||||
|
tests/kind/fixture/*.sh
|
||||||
|
tests/kind/provider/nerdctl
|
||||||
|
tests/model-correspondence/*.cfg
|
||||||
|
tests/model-correspondence/*.env
|
||||||
|
tests/model-correspondence/*.json
|
||||||
|
tests/model-correspondence/*.mjs
|
||||||
|
tests/model-correspondence/*.sh
|
||||||
|
web/.node-version
|
||||||
|
web/index.html
|
||||||
|
web/package-lock.json
|
||||||
|
web/package.json
|
||||||
|
web/playwright.config.ts
|
||||||
|
web/src/*.css
|
||||||
|
web/src/*.svelte
|
||||||
|
web/src/*.ts
|
||||||
|
web/src/components/*.svelte
|
||||||
|
web/src/generated/*.ts
|
||||||
|
web/src/workflows/*.ts
|
||||||
|
web/svelte.config.js
|
||||||
|
web/test/*.mjs
|
||||||
|
web/test/browser/*.ts
|
||||||
|
web/test/components/*.ts
|
||||||
|
web/test/fixtures/*.json
|
||||||
|
web/tsconfig.json
|
||||||
|
web/vite.config.ts
|
||||||
|
|
||||||
|
source-repository head
|
||||||
|
type: git
|
||||||
|
location: https://code.quorate.dev/ms2k/nuthatch
|
||||||
|
|
||||||
common shared
|
common shared
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
@ -100,7 +151,7 @@ common shared
|
||||||
, warp-tls >= 3.4 && < 3.5
|
, warp-tls >= 3.4 && < 3.5
|
||||||
, xml-conduit >= 1.9 && < 1.11
|
, xml-conduit >= 1.9 && < 1.11
|
||||||
|
|
||||||
library
|
library nuthatch-internal
|
||||||
import: shared
|
import: shared
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
|
|
@ -137,7 +188,7 @@ executable nuthatch
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -149,7 +200,7 @@ executable nuthatch-generate-web-contract
|
||||||
main-is: GenerateContract.hs
|
main-is: GenerateContract.hs
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
|
|
||||||
executable nuthatch-web-contract-integration
|
executable nuthatch-web-contract-integration
|
||||||
|
|
@ -157,10 +208,10 @@ executable nuthatch-web-contract-integration
|
||||||
main-is: WebContractIntegration.hs
|
main-is: WebContractIntegration.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, text
|
, text
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -170,7 +221,7 @@ executable nuthatch-storage-integration
|
||||||
main-is: Integration.hs
|
main-is: Integration.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, async
|
, async
|
||||||
, bytestring
|
, bytestring
|
||||||
|
|
@ -185,7 +236,7 @@ executable nuthatch-recovery-probe
|
||||||
main-is: RecoveryPhase.hs
|
main-is: RecoveryPhase.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, text
|
, text
|
||||||
|
|
@ -201,7 +252,7 @@ executable nuthatch-protocol-conformance
|
||||||
other-modules:
|
other-modules:
|
||||||
Nuthatch.ModelEvidence
|
Nuthatch.ModelEvidence
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, crypton
|
, crypton
|
||||||
|
|
@ -220,7 +271,7 @@ executable nuthatch-model-evidence
|
||||||
other-modules:
|
other-modules:
|
||||||
Nuthatch.ModelEvidence
|
Nuthatch.ModelEvidence
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, base
|
, base
|
||||||
, text
|
, text
|
||||||
|
|
@ -230,7 +281,7 @@ executable nuthatch-runtime-probe
|
||||||
main-is: RuntimeProbe.hs
|
main-is: RuntimeProbe.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -248,13 +299,13 @@ executable nuthatch-auth-probe
|
||||||
Nuthatch.Probe.Oidc
|
Nuthatch.Probe.Oidc
|
||||||
Nuthatch.Probe.ProductClient
|
Nuthatch.Probe.ProductClient
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, crypton
|
, crypton
|
||||||
, memory
|
, memory
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -271,13 +322,13 @@ executable nuthatch-product-probe
|
||||||
Nuthatch.Probe.Oidc
|
Nuthatch.Probe.Oidc
|
||||||
Nuthatch.Probe.ProductClient
|
Nuthatch.Probe.ProductClient
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, crypton
|
, crypton
|
||||||
, memory
|
, memory
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -295,14 +346,14 @@ executable nuthatch-idempotency-probe
|
||||||
Nuthatch.Probe.Oidc
|
Nuthatch.Probe.Oidc
|
||||||
Nuthatch.Probe.ProductClient
|
Nuthatch.Probe.ProductClient
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, async
|
, async
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, crypton
|
, crypton
|
||||||
, memory
|
, memory
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -320,7 +371,7 @@ executable nuthatch-streaming-probe
|
||||||
Nuthatch.Probe.Oidc
|
Nuthatch.Probe.Oidc
|
||||||
Nuthatch.Probe.ProductClient
|
Nuthatch.Probe.ProductClient
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, async
|
, async
|
||||||
, base
|
, base
|
||||||
|
|
@ -328,7 +379,7 @@ executable nuthatch-streaming-probe
|
||||||
, crypton
|
, crypton
|
||||||
, directory
|
, directory
|
||||||
, memory
|
, memory
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -345,13 +396,13 @@ executable nuthatch-runtime-http-probe
|
||||||
Nuthatch.Probe.Oidc
|
Nuthatch.Probe.Oidc
|
||||||
Nuthatch.Probe.ProductClient
|
Nuthatch.Probe.ProductClient
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, crypton
|
, crypton
|
||||||
, memory
|
, memory
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-threaded
|
-threaded
|
||||||
|
|
@ -365,7 +416,7 @@ executable nuthatch-runtime-audit-probe
|
||||||
other-modules:
|
other-modules:
|
||||||
Nuthatch.Probe.Assert
|
Nuthatch.Probe.Assert
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
|
|
@ -379,13 +430,13 @@ executable nuthatch-fixture-probe
|
||||||
Nuthatch.Probe.Assert
|
Nuthatch.Probe.Assert
|
||||||
Nuthatch.Probe.Http
|
Nuthatch.Probe.Http
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, crypton
|
, crypton
|
||||||
, memory
|
, memory
|
||||||
, postgresql-simple
|
, postgresql-simple
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
, text
|
, text
|
||||||
|
|
||||||
|
|
@ -398,12 +449,12 @@ executable nuthatch-dependency-probe
|
||||||
Nuthatch.Probe.Curl
|
Nuthatch.Probe.Curl
|
||||||
Nuthatch.Probe.Http
|
Nuthatch.Probe.Http
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, hedis
|
, hedis
|
||||||
, postgresql-simple
|
, postgresql-simple
|
||||||
, process
|
, process >= 1.6 && < 1.7
|
||||||
, temporary
|
, temporary
|
||||||
|
|
||||||
executable nuthatch-browser-session-fixture
|
executable nuthatch-browser-session-fixture
|
||||||
|
|
@ -411,7 +462,7 @@ executable nuthatch-browser-session-fixture
|
||||||
main-is: BrowserSessionFixture.hs
|
main-is: BrowserSessionFixture.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, aeson
|
, aeson
|
||||||
, base
|
, base
|
||||||
, text
|
, text
|
||||||
|
|
@ -436,7 +487,7 @@ test-suite nuthatch-unit
|
||||||
Nuthatch.Test.Streaming
|
Nuthatch.Test.Streaming
|
||||||
Nuthatch.Test.UploadIdentityFixture
|
Nuthatch.Test.UploadIdentityFixture
|
||||||
build-depends:
|
build-depends:
|
||||||
, nuthatch
|
, nuthatch:nuthatch-internal
|
||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, hedgehog >= 1.7 && < 1.8
|
, hedgehog >= 1.7 && < 1.8
|
||||||
|
|
|
||||||
252
scripts/generate-third-party-notices.mjs
Normal file
252
scripts/generate-third-party-notices.mjs
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
import { execFileSync } from 'node:child_process';
|
||||||
|
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
||||||
|
import { basename, dirname, join, resolve } from 'node:path';
|
||||||
|
|
||||||
|
const [planPath, lockPath, nodeModulesPath, outputPath] = process.argv.slice(2);
|
||||||
|
if ([planPath, lockPath, nodeModulesPath, outputPath].some((value) => value === undefined)) {
|
||||||
|
throw new Error('usage: generate-third-party-notices.mjs PLAN LOCK NODE_MODULES OUTPUT');
|
||||||
|
}
|
||||||
|
|
||||||
|
const plan = readJson(planPath);
|
||||||
|
const lock = readJson(lockPath);
|
||||||
|
const cabalCache = fieldFromCommand('cabal', ['path'], 'cache-home');
|
||||||
|
const ghcLibDir = run('ghc', ['--print-libdir']).trim();
|
||||||
|
const ghcRoot = resolve(ghcLibDir, '../../..');
|
||||||
|
const ghcDocParent = join(ghcRoot, 'share', 'doc');
|
||||||
|
const ghcDocRootName = readdirSync(ghcDocParent).find((name) => name.endsWith(`-ghc-${compilerVersion(plan)}`));
|
||||||
|
if (ghcDocRootName === undefined) throw new Error('GHC package license directory not found');
|
||||||
|
const ghcDocRoot = join(ghcDocParent, ghcDocRootName);
|
||||||
|
|
||||||
|
const haskellPackages = runtimePackages(plan['install-plan'])
|
||||||
|
.map((pkg) => haskellNotice(pkg, cabalCache, ghcDocRoot, compilerVersion(plan)))
|
||||||
|
.sort(comparePackage);
|
||||||
|
const npmPackages = npmProductionPackages(lock)
|
||||||
|
.map((pkg) => npmNotice(pkg, nodeModulesPath))
|
||||||
|
.sort(comparePackage);
|
||||||
|
validateLicenses([...haskellPackages, ...npmPackages]);
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
'NUTHATCH THIRD-PARTY NOTICES',
|
||||||
|
'',
|
||||||
|
'This file is generated by scripts/generate-third-party-notices.mjs.',
|
||||||
|
'It conservatively covers the Haskell dependency plan selected for exe:nuthatch',
|
||||||
|
'and every non-development package in the locked browser dependency closure.',
|
||||||
|
'',
|
||||||
|
'The runtime image separately records its installed Debian packages in',
|
||||||
|
'/usr/share/doc/nuthatch/debian-packages.txt. Their copyright and license',
|
||||||
|
'materials remain in the image under /usr/share/doc.',
|
||||||
|
'',
|
||||||
|
'Container images used only by the test fixture are separate programs and are',
|
||||||
|
'not redistributed as part of Nuthatch. Their licenses remain their own.',
|
||||||
|
'',
|
||||||
|
inventory('HASKELL INVENTORY', haskellPackages),
|
||||||
|
inventory('BROWSER PRODUCTION DEPENDENCY INVENTORY', npmPackages),
|
||||||
|
'LICENSE AND NOTICE TEXTS',
|
||||||
|
...haskellPackages.flatMap(renderNotice),
|
||||||
|
...npmPackages.flatMap(renderNotice)
|
||||||
|
];
|
||||||
|
|
||||||
|
writeFileSync(outputPath, `${sections.join('\n').replace(/\n+$/, '')}\n`);
|
||||||
|
|
||||||
|
function readJson(path) {
|
||||||
|
return JSON.parse(readFileSync(path, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command, args) {
|
||||||
|
return execFileSync(command, args, { encoding: 'utf8' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function fieldFromCommand(command, args, field) {
|
||||||
|
const prefix = `${field}:`;
|
||||||
|
const line = run(command, args).split('\n').find((candidate) => candidate.startsWith(prefix));
|
||||||
|
if (line === undefined) throw new Error(`${field} not reported by ${command}`);
|
||||||
|
return line.slice(prefix.length).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function compilerVersion(planValue) {
|
||||||
|
const match = /^ghc-(.+)$/.exec(planValue['compiler-id']);
|
||||||
|
if (match === null) throw new Error(`unsupported compiler id ${planValue['compiler-id']}`);
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniquePackages(packages) {
|
||||||
|
const unique = new Map();
|
||||||
|
for (const pkg of packages) {
|
||||||
|
const key = `${pkg['pkg-name']}\u0000${pkg['pkg-version']}`;
|
||||||
|
const existing = unique.get(key);
|
||||||
|
if (existing !== undefined && existing.type !== pkg.type) {
|
||||||
|
throw new Error(`inconsistent package source for ${pkg['pkg-name']}-${pkg['pkg-version']}`);
|
||||||
|
}
|
||||||
|
unique.set(key, pkg);
|
||||||
|
}
|
||||||
|
return [...unique.values()];
|
||||||
|
}
|
||||||
|
|
||||||
|
function runtimePackages(packages) {
|
||||||
|
const byId = new Map(packages.map((pkg) => [pkg.id, pkg]));
|
||||||
|
const root = packages.find((pkg) => pkg['pkg-name'] === 'nuthatch' && pkg['component-name'] === 'exe:nuthatch');
|
||||||
|
if (root === undefined) throw new Error('exe:nuthatch not found in Cabal plan');
|
||||||
|
const selected = [];
|
||||||
|
const visited = new Set();
|
||||||
|
const pending = [...(root.depends ?? [])];
|
||||||
|
while (pending.length > 0) {
|
||||||
|
const id = pending.pop();
|
||||||
|
if (visited.has(id)) continue;
|
||||||
|
visited.add(id);
|
||||||
|
const pkg = byId.get(id);
|
||||||
|
if (pkg === undefined) throw new Error(`Cabal dependency ${id} not found in plan`);
|
||||||
|
pending.push(...(pkg.depends ?? []));
|
||||||
|
if (pkg['pkg-name'] !== 'nuthatch') selected.push(pkg);
|
||||||
|
}
|
||||||
|
return uniquePackages(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function haskellNotice(pkg, cacheRoot, docRoot, ghcVersion) {
|
||||||
|
const name = pkg['pkg-name'];
|
||||||
|
const version = pkg['pkg-version'];
|
||||||
|
if (pkg.type === 'configured') {
|
||||||
|
const archive = join(cacheRoot, 'packages', 'hackage.haskell.org', name, version, `${name}-${version}.tar.gz`);
|
||||||
|
if (!existsSync(archive)) {
|
||||||
|
run('cabal', ['get', `${name}-${version}`, '--destdir=dist-newstyle/third-party/license-sources']);
|
||||||
|
}
|
||||||
|
if (!existsSync(archive)) throw new Error(`Hackage archive not found after fetch: ${archive}`);
|
||||||
|
const entries = run('tar', ['-tzf', archive]).trim().split('\n');
|
||||||
|
const cabalEntry = entries.find((entry) => dirname(entry) === `${name}-${version}` && entry.endsWith('.cabal'));
|
||||||
|
if (cabalEntry === undefined) throw new Error(`Cabal metadata not found in ${archive}`);
|
||||||
|
const cabalText = run('tar', ['-xOzf', archive, cabalEntry]);
|
||||||
|
const licenseValue = /^license:\s*(\S.*?)\s*$/im.exec(cabalText)?.[1];
|
||||||
|
if (licenseValue === undefined) throw new Error(`license field not found for ${name}-${version}`);
|
||||||
|
const license = normalizeLicense(licenseValue);
|
||||||
|
const documents = archiveDocuments(archive, entries, `${name}-${version}`);
|
||||||
|
if (documents.length === 0) {
|
||||||
|
documents.push({
|
||||||
|
name: 'PACKAGE METADATA',
|
||||||
|
text: `The source archive supplies no standalone license or notice file.\nIts Cabal metadata declares ${license}.\n`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ecosystem: 'Haskell',
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
license,
|
||||||
|
documents
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (pkg.type === 'pre-existing') {
|
||||||
|
const packageId = `${name}-${version}`;
|
||||||
|
const licenseOutput = run('ghc-pkg', ['--global', 'field', packageId, 'license']);
|
||||||
|
const licenseValue = /^license:\s*(\S.*?)\s*$/im.exec(licenseOutput)?.[1];
|
||||||
|
if (licenseValue === undefined) throw new Error(`GHC package license not found for ${packageId}`);
|
||||||
|
const license = normalizeLicense(licenseValue);
|
||||||
|
const packageLicense = join(docRoot, packageId, 'LICENSE');
|
||||||
|
const fallbackLicense = join(docRoot, `ghc-${ghcVersion}`, 'LICENSE');
|
||||||
|
const licensePath = existsSync(packageLicense) ? packageLicense : fallbackLicense;
|
||||||
|
if (!existsSync(licensePath)) throw new Error(`license text not found for ${packageId}`);
|
||||||
|
return {
|
||||||
|
ecosystem: 'Haskell',
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
license,
|
||||||
|
documents: [{ name: 'LICENSE', text: readFileSync(licensePath, 'utf8') }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error(`unsupported Cabal plan package type ${pkg.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function archiveDocuments(archive, entries, root) {
|
||||||
|
const candidates = entries.filter((entry) => {
|
||||||
|
const relative = entry.slice(root.length + 1);
|
||||||
|
return !relative.includes('/') && /^(licen[cs]e|copying|notice)([._-].*)?$/i.test(relative);
|
||||||
|
});
|
||||||
|
return candidates.sort().map((entry) => ({
|
||||||
|
name: basename(entry),
|
||||||
|
text: run('tar', ['-xOzf', archive, entry])
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function npmProductionPackages(lockValue) {
|
||||||
|
return Object.entries(lockValue.packages)
|
||||||
|
.filter(([path, metadata]) => path !== '' && metadata.dev !== true)
|
||||||
|
.map(([path, metadata]) => ({
|
||||||
|
path: path.replace(/^node_modules\//, ''),
|
||||||
|
name: metadata.name ?? path.replace(/^node_modules\//, ''),
|
||||||
|
version: metadata.version,
|
||||||
|
license: metadata.license
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function npmNotice(pkg, modulesRoot) {
|
||||||
|
if (pkg.version === undefined || pkg.license === undefined) {
|
||||||
|
throw new Error(`unclassified npm package ${pkg.name}`);
|
||||||
|
}
|
||||||
|
const packageRoot = join(modulesRoot, pkg.path);
|
||||||
|
const candidates = readdirSync(packageRoot)
|
||||||
|
.filter((name) => /^(licen[cs]e|copying|notice)([._-].*)?$/i.test(name))
|
||||||
|
.sort();
|
||||||
|
const documents = candidates.map((name) => ({ name, text: readFileSync(join(packageRoot, name), 'utf8') }));
|
||||||
|
if (documents.length === 0) {
|
||||||
|
documents.push({
|
||||||
|
name: 'PACKAGE METADATA',
|
||||||
|
text: `The installed package supplies no standalone license or notice file.\nIts locked metadata declares ${pkg.license}.\n`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ecosystem: 'npm',
|
||||||
|
name: pkg.name,
|
||||||
|
version: pkg.version,
|
||||||
|
license: pkg.license,
|
||||||
|
documents
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function comparePackage(left, right) {
|
||||||
|
return compareText(left.name, right.name) || compareText(left.version, right.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareText(left, right) {
|
||||||
|
return left < right ? -1 : left > right ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLicense(license) {
|
||||||
|
if (license === 'BSD2') return 'BSD-2-Clause';
|
||||||
|
if (license === 'BSD3') return 'BSD-3-Clause';
|
||||||
|
return license;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateLicenses(packages) {
|
||||||
|
const allowed = new Set(['Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC', 'MIT']);
|
||||||
|
const unsupported = packages.filter((pkg) => !allowed.has(pkg.license));
|
||||||
|
if (unsupported.length > 0) {
|
||||||
|
throw new Error(`unsupported shipped licenses: ${unsupported.map((pkg) => `${pkg.name} (${pkg.license})`).join(', ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inventory(title, packages) {
|
||||||
|
return [
|
||||||
|
title,
|
||||||
|
...packages.map((pkg) => `${pkg.name}\t${pkg.version}\t${pkg.license}`),
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNotice(pkg) {
|
||||||
|
const separator = '='.repeat(79);
|
||||||
|
return [
|
||||||
|
separator,
|
||||||
|
`${pkg.ecosystem}: ${pkg.name} ${pkg.version} (${pkg.license})`,
|
||||||
|
...pkg.documents.flatMap((document) => [
|
||||||
|
`--- ${document.name} ---`,
|
||||||
|
normalizeDocument(document.text),
|
||||||
|
''
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDocument(document) {
|
||||||
|
return document
|
||||||
|
.replace(/\r\n?/g, '\n')
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => line.replace(/[ \t]+$/, ''))
|
||||||
|
.join('\n')
|
||||||
|
.replace(/\n+$/, '');
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,12 @@ set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||||
|
|
||||||
|
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
if ! git diff --quiet || ! git diff --cached --quiet || [[ -n "$(git ls-files --others --exclude-standard)" ]]; then
|
if ! git diff --quiet || ! git diff --cached --quiet || [[ -n "$(git ls-files --others --exclude-standard)" ]]; then
|
||||||
echo "check-v1 requires a clean checkout" >&2
|
echo "check-v1 requires a clean checkout" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
unset NUTHATCH_APP_INTERACTIVE
|
unset NUTHATCH_APP_INTERACTIVE
|
||||||
unset NUTHATCH_STREAMING_LANE
|
unset NUTHATCH_STREAMING_LANE
|
||||||
|
|
@ -16,6 +18,8 @@ unset NUTHATCH_IDEMPOTENCY_FIRST_LANE_ONLY
|
||||||
unset NUTHATCH_RECOVERY_TARGET
|
unset NUTHATCH_RECOVERY_TARGET
|
||||||
unset NUTHATCH_PROTOCOL_TARGET
|
unset NUTHATCH_PROTOCOL_TARGET
|
||||||
|
|
||||||
|
npm --prefix web ci
|
||||||
|
|
||||||
targets=(
|
targets=(
|
||||||
test
|
test
|
||||||
check-dependencies
|
check-dependencies
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,16 @@ prepare_tools() {
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_provider() {
|
configure_provider() {
|
||||||
|
local provider_bin="$RUNTIME/provider-bin"
|
||||||
if [[ -z "${NUTHATCH_REAL_NERDCTL:-}" ]]; then
|
if [[ -z "${NUTHATCH_REAL_NERDCTL:-}" ]]; then
|
||||||
NUTHATCH_REAL_NERDCTL="$(command -v nerdctl)" || return
|
NUTHATCH_REAL_NERDCTL="$(command -v nerdctl)" || return
|
||||||
fi
|
fi
|
||||||
|
mkdir -p "$provider_bin"
|
||||||
|
install -m 0755 "$TESTBED/provider/nerdctl" "$provider_bin/nerdctl"
|
||||||
export NUTHATCH_REAL_NERDCTL
|
export NUTHATCH_REAL_NERDCTL
|
||||||
export CONTAINERD_NAMESPACE="$NERDCTL_NAMESPACE"
|
export CONTAINERD_NAMESPACE="$NERDCTL_NAMESPACE"
|
||||||
export KIND_EXPERIMENTAL_PROVIDER=nerdctl
|
export KIND_EXPERIMENTAL_PROVIDER=nerdctl
|
||||||
export PATH="$TESTBED/provider:$PATH"
|
export PATH="$provider_bin:$PATH"
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime_ctr() {
|
runtime_ctr() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue