@shortlink-org/portolan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/catalog/enum_test.go +46 -0
- package/catalog/model.go +932 -0
- package/catalog/roundtrip_test.go +185 -0
- package/catalog/via_test.go +38 -0
- package/cli/portolan.mjs +368 -0
- package/go.mod +14 -0
- package/go.sum +14 -0
- package/index.html +13 -0
- package/internal/commands/cargo.go +154 -0
- package/internal/commands/commands.go +143 -0
- package/internal/commands/commands_test.go +323 -0
- package/internal/commands/gradle.go +77 -0
- package/internal/commands/justfile.go +82 -0
- package/internal/commands/makefile.go +106 -0
- package/internal/commands/maven.go +93 -0
- package/internal/commands/packagejson.go +159 -0
- package/internal/commands/pyproject.go +169 -0
- package/internal/commands/taskfile.go +101 -0
- package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
- package/internal/commands/testdata/estate/Makefile +33 -0
- package/internal/commands/testdata/estate/Taskfile.yml +21 -0
- package/internal/commands/testdata/estate/build.gradle.kts +21 -0
- package/internal/commands/testdata/estate/justfile +20 -0
- package/internal/commands/testdata/estate/package.json +13 -0
- package/internal/commands/testdata/estate/pom.xml +29 -0
- package/internal/commands/testdata/estate/pyproject.toml +27 -0
- package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
- package/internal/commands/testdata/golden/commands.json +305 -0
- package/internal/gohttp/analyze.go +2500 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +143 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +36 -0
- package/internal/goscan/tree.go +155 -0
- package/internal/goscan/types.go +99 -0
- package/internal/wsdl/ids.go +127 -0
- package/internal/wsdl/ids_test.go +21 -0
- package/internal/wsdl/model.go +62 -0
- package/internal/wsdl/parse.go +920 -0
- package/internal/wsdl/parse_test.go +133 -0
- package/package.json +117 -0
- package/plugin/describe.go +107 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +117 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +1160 -0
- package/plugins/extract-adr/describe.go +19 -0
- package/plugins/extract-adr/describe_test.go +11 -0
- package/plugins/extract-adr/extract.go +153 -0
- package/plugins/extract-adr/extract_test.go +350 -0
- package/plugins/extract-adr/history.go +99 -0
- package/plugins/extract-adr/main.go +65 -0
- package/plugins/extract-adr/options.schema.json +26 -0
- package/plugins/extract-adr/parse.go +642 -0
- package/plugins/extract-adr/parse_test.go +405 -0
- package/plugins/extract-asyncapi/describe.go +19 -0
- package/plugins/extract-asyncapi/describe_test.go +11 -0
- package/plugins/extract-asyncapi/extract.go +315 -0
- package/plugins/extract-asyncapi/extract_test.go +197 -0
- package/plugins/extract-asyncapi/main.go +48 -0
- package/plugins/extract-asyncapi/options.schema.json +26 -0
- package/plugins/extract-asyncapi/spec.go +151 -0
- package/plugins/extract-celery/README.md +169 -0
- package/plugins/extract-celery/extract.py +346 -0
- package/plugins/extract-celery/extract_test.py +192 -0
- package/plugins/extract-celery/main.py +66 -0
- package/plugins/extract-celery/options.py +31 -0
- package/plugins/extract-celery/options.schema.json +31 -0
- package/plugins/extract-celery/producers.py +156 -0
- package/plugins/extract-celery/routes_test.py +106 -0
- package/plugins/extract-commands/describe.go +19 -0
- package/plugins/extract-commands/describe_test.go +11 -0
- package/plugins/extract-commands/extract.go +79 -0
- package/plugins/extract-commands/extract_test.go +92 -0
- package/plugins/extract-commands/main.go +55 -0
- package/plugins/extract-commands/options.schema.json +20 -0
- package/plugins/extract-csr/avro.go +258 -0
- package/plugins/extract-csr/describe.go +19 -0
- package/plugins/extract-csr/describe_test.go +11 -0
- package/plugins/extract-csr/extract.go +338 -0
- package/plugins/extract-csr/extract_test.go +374 -0
- package/plugins/extract-csr/jsonschema.go +343 -0
- package/plugins/extract-csr/lock.go +25 -0
- package/plugins/extract-csr/main.go +84 -0
- package/plugins/extract-csr/options.schema.json +43 -0
- package/plugins/extract-csr/subject.go +102 -0
- package/plugins/extract-django/README.md +386 -0
- package/plugins/extract-django/apps.py +100 -0
- package/plugins/extract-django/auth.py +302 -0
- package/plugins/extract-django/auth_test.py +188 -0
- package/plugins/extract-django/clients.py +121 -0
- package/plugins/extract-django/contracts.py +458 -0
- package/plugins/extract-django/database.py +119 -0
- package/plugins/extract-django/domain.py +318 -0
- package/plugins/extract-django/events.py +86 -0
- package/plugins/extract-django/extract.py +601 -0
- package/plugins/extract-django/extract_test.py +397 -0
- package/plugins/extract-django/flows.py +1278 -0
- package/plugins/extract-django/flows_test.py +338 -0
- package/plugins/extract-django/ids.py +42 -0
- package/plugins/extract-django/ids_test.py +30 -0
- package/plugins/extract-django/lifecycle.py +222 -0
- package/plugins/extract-django/lifecycle_test.py +76 -0
- package/plugins/extract-django/main.py +66 -0
- package/plugins/extract-django/openapi.py +229 -0
- package/plugins/extract-django/openapi_test.py +60 -0
- package/plugins/extract-django/operations.py +96 -0
- package/plugins/extract-django/options.py +61 -0
- package/plugins/extract-django/options.schema.json +126 -0
- package/plugins/extract-django/routing.py +214 -0
- package/plugins/extract-django/routing_test.py +198 -0
- package/plugins/extract-django/serializers.py +558 -0
- package/plugins/extract-django/store.py +434 -0
- package/plugins/extract-django/transport.py +242 -0
- package/plugins/extract-flows/describe.go +19 -0
- package/plugins/extract-flows/describe_test.go +11 -0
- package/plugins/extract-flows/extract.go +91 -0
- package/plugins/extract-flows/main.go +45 -0
- package/plugins/extract-flows/options.schema.json +16 -0
- package/plugins/extract-flows/parse.go +593 -0
- package/plugins/extract-flows/parse_test.go +204 -0
- package/plugins/extract-glossary/describe.go +19 -0
- package/plugins/extract-glossary/describe_test.go +11 -0
- package/plugins/extract-glossary/extract.go +115 -0
- package/plugins/extract-glossary/extract_test.go +220 -0
- package/plugins/extract-glossary/main.go +59 -0
- package/plugins/extract-glossary/options.schema.json +21 -0
- package/plugins/extract-glossary/parse.go +214 -0
- package/plugins/extract-glossary/parse_test.go +203 -0
- package/plugins/extract-go/aggregate.go +214 -0
- package/plugins/extract-go/client.go +409 -0
- package/plugins/extract-go/client_test.go +266 -0
- package/plugins/extract-go/describe.go +19 -0
- package/plugins/extract-go/describe_test.go +11 -0
- package/plugins/extract-go/enum.go +195 -0
- package/plugins/extract-go/enum_test.go +82 -0
- package/plugins/extract-go/event.go +99 -0
- package/plugins/extract-go/extract.go +191 -0
- package/plugins/extract-go/extract_test.go +261 -0
- package/plugins/extract-go/flow.go +1441 -0
- package/plugins/extract-go/flow_test.go +609 -0
- package/plugins/extract-go/httpclient.go +174 -0
- package/plugins/extract-go/httpclient_test.go +296 -0
- package/plugins/extract-go/ids.go +92 -0
- package/plugins/extract-go/layout.go +225 -0
- package/plugins/extract-go/layout_test.go +125 -0
- package/plugins/extract-go/lifecycle.go +324 -0
- package/plugins/extract-go/lifecycle_test.go +108 -0
- package/plugins/extract-go/main.go +79 -0
- package/plugins/extract-go/operation.go +157 -0
- package/plugins/extract-go/options.schema.json +66 -0
- package/plugins/extract-go/source.go +316 -0
- package/plugins/extract-go/transport.go +321 -0
- package/plugins/extract-go/transport_test.go +145 -0
- package/plugins/extract-go/wiring.go +434 -0
- package/plugins/extract-go-nats/describe.go +19 -0
- package/plugins/extract-go-nats/describe_test.go +11 -0
- package/plugins/extract-go-nats/extract.go +177 -0
- package/plugins/extract-go-nats/extract_test.go +389 -0
- package/plugins/extract-go-nats/index.go +379 -0
- package/plugins/extract-go-nats/main.go +42 -0
- package/plugins/extract-go-nats/options.schema.json +21 -0
- package/plugins/extract-go-nats/resolve.go +161 -0
- package/plugins/extract-go-nats/sites.go +224 -0
- package/plugins/extract-graphql/describe.go +19 -0
- package/plugins/extract-graphql/describe_test.go +11 -0
- package/plugins/extract-graphql/extract.go +433 -0
- package/plugins/extract-graphql/extract_test.go +256 -0
- package/plugins/extract-graphql/ids.go +49 -0
- package/plugins/extract-graphql/lex.go +237 -0
- package/plugins/extract-graphql/main.go +51 -0
- package/plugins/extract-graphql/options.schema.json +31 -0
- package/plugins/extract-graphql/parse.go +621 -0
- package/plugins/extract-graphql/parse_test.go +122 -0
- package/plugins/extract-http-clients/describe.go +19 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +705 -0
- package/plugins/extract-http-clients/extract_test.go +1263 -0
- package/plugins/extract-http-clients/main.go +42 -0
- package/plugins/extract-http-clients/options.schema.json +33 -0
- package/plugins/extract-java/README.md +190 -0
- package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
- package/plugins/extract-java/options.schema.json +70 -0
- package/plugins/extract-openapi/describe.go +19 -0
- package/plugins/extract-openapi/describe_test.go +11 -0
- package/plugins/extract-openapi/discover.go +243 -0
- package/plugins/extract-openapi/extract.go +526 -0
- package/plugins/extract-openapi/extract_test.go +545 -0
- package/plugins/extract-openapi/main.go +75 -0
- package/plugins/extract-openapi/options.schema.json +62 -0
- package/plugins/extract-openapi/spec.go +350 -0
- package/plugins/extract-project/README.md +22 -0
- package/plugins/extract-project/describe.go +19 -0
- package/plugins/extract-project/describe_test.go +11 -0
- package/plugins/extract-project/extract.go +221 -0
- package/plugins/extract-project/extract_test.go +109 -0
- package/plugins/extract-project/main.go +41 -0
- package/plugins/extract-project/options.schema.json +48 -0
- package/plugins/extract-proto/ast.go +125 -0
- package/plugins/extract-proto/consumes.go +77 -0
- package/plugins/extract-proto/describe.go +19 -0
- package/plugins/extract-proto/extract.go +293 -0
- package/plugins/extract-proto/extract_test.go +459 -0
- package/plugins/extract-proto/ids.go +89 -0
- package/plugins/extract-proto/ids_test.go +57 -0
- package/plugins/extract-proto/lex.go +285 -0
- package/plugins/extract-proto/main.go +100 -0
- package/plugins/extract-proto/module.go +120 -0
- package/plugins/extract-proto/options.schema.json +51 -0
- package/plugins/extract-proto/parse.go +720 -0
- package/plugins/extract-proto/parse_test.go +307 -0
- package/plugins/extract-proto/provides.go +236 -0
- package/plugins/extract-proto/resolve.go +222 -0
- package/plugins/extract-proto/resolve_test.go +119 -0
- package/plugins/extract-python-kafka/README.md +36 -0
- package/plugins/extract-python-kafka/extract.py +211 -0
- package/plugins/extract-python-kafka/extract_test.py +68 -0
- package/plugins/extract-python-kafka/main.py +59 -0
- package/plugins/extract-python-kafka/options.py +29 -0
- package/plugins/extract-python-kafka/options.schema.json +31 -0
- package/plugins/extract-redis/README.md +11 -0
- package/plugins/extract-redis/describe.go +19 -0
- package/plugins/extract-redis/describe_test.go +11 -0
- package/plugins/extract-redis/extract.go +183 -0
- package/plugins/extract-redis/extract_test.go +168 -0
- package/plugins/extract-redis/keyspaces.go +469 -0
- package/plugins/extract-redis/main.go +44 -0
- package/plugins/extract-redis/options.schema.json +30 -0
- package/plugins/extract-river/describe.go +19 -0
- package/plugins/extract-river/describe_test.go +11 -0
- package/plugins/extract-river/extract.go +507 -0
- package/plugins/extract-river/extract_test.go +132 -0
- package/plugins/extract-river/main.go +42 -0
- package/plugins/extract-river/options.schema.json +21 -0
- package/plugins/extract-rust/Cargo.lock +133 -0
- package/plugins/extract-rust/Cargo.toml +23 -0
- package/plugins/extract-rust/README.md +204 -0
- package/plugins/extract-rust/options.schema.json +65 -0
- package/plugins/extract-rust/rustfmt.toml +1 -0
- package/plugins/extract-sql/ddl.go +893 -0
- package/plugins/extract-sql/ddl_test.go +401 -0
- package/plugins/extract-sql/describe.go +19 -0
- package/plugins/extract-sql/describe_test.go +11 -0
- package/plugins/extract-sql/layout.go +221 -0
- package/plugins/extract-sql/layout_test.go +55 -0
- package/plugins/extract-sql/lineage.go +107 -0
- package/plugins/extract-sql/main.go +141 -0
- package/plugins/extract-sql/maps.go +564 -0
- package/plugins/extract-sql/maps_java.go +117 -0
- package/plugins/extract-sql/maps_rust.go +333 -0
- package/plugins/extract-sql/maps_rust_test.go +70 -0
- package/plugins/extract-sql/maps_test.go +204 -0
- package/plugins/extract-sql/maps_ts.go +398 -0
- package/plugins/extract-sql/maps_ts_test.go +136 -0
- package/plugins/extract-sql/options.schema.json +53 -0
- package/plugins/extract-sql/projection.go +70 -0
- package/plugins/extract-sql/projection_test.go +50 -0
- package/plugins/extract-sql/store.go +420 -0
- package/plugins/extract-sql/store_test.go +233 -0
- package/plugins/extract-sql/view.go +295 -0
- package/plugins/extract-ts/README.md +306 -0
- package/plugins/extract-ts/ast.test.ts +178 -0
- package/plugins/extract-ts/ast.ts +648 -0
- package/plugins/extract-ts/clients.ts +185 -0
- package/plugins/extract-ts/domain.ts +164 -0
- package/plugins/extract-ts/extract.test.ts +208 -0
- package/plugins/extract-ts/extract.ts +200 -0
- package/plugins/extract-ts/flows.ts +783 -0
- package/plugins/extract-ts/graphql.test.ts +73 -0
- package/plugins/extract-ts/graphql.ts +181 -0
- package/plugins/extract-ts/ids.test.ts +21 -0
- package/plugins/extract-ts/ids.ts +52 -0
- package/plugins/extract-ts/lifecycle.ts +138 -0
- package/plugins/extract-ts/main.ts +48 -0
- package/plugins/extract-ts/openapi.test.ts +64 -0
- package/plugins/extract-ts/openapi.ts +99 -0
- package/plugins/extract-ts/operations.ts +93 -0
- package/plugins/extract-ts/options.schema.json +96 -0
- package/plugins/extract-ts/source.test.ts +174 -0
- package/plugins/extract-ts/source.ts +465 -0
- package/plugins/extract-ts/transport.ts +147 -0
- package/plugins/extract-ts/tsconfig.json +13 -0
- package/plugins/extract-ts/wiring.ts +190 -0
- package/plugins/extract-watermill/describe.go +19 -0
- package/plugins/extract-watermill/describe_test.go +11 -0
- package/plugins/extract-watermill/extract.go +1228 -0
- package/plugins/extract-watermill/extract_test.go +234 -0
- package/plugins/extract-watermill/main.go +41 -0
- package/plugins/extract-watermill/options.schema.json +21 -0
- package/plugins/extract-wsdl/describe.go +19 -0
- package/plugins/extract-wsdl/describe_test.go +11 -0
- package/plugins/extract-wsdl/extract.go +148 -0
- package/plugins/extract-wsdl/extract_test.go +45 -0
- package/plugins/extract-wsdl/main.go +53 -0
- package/plugins/extract-wsdl/options.schema.json +59 -0
- package/plugins/fetch-bsr/auth.go +114 -0
- package/plugins/fetch-bsr/bsr.go +240 -0
- package/plugins/fetch-bsr/cache.go +66 -0
- package/plugins/fetch-bsr/describe.go +19 -0
- package/plugins/fetch-bsr/fetch.go +192 -0
- package/plugins/fetch-bsr/fetch_test.go +465 -0
- package/plugins/fetch-bsr/lock.go +71 -0
- package/plugins/fetch-bsr/main.go +117 -0
- package/plugins/fetch-bsr/options.schema.json +42 -0
- package/plugins/fetch-csr/auth.go +95 -0
- package/plugins/fetch-csr/cache.go +69 -0
- package/plugins/fetch-csr/describe.go +19 -0
- package/plugins/fetch-csr/describe_test.go +11 -0
- package/plugins/fetch-csr/fetch.go +254 -0
- package/plugins/fetch-csr/fetch_test.go +494 -0
- package/plugins/fetch-csr/lock.go +95 -0
- package/plugins/fetch-csr/main.go +122 -0
- package/plugins/fetch-csr/options.schema.json +42 -0
- package/plugins/fetch-csr/registry.go +209 -0
- package/plugins/fetch-git/cache.go +65 -0
- package/plugins/fetch-git/describe.go +19 -0
- package/plugins/fetch-git/describe_test.go +11 -0
- package/plugins/fetch-git/fetch.go +181 -0
- package/plugins/fetch-git/fetch_test.go +332 -0
- package/plugins/fetch-git/git.go +169 -0
- package/plugins/fetch-git/lock.go +72 -0
- package/plugins/fetch-git/main.go +127 -0
- package/plugins/fetch-git/offline.go +39 -0
- package/plugins/fetch-git/options.schema.json +42 -0
- package/plugins/fetch-git/pin.go +90 -0
- package/plugins/fetch-git/pin_test.go +74 -0
- package/plugins/gen-backstage/describe.go +17 -0
- package/plugins/gen-backstage/main.go +22 -0
- package/plugins/gen-backstage/options.schema.json +30 -0
- package/plugins/gen-backstage/plugin.go +473 -0
- package/plugins/gen-backstage/plugin_test.go +145 -0
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +162 -0
- package/plugins/gen-markdown/aggregate.go +362 -0
- package/plugins/gen-markdown/canonical.go +93 -0
- package/plugins/gen-markdown/context.go +90 -0
- package/plugins/gen-markdown/coverage_test.go +89 -0
- package/plugins/gen-markdown/describe.go +19 -0
- package/plugins/gen-markdown/describe_test.go +11 -0
- package/plugins/gen-markdown/external.go +71 -0
- package/plugins/gen-markdown/flow.go +277 -0
- package/plugins/gen-markdown/glossary.go +76 -0
- package/plugins/gen-markdown/glossary_test.go +148 -0
- package/plugins/gen-markdown/llms.go +302 -0
- package/plugins/gen-markdown/main.go +22 -0
- package/plugins/gen-markdown/markdown.go +254 -0
- package/plugins/gen-markdown/markdown_test.go +100 -0
- package/plugins/gen-markdown/module.go +107 -0
- package/plugins/gen-markdown/options.schema.json +16 -0
- package/plugins/gen-markdown/plugin.go +39 -0
- package/plugins/gen-markdown/quality_test.go +176 -0
- package/plugins/gen-markdown/redis_test.go +38 -0
- package/plugins/gen-markdown/render.go +344 -0
- package/plugins/gen-markdown/render_test.go +186 -0
- package/plugins/gen-markdown/service.go +456 -0
- package/plugins/gen-markdown/source.go +177 -0
- package/plugins/gen-markdown/store.go +201 -0
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +17 -0
- package/plugins/gen-mermaid/main.go +22 -0
- package/plugins/gen-mermaid/options.schema.json +10 -0
- package/plugins/gen-mermaid/plugin.go +104 -0
- package/plugins/gen-mermaid/plugin_test.go +33 -0
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/pyplugin/catalog.py +209 -0
- package/plugins/pyplugin/celery_conf.py +436 -0
- package/plugins/pyplugin/celery_tasks.py +76 -0
- package/plugins/pyplugin/kafka.py +570 -0
- package/plugins/pyplugin/names.py +38 -0
- package/plugins/pyplugin/protocol.py +79 -0
- package/plugins/pyplugin/source.py +285 -0
- package/plugins/verify-codeowners/describe.go +19 -0
- package/plugins/verify-codeowners/describe_test.go +11 -0
- package/plugins/verify-codeowners/main.go +75 -0
- package/plugins/verify-codeowners/match.go +85 -0
- package/plugins/verify-codeowners/match_test.go +47 -0
- package/plugins/verify-codeowners/options.schema.json +15 -0
- package/plugins/verify-codeowners/owners.go +164 -0
- package/plugins/verify-codeowners/owners_test.go +225 -0
- package/plugins/verify-codeowners/parse.go +90 -0
- package/plugins/verify-codeowners/parse_test.go +62 -0
- package/plugins/verify-otel/describe.go +19 -0
- package/plugins/verify-otel/describe_test.go +11 -0
- package/plugins/verify-otel/main.go +53 -0
- package/plugins/verify-otel/match.go +336 -0
- package/plugins/verify-otel/options.schema.json +30 -0
- package/plugins/verify-otel/otlp.go +200 -0
- package/plugins/verify-otel/verify.go +734 -0
- package/plugins/verify-otel/verify_test.go +460 -0
- package/portolan.json +1073 -0
- package/public/favicon.svg +12 -0
- package/schema/portolan.schema.json +2558 -0
- package/scripts/README.md +53 -0
- package/scripts/build-report.mjs +60 -0
- package/scripts/build-report.test.mjs +44 -0
- package/scripts/builtin-plugins.mjs +49 -0
- package/scripts/catalog-sources.mjs +67 -0
- package/scripts/diff.mjs +315 -0
- package/scripts/diff.test.mjs +151 -0
- package/scripts/forge-comment.mjs +144 -0
- package/scripts/forge-comment.test.mjs +151 -0
- package/scripts/forge-release.mjs +138 -0
- package/scripts/forge-release.test.mjs +106 -0
- package/scripts/forge.mjs +61 -0
- package/scripts/gen-likec4.mjs +854 -0
- package/scripts/gen-likec4.test.mjs +258 -0
- package/scripts/gen.mjs +603 -0
- package/scripts/generated-docs.test.mjs +66 -0
- package/scripts/local-api.mjs +1192 -0
- package/scripts/local-api.test.mjs +387 -0
- package/scripts/manifest.mjs +214 -0
- package/scripts/manifest.test.mjs +148 -0
- package/scripts/output-path.mjs +79 -0
- package/scripts/output-path.test.mjs +45 -0
- package/scripts/package-smoke.mjs +37 -0
- package/scripts/plugin-host.mjs +419 -0
- package/scripts/plugin-host.test.mjs +109 -0
- package/scripts/plugin-wasm-worker.mjs +34 -0
- package/scripts/run-builtin.mjs +81 -0
- package/scripts/schema.mjs +357 -0
- package/scripts/site-docs.mjs +295 -0
- package/scripts/site-docs.test.mjs +181 -0
- package/scripts/vendor-lock.mjs +58 -0
- package/scripts/vendor-lock.test.mjs +69 -0
- package/src/README.md +38 -0
- package/src/app/App.tsx +448 -0
- package/src/app/BranchPicker.test.ts +46 -0
- package/src/app/BranchPicker.tsx +153 -0
- package/src/app/Breadcrumbs.test.ts +38 -0
- package/src/app/Breadcrumbs.tsx +223 -0
- package/src/app/BuildStamp.tsx +146 -0
- package/src/app/CommandPalette.tsx +292 -0
- package/src/app/HashScroll.tsx +45 -0
- package/src/app/Sidebar.tsx +1742 -0
- package/src/app/TopBar.tsx +393 -0
- package/src/app/density.tsx +64 -0
- package/src/app/forge-access.test.ts +27 -0
- package/src/app/forge-access.tsx +80 -0
- package/src/app/panels.tsx +141 -0
- package/src/app/pins.tsx +242 -0
- package/src/app/query-client.ts +24 -0
- package/src/app/responsive.ts +55 -0
- package/src/app/shortcuts.test.ts +39 -0
- package/src/app/shortcuts.tsx +282 -0
- package/src/app/theme.tsx +51 -0
- package/src/app/title.test.ts +14 -0
- package/src/app/title.ts +30 -0
- package/src/app/toast.tsx +59 -0
- package/src/app/tree-keys.test.ts +80 -0
- package/src/app/tree-keys.ts +87 -0
- package/src/app/ui-store.ts +52 -0
- package/src/catalog-error.test.ts +105 -0
- package/src/catalog-profile.test.ts +59 -0
- package/src/catalog-profile.ts +101 -0
- package/src/catalog-stores.test.ts +431 -0
- package/src/catalog.test.ts +1215 -0
- package/src/catalog.ts +3125 -0
- package/src/chat/Cards.tsx +408 -0
- package/src/chat/ChatPanel.tsx +79 -0
- package/src/chat/ChatSettings.tsx +219 -0
- package/src/chat/Composer.tsx +99 -0
- package/src/chat/Conversation.tsx +281 -0
- package/src/chat/Header.tsx +46 -0
- package/src/chat/Message.tsx +49 -0
- package/src/chat/Notice.tsx +195 -0
- package/src/chat/Starter.tsx +67 -0
- package/src/chat/Steps.tsx +45 -0
- package/src/chat/Waiting.tsx +87 -0
- package/src/chat/flags.test.ts +59 -0
- package/src/chat/flags.ts +155 -0
- package/src/chat/linkify.test.ts +30 -0
- package/src/chat/linkify.ts +45 -0
- package/src/chat/prefs.ts +56 -0
- package/src/chat/prompt.test.ts +43 -0
- package/src/chat/prompt.ts +157 -0
- package/src/chat/store.ts +35 -0
- package/src/chat/tools.ts +58 -0
- package/src/chat/transport.ts +70 -0
- package/src/components/AdrRow.tsx +29 -0
- package/src/components/AnchorLink.tsx +67 -0
- package/src/components/ApiReference.tsx +134 -0
- package/src/components/AsyncApiReference.tsx +120 -0
- package/src/components/CatalogStamp.tsx +155 -0
- package/src/components/ChannelRows.tsx +132 -0
- package/src/components/CommandRows.tsx +139 -0
- package/src/components/DiagramSkeleton.tsx +71 -0
- package/src/components/EditorLink.tsx +85 -0
- package/src/components/FieldTree.tsx +305 -0
- package/src/components/FlowTrigger.tsx +34 -0
- package/src/components/Ident.tsx +89 -0
- package/src/components/Integrations.tsx +243 -0
- package/src/components/LifecycleDiagram.tsx +110 -0
- package/src/components/MachineDocs.tsx +42 -0
- package/src/components/Markdown.tsx +173 -0
- package/src/components/MarkdownTable.tsx +206 -0
- package/src/components/Mermaid.tsx +211 -0
- package/src/components/MethodRows.tsx +309 -0
- package/src/components/Overlay.tsx +125 -0
- package/src/components/PageHeader.tsx +189 -0
- package/src/components/RowActions.tsx +97 -0
- package/src/components/SchemaDocument.tsx +89 -0
- package/src/components/Select.tsx +126 -0
- package/src/components/ShapeRows.tsx +118 -0
- package/src/components/SourceDoc.tsx +82 -0
- package/src/components/SourcePreview.tsx +441 -0
- package/src/components/TabRow.tsx +182 -0
- package/src/components/TechIcon.tsx +34 -0
- package/src/components/Toc.tsx +132 -0
- package/src/components/WhatLinksHere.tsx +273 -0
- package/src/components/WsdlReference.tsx +84 -0
- package/src/components/ddd-icons.tsx +107 -0
- package/src/components/kind.tsx +157 -0
- package/src/components/logo.tsx +61 -0
- package/src/components/primitives.tsx +264 -0
- package/src/data.test.ts +18 -0
- package/src/data.ts +202 -0
- package/src/enrich.test.ts +1150 -0
- package/src/enrich.ts +672 -0
- package/src/er/ErCanvas.tsx +533 -0
- package/src/er/RedisSchema.tsx +74 -0
- package/src/er/RelationCard.tsx +267 -0
- package/src/er/StoreHeader.tsx +155 -0
- package/src/er/TableNode.tsx +84 -0
- package/src/er/ViewNode.tsx +83 -0
- package/src/er/layout.test.ts +115 -0
- package/src/er/layout.ts +77 -0
- package/src/er/lineage.test.ts +140 -0
- package/src/er/lineage.ts +107 -0
- package/src/er/markers.tsx +79 -0
- package/src/er/spec.test.ts +344 -0
- package/src/er/spec.ts +426 -0
- package/src/flow/ChainList.tsx +162 -0
- package/src/flow/FlowTable.tsx +123 -0
- package/src/flow/FlowToolbar.tsx +372 -0
- package/src/flow/StepDetail.tsx +380 -0
- package/src/flow/StepRail.tsx +428 -0
- package/src/flow/answers.test.ts +50 -0
- package/src/flow/answers.ts +58 -0
- package/src/flow/chain.test.ts +383 -0
- package/src/flow/chain.ts +353 -0
- package/src/flow/chapters.test.ts +185 -0
- package/src/flow/chapters.ts +225 -0
- package/src/flow/continues.test.ts +102 -0
- package/src/flow/continues.ts +65 -0
- package/src/flow/cross-context.test.ts +97 -0
- package/src/flow/cross-context.ts +35 -0
- package/src/flow/mermaid.test.ts +22 -0
- package/src/flow/mermaid.ts +141 -0
- package/src/flow/outline.test.ts +212 -0
- package/src/flow/outline.ts +170 -0
- package/src/flow/paths.test.ts +129 -0
- package/src/flow/paths.ts +147 -0
- package/src/flow/prefs.test.ts +80 -0
- package/src/flow/prefs.ts +104 -0
- package/src/graph/DependencyGraph.tsx +283 -0
- package/src/graph/DependencyNodes.tsx +162 -0
- package/src/graph/EdgeLabel.tsx +61 -0
- package/src/graph/FocusedEventGraph.tsx +211 -0
- package/src/graph/GraphToolbar.tsx +187 -0
- package/src/graph/RoutedEdge.tsx +80 -0
- package/src/graph/dependency-layout.test.ts +360 -0
- package/src/graph/dependency-layout.ts +444 -0
- package/src/graph/elk.test.ts +70 -0
- package/src/graph/elk.ts +287 -0
- package/src/graph/nodes.tsx +97 -0
- package/src/graph/theme.ts +81 -0
- package/src/graph/useElkFlow.ts +125 -0
- package/src/index.css +1547 -0
- package/src/language/InLanguage.tsx +40 -0
- package/src/language/TermCard.tsx +132 -0
- package/src/language/cards.test.ts +114 -0
- package/src/language/cards.ts +111 -0
- package/src/lib/admonition.test.ts +49 -0
- package/src/lib/admonition.ts +48 -0
- package/src/lib/adr.test.ts +131 -0
- package/src/lib/adr.ts +68 -0
- package/src/lib/api.test.ts +122 -0
- package/src/lib/api.ts +135 -0
- package/src/lib/backlinks.test.ts +330 -0
- package/src/lib/backlinks.ts +567 -0
- package/src/lib/branch-compare.ts +16 -0
- package/src/lib/build-info.test.ts +76 -0
- package/src/lib/build-info.ts +80 -0
- package/src/lib/catalog-diff.test.ts +275 -0
- package/src/lib/catalog-diff.ts +655 -0
- package/src/lib/clipboard.ts +43 -0
- package/src/lib/comparison-memory.test.ts +40 -0
- package/src/lib/comparison-memory.ts +39 -0
- package/src/lib/context-color.ts +27 -0
- package/src/lib/context-map.test.ts +148 -0
- package/src/lib/context-map.ts +399 -0
- package/src/lib/data-model.test.ts +245 -0
- package/src/lib/data-model.ts +399 -0
- package/src/lib/data-problems.test.ts +178 -0
- package/src/lib/data-problems.ts +314 -0
- package/src/lib/derive.test.ts +251 -0
- package/src/lib/derive.ts +515 -0
- package/src/lib/editor-link.test.ts +125 -0
- package/src/lib/editor-link.ts +96 -0
- package/src/lib/editor-prefs.ts +27 -0
- package/src/lib/event-graph.test.ts +237 -0
- package/src/lib/event-graph.ts +327 -0
- package/src/lib/export-canvas.ts +37 -0
- package/src/lib/export-file.test.ts +30 -0
- package/src/lib/export-file.ts +20 -0
- package/src/lib/flow-tree.test.ts +177 -0
- package/src/lib/flow-tree.ts +160 -0
- package/src/lib/forge-refs.test.ts +50 -0
- package/src/lib/forge-refs.ts +60 -0
- package/src/lib/format.test.ts +35 -0
- package/src/lib/format.ts +64 -0
- package/src/lib/github-catalog.test.ts +301 -0
- package/src/lib/github-catalog.ts +610 -0
- package/src/lib/integrations.test.ts +119 -0
- package/src/lib/integrations.ts +133 -0
- package/src/lib/kinds.test.ts +91 -0
- package/src/lib/kinds.ts +204 -0
- package/src/lib/lifecycle.test.ts +93 -0
- package/src/lib/lifecycle.ts +165 -0
- package/src/lib/local-api.ts +194 -0
- package/src/lib/motion-features.ts +11 -0
- package/src/lib/motion.test.ts +120 -0
- package/src/lib/motion.tsx +269 -0
- package/src/lib/palette-recent.test.ts +80 -0
- package/src/lib/palette-recent.ts +90 -0
- package/src/lib/palette.test.ts +365 -0
- package/src/lib/palette.ts +593 -0
- package/src/lib/pins.test.ts +139 -0
- package/src/lib/pins.ts +130 -0
- package/src/lib/problems.test.ts +112 -0
- package/src/lib/project-preview.ts +5 -0
- package/src/lib/proto-problems.test.ts +121 -0
- package/src/lib/proto-problems.ts +65 -0
- package/src/lib/queries.test.ts +113 -0
- package/src/lib/queries.ts +131 -0
- package/src/lib/registry.test.ts +219 -0
- package/src/lib/registry.ts +208 -0
- package/src/lib/scenarios.ts +547 -0
- package/src/lib/setup-info.test.ts +199 -0
- package/src/lib/setup-info.ts +415 -0
- package/src/lib/shape.test.ts +289 -0
- package/src/lib/shape.ts +454 -0
- package/src/lib/sidebar-prefs.test.ts +32 -0
- package/src/lib/sidebar-prefs.ts +75 -0
- package/src/lib/source-code.test.ts +159 -0
- package/src/lib/source-code.ts +286 -0
- package/src/lib/source-doc.test.ts +131 -0
- package/src/lib/source-doc.ts +103 -0
- package/src/lib/source-highlight.test.ts +29 -0
- package/src/lib/source-highlight.ts +24 -0
- package/src/lib/source-link.test.ts +153 -0
- package/src/lib/source-link.ts +201 -0
- package/src/lib/spec-files.test.ts +24 -0
- package/src/lib/spec-files.ts +23 -0
- package/src/lib/table-directive.test.ts +139 -0
- package/src/lib/table-directive.ts +106 -0
- package/src/lib/tech.test.ts +120 -0
- package/src/lib/tech.ts +421 -0
- package/src/lib/terms.test.ts +67 -0
- package/src/lib/terms.ts +179 -0
- package/src/lib/wire-problems.test.ts +263 -0
- package/src/lib/wire-problems.ts +281 -0
- package/src/likec4/C4View.tsx +55 -0
- package/src/likec4/CanvasBridge.tsx +187 -0
- package/src/likec4/FlowView.tsx +235 -0
- package/src/likec4/InteractiveView.tsx +144 -0
- package/src/likec4/canvas-viewport.test.ts +149 -0
- package/src/likec4/canvas-viewport.ts +135 -0
- package/src/likec4/flow-edges.test.ts +51 -0
- package/src/likec4/flow-edges.ts +89 -0
- package/src/likec4/frame-theme.test.ts +76 -0
- package/src/likec4/frame-theme.ts +101 -0
- package/src/likec4/highlight-css.test.ts +49 -0
- package/src/likec4/highlight-css.ts +52 -0
- package/src/likec4/ids.test.ts +151 -0
- package/src/likec4/ids.ts +115 -0
- package/src/likec4/levels.tsx +70 -0
- package/src/likec4/mapping.test.ts +66 -0
- package/src/likec4/mapping.ts +54 -0
- package/src/likec4/reserved.json +141 -0
- package/src/likec4/view-index.ts +78 -0
- package/src/main.tsx +13 -0
- package/src/map/ContextMapGraph.tsx +274 -0
- package/src/map/ContextNode.tsx +70 -0
- package/src/map/FloatingEdge.tsx +86 -0
- package/src/merge.test.ts +1038 -0
- package/src/merge.ts +1004 -0
- package/src/pages/AdrDetail.tsx +242 -0
- package/src/pages/AdrIndex.tsx +114 -0
- package/src/pages/AggregatePage.tsx +727 -0
- package/src/pages/BlockPage.tsx +478 -0
- package/src/pages/CatalogFailure.tsx +111 -0
- package/src/pages/Changes.tsx +313 -0
- package/src/pages/ContextMap.tsx +534 -0
- package/src/pages/ContextPage.tsx +346 -0
- package/src/pages/EnumPage.tsx +282 -0
- package/src/pages/EventPage.tsx +686 -0
- package/src/pages/ExternalPage.tsx +204 -0
- package/src/pages/FlowDetail.tsx +663 -0
- package/src/pages/FlowIndex.tsx +303 -0
- package/src/pages/GraphPage.tsx +217 -0
- package/src/pages/Language.tsx +257 -0
- package/src/pages/ModulePage.tsx +366 -0
- package/src/pages/NotFound.tsx +54 -0
- package/src/pages/Overview.tsx +344 -0
- package/src/pages/Problems.tsx +360 -0
- package/src/pages/RegistryIndex.tsx +118 -0
- package/src/pages/ServicePage.tsx +746 -0
- package/src/pages/Settings.tsx +975 -0
- package/src/pages/StorePage.tsx +114 -0
- package/src/peek/PeekCard.test.ts +35 -0
- package/src/peek/PeekCard.tsx +118 -0
- package/src/peek/PeekLayer.tsx +147 -0
- package/src/peek/model.test.ts +156 -0
- package/src/peek/model.ts +253 -0
- package/src/routes.test.ts +222 -0
- package/src/routes.ts +449 -0
- package/src/selection/DetailPanel.tsx +1407 -0
- package/src/selection/hash.test.ts +73 -0
- package/src/selection/hash.ts +58 -0
- package/src/selection/model.test.ts +199 -0
- package/src/selection/model.ts +473 -0
- package/src/selection/pages.test.ts +115 -0
- package/src/selection/pages.ts +268 -0
- package/src/selection/store.test.ts +62 -0
- package/src/selection/store.ts +46 -0
- package/src/selection/sync.tsx +99 -0
- package/src/table/ColumnsMenu.tsx +72 -0
- package/src/table/DataTable.tsx +780 -0
- package/src/table/Facets.tsx +54 -0
- package/src/table/GroupMenu.tsx +69 -0
- package/src/table/Toolbar.tsx +190 -0
- package/src/table/cells.tsx +117 -0
- package/src/table/compare.test.ts +117 -0
- package/src/table/compare.ts +196 -0
- package/src/table/export.test.ts +88 -0
- package/src/table/export.ts +66 -0
- package/src/table/facet-groups.test.ts +74 -0
- package/src/table/facet-groups.ts +56 -0
- package/src/table/grouping.test.ts +84 -0
- package/src/table/grouping.ts +82 -0
- package/src/table/infer.test.ts +57 -0
- package/src/table/infer.ts +50 -0
- package/src/table/persist.test.ts +76 -0
- package/src/table/persist.ts +112 -0
- package/src/table/sort-url.test.ts +75 -0
- package/src/table/sort-url.ts +52 -0
- package/src/table/types.ts +80 -0
- package/src/table/useTableState.ts +220 -0
- package/src/test-catalog.ts +7 -0
- package/src/testing/estate/data__catalog.json +2847 -0
- package/src/testing/estate/data__flows.json +1724 -0
- package/src/testing/estate/examples__auth__portolan__api.json +212 -0
- package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
- package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
- package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
- package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
- package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
- package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
- package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
- package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
- package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
- package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
- package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
- package/src/testing/estate.ts +49 -0
- package/src/trail/Trail.tsx +81 -0
- package/src/trail/model.test.ts +157 -0
- package/src/trail/model.ts +189 -0
- package/src/trail/record.tsx +32 -0
- package/src/trail/store.test.ts +66 -0
- package/src/trail/store.ts +102 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +171 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// The fetcher, against a recorded registry. NEVER against a live one: a test
|
|
4
|
+
// that reaches buf.build would fail on a train, and the whole point of this
|
|
5
|
+
// plugin's design is that the build does not depend on a registry being up.
|
|
6
|
+
|
|
7
|
+
import (
|
|
8
|
+
"encoding/base64"
|
|
9
|
+
"encoding/json"
|
|
10
|
+
"net/http"
|
|
11
|
+
"net/http/httptest"
|
|
12
|
+
"os"
|
|
13
|
+
"path/filepath"
|
|
14
|
+
"sort"
|
|
15
|
+
"strings"
|
|
16
|
+
"testing"
|
|
17
|
+
|
|
18
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const (
|
|
22
|
+
moduleName = "buf.build/acme/shop"
|
|
23
|
+
pinned = "c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
var protoFile = "syntax = \"proto3\";\n\npackage shop.v1;\n\nservice Orders {\n rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse);\n}\n\nmessage PlaceOrderRequest { string customer_id = 1; }\nmessage PlaceOrderResponse { string order_id = 1; }\n"
|
|
27
|
+
|
|
28
|
+
// registry serves the two calls this plugin makes, from recorded shapes.
|
|
29
|
+
func registry(t *testing.T, seen *[]string) *httptest.Server {
|
|
30
|
+
t.Helper()
|
|
31
|
+
|
|
32
|
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
33
|
+
if seen != nil {
|
|
34
|
+
*seen = append(*seen, r.Header.Get("Authorization"))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
w.Header().Set("Content-Type", "application/json")
|
|
38
|
+
|
|
39
|
+
switch r.URL.Path {
|
|
40
|
+
case downloadMethod:
|
|
41
|
+
var req downloadRequest
|
|
42
|
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
43
|
+
t.Errorf("the download request is not json: %v", err)
|
|
44
|
+
}
|
|
45
|
+
// The wire shape, asserted here rather than assumed: a request that
|
|
46
|
+
// stopped naming the module would still "pass" every other test.
|
|
47
|
+
if len(req.Values) != 1 || req.Values[0].ResourceRef.Name == nil {
|
|
48
|
+
t.Errorf("download request: %+v", req)
|
|
49
|
+
} else {
|
|
50
|
+
name := req.Values[0].ResourceRef.Name
|
|
51
|
+
if name.Owner != "acme" || name.Module != "shop" {
|
|
52
|
+
t.Errorf("download names %s/%s", name.Owner, name.Module)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_, _ = w.Write([]byte(`{"contents":[{"commit":{"id":"` + pinned +
|
|
57
|
+
`","digest":{"type":"DIGEST_TYPE_B5","value":"` +
|
|
58
|
+
base64.StdEncoding.EncodeToString([]byte{0xde, 0xad, 0xbe, 0xef}) +
|
|
59
|
+
`"}},"files":[{"path":"shop/v1/orders.proto","content":"` +
|
|
60
|
+
base64.StdEncoding.EncodeToString([]byte(protoFile)) + `"}]}]}`))
|
|
61
|
+
|
|
62
|
+
case commitsMethod:
|
|
63
|
+
_, _ = w.Write([]byte(`{"commits":[{"id":"` + pinned +
|
|
64
|
+
`","digest":{"type":"DIGEST_TYPE_B5","value":"` +
|
|
65
|
+
base64.StdEncoding.EncodeToString([]byte{0xde, 0xad, 0xbe, 0xef}) + `"}}]}`))
|
|
66
|
+
|
|
67
|
+
default:
|
|
68
|
+
w.WriteHeader(http.StatusNotFound)
|
|
69
|
+
_, _ = w.Write([]byte(`{"code":"not_found","message":"no such method"}`))
|
|
70
|
+
}
|
|
71
|
+
}))
|
|
72
|
+
t.Cleanup(server.Close)
|
|
73
|
+
|
|
74
|
+
return server
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func options(t *testing.T, base, cache string) Options {
|
|
78
|
+
t.Helper()
|
|
79
|
+
|
|
80
|
+
return Options{
|
|
81
|
+
Registry: base,
|
|
82
|
+
Cache: cache,
|
|
83
|
+
Modules: []Module{{Module: moduleName, Commit: pinned}},
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// A test must never inherit a real credential or a real offline setting from
|
|
88
|
+
// whoever is running it.
|
|
89
|
+
func isolate(t *testing.T) {
|
|
90
|
+
t.Helper()
|
|
91
|
+
t.Setenv(TokenEnv, "")
|
|
92
|
+
t.Setenv(OfflineEnv, "")
|
|
93
|
+
t.Setenv("CI", "")
|
|
94
|
+
t.Setenv("HOME", t.TempDir())
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func names(resp plugin.Response) []string {
|
|
98
|
+
out := make([]string, 0, len(resp.Files))
|
|
99
|
+
for _, f := range resp.Files {
|
|
100
|
+
out = append(out, f.Name)
|
|
101
|
+
}
|
|
102
|
+
sort.Strings(out)
|
|
103
|
+
|
|
104
|
+
return out
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func contentsOf(resp plugin.Response, name string) string {
|
|
108
|
+
for _, f := range resp.Files {
|
|
109
|
+
if f.Name == name {
|
|
110
|
+
return f.Contents
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return ""
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// write puts a response into a directory the way the host's apply() would, so
|
|
118
|
+
// the next run can replay it.
|
|
119
|
+
func write(t *testing.T, dir string, resp plugin.Response) {
|
|
120
|
+
t.Helper()
|
|
121
|
+
|
|
122
|
+
for _, f := range resp.Files {
|
|
123
|
+
at := filepath.Join(dir, filepath.FromSlash(f.Name))
|
|
124
|
+
if err := os.MkdirAll(filepath.Dir(at), 0o755); err != nil {
|
|
125
|
+
t.Fatal(err)
|
|
126
|
+
}
|
|
127
|
+
if err := os.WriteFile(at, []byte(f.Contents), 0o644); err != nil {
|
|
128
|
+
t.Fatal(err)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func TestFetchWritesProtosAndALock(t *testing.T) {
|
|
134
|
+
isolate(t)
|
|
135
|
+
server := registry(t, nil)
|
|
136
|
+
cache := t.TempDir()
|
|
137
|
+
|
|
138
|
+
resp, err := fetch(options(t, server.URL, cache))
|
|
139
|
+
if err != nil {
|
|
140
|
+
t.Fatal(err)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Each module in its own directory with its own lock, so extract-proto
|
|
144
|
+
// finds the lock beside the files it is already reading.
|
|
145
|
+
want := []string{"acme/shop/bsr.lock.json", "acme/shop/shop/v1/orders.proto"}
|
|
146
|
+
if got := names(resp); !equal(got, want) {
|
|
147
|
+
t.Fatalf("files: %v, want %v", got, want)
|
|
148
|
+
}
|
|
149
|
+
if contentsOf(resp, "acme/shop/shop/v1/orders.proto") != protoFile {
|
|
150
|
+
t.Error("the proto came back changed")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var lock Lock
|
|
154
|
+
if err := json.Unmarshal([]byte(contentsOf(resp, "acme/shop/bsr.lock.json")), &lock); err != nil {
|
|
155
|
+
t.Fatal(err)
|
|
156
|
+
}
|
|
157
|
+
if len(lock.Modules) != 1 || lock.Modules[0].Commit != pinned {
|
|
158
|
+
t.Fatalf("lock: %+v", lock)
|
|
159
|
+
}
|
|
160
|
+
if lock.Modules[0].Digest != "b5:deadbeef" {
|
|
161
|
+
t.Errorf("digest: %q, want the b5:hex form buf writes", lock.Modules[0].Digest)
|
|
162
|
+
}
|
|
163
|
+
if len(lock.Modules[0].Files) != 1 || lock.Modules[0].Files[0].SHA256 != digestOf([]byte(protoFile)) {
|
|
164
|
+
t.Errorf("the lock does not record what was written: %+v", lock.Modules[0].Files)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// THE CI-GREEN GUARANTEE, TESTED.
|
|
169
|
+
//
|
|
170
|
+
// An offline run over a committed copy must produce output byte-identical to
|
|
171
|
+
// the online run that wrote it, or `gen:check` reports drift on every build
|
|
172
|
+
// that cannot reach the registry - which is every build in CI.
|
|
173
|
+
func TestOfflineReplayIsByteIdentical(t *testing.T) {
|
|
174
|
+
isolate(t)
|
|
175
|
+
server := registry(t, nil)
|
|
176
|
+
cache := t.TempDir()
|
|
177
|
+
|
|
178
|
+
online, err := fetch(options(t, server.URL, cache))
|
|
179
|
+
if err != nil {
|
|
180
|
+
t.Fatal(err)
|
|
181
|
+
}
|
|
182
|
+
write(t, cache, online)
|
|
183
|
+
|
|
184
|
+
t.Setenv(OfflineEnv, "1")
|
|
185
|
+
replayed, err := fetch(options(t, server.URL, cache))
|
|
186
|
+
if err != nil {
|
|
187
|
+
t.Fatal(err)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if !equal(names(online), names(replayed)) {
|
|
191
|
+
t.Fatalf("offline emitted %v, online emitted %v", names(replayed), names(online))
|
|
192
|
+
}
|
|
193
|
+
for _, f := range online.Files {
|
|
194
|
+
if contentsOf(replayed, f.Name) != f.Contents {
|
|
195
|
+
t.Errorf("%s differs between an online and an offline run", f.Name)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if len(replayed.Warnings()) != 1 || !strings.Contains(replayed.Warnings()[0].Message, "not fetched") {
|
|
199
|
+
t.Errorf("an offline run said nothing about not fetching: %+v", replayed.Warnings())
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// CI is set on every runner, so a build there never opens a socket even if
|
|
204
|
+
// nobody remembered to set PORTOLAN_OFFLINE.
|
|
205
|
+
func TestCiImpliesOffline(t *testing.T) {
|
|
206
|
+
isolate(t)
|
|
207
|
+
cache := t.TempDir()
|
|
208
|
+
server := registry(t, nil)
|
|
209
|
+
|
|
210
|
+
online, err := fetch(options(t, server.URL, cache))
|
|
211
|
+
if err != nil {
|
|
212
|
+
t.Fatal(err)
|
|
213
|
+
}
|
|
214
|
+
write(t, cache, online)
|
|
215
|
+
|
|
216
|
+
t.Setenv("CI", "true")
|
|
217
|
+
if _, err := fetch(options(t, server.URL, cache)); err != nil {
|
|
218
|
+
t.Fatalf("a CI run over a committed copy failed: %v", err)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// A vendored copy someone edited by hand stops matching its digest. That is
|
|
223
|
+
// exactly the drift docs/adr/org.0001.md wants visible, so it is reported by
|
|
224
|
+
// path rather than quietly overwritten.
|
|
225
|
+
func TestEditedCacheIsReportedByPath(t *testing.T) {
|
|
226
|
+
isolate(t)
|
|
227
|
+
server := registry(t, nil)
|
|
228
|
+
cache := t.TempDir()
|
|
229
|
+
|
|
230
|
+
online, err := fetch(options(t, server.URL, cache))
|
|
231
|
+
if err != nil {
|
|
232
|
+
t.Fatal(err)
|
|
233
|
+
}
|
|
234
|
+
write(t, cache, online)
|
|
235
|
+
|
|
236
|
+
at := filepath.Join(cache, "acme", "shop", "shop", "v1", "orders.proto")
|
|
237
|
+
if err := os.WriteFile(at, []byte("// edited by hand\n"), 0o644); err != nil {
|
|
238
|
+
t.Fatal(err)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
t.Setenv(OfflineEnv, "1")
|
|
242
|
+
_, err = fetch(options(t, server.URL, cache))
|
|
243
|
+
if err == nil {
|
|
244
|
+
t.Fatal("an edited vendored copy was accepted")
|
|
245
|
+
}
|
|
246
|
+
if !strings.Contains(err.Error(), "shop/v1/orders.proto") {
|
|
247
|
+
t.Errorf("the error does not name the edited file: %v", err)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// RULE 3. The host deletes files a step stops naming, so a failed fetch with
|
|
252
|
+
// nothing to fall back to must be a non-zero exit and NOT a short file list -
|
|
253
|
+
// dropping a repository's vendored protos because a laptop went offline is
|
|
254
|
+
// worse than a red build.
|
|
255
|
+
func TestOfflineWithNoCacheFailsRatherThanEmittingNothing(t *testing.T) {
|
|
256
|
+
isolate(t)
|
|
257
|
+
t.Setenv(OfflineEnv, "1")
|
|
258
|
+
|
|
259
|
+
resp, err := fetch(options(t, "http://127.0.0.1:1", t.TempDir()))
|
|
260
|
+
if err == nil {
|
|
261
|
+
t.Fatalf("an empty cache produced a response instead of an error: %v", names(resp))
|
|
262
|
+
}
|
|
263
|
+
if len(resp.Files) != 0 {
|
|
264
|
+
t.Errorf("a failed run emitted files: %v", names(resp))
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// The same rule when the registry is reachable but broken.
|
|
269
|
+
func TestFailedFetchWithNoCacheFails(t *testing.T) {
|
|
270
|
+
isolate(t)
|
|
271
|
+
broken := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
272
|
+
w.WriteHeader(http.StatusInternalServerError)
|
|
273
|
+
_, _ = w.Write([]byte(`{"code":"internal","message":"the registry is having a day"}`))
|
|
274
|
+
}))
|
|
275
|
+
t.Cleanup(broken.Close)
|
|
276
|
+
|
|
277
|
+
_, err := fetch(options(t, broken.URL, t.TempDir()))
|
|
278
|
+
if err == nil {
|
|
279
|
+
t.Fatal("a failing registry with no cache produced a response")
|
|
280
|
+
}
|
|
281
|
+
if !strings.Contains(err.Error(), "the registry is having a day") {
|
|
282
|
+
t.Errorf("the registry's own message did not survive: %v", err)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// A failing registry with a good copy in the tree is not a failure at all:
|
|
287
|
+
// the output is unchanged and the build stays green.
|
|
288
|
+
func TestFailedFetchFallsBackToTheCommittedCopy(t *testing.T) {
|
|
289
|
+
isolate(t)
|
|
290
|
+
cache := t.TempDir()
|
|
291
|
+
|
|
292
|
+
server := registry(t, nil)
|
|
293
|
+
online, err := fetch(options(t, server.URL, cache))
|
|
294
|
+
if err != nil {
|
|
295
|
+
t.Fatal(err)
|
|
296
|
+
}
|
|
297
|
+
write(t, cache, online)
|
|
298
|
+
|
|
299
|
+
broken := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
300
|
+
w.WriteHeader(http.StatusBadGateway)
|
|
301
|
+
}))
|
|
302
|
+
t.Cleanup(broken.Close)
|
|
303
|
+
|
|
304
|
+
fallback, err := fetch(options(t, broken.URL, cache))
|
|
305
|
+
if err != nil {
|
|
306
|
+
t.Fatalf("a good committed copy did not save the run: %v", err)
|
|
307
|
+
}
|
|
308
|
+
if !equal(names(fallback), names(online)) {
|
|
309
|
+
t.Errorf("the fallback emitted %v", names(fallback))
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Offline there is nothing to resolve a label against, so an unpinned module
|
|
314
|
+
// is refused by name rather than guessed at.
|
|
315
|
+
func TestUnpinnedModuleIsRefusedOffline(t *testing.T) {
|
|
316
|
+
isolate(t)
|
|
317
|
+
t.Setenv(OfflineEnv, "1")
|
|
318
|
+
|
|
319
|
+
opts := options(t, "http://127.0.0.1:1", t.TempDir())
|
|
320
|
+
opts.Modules[0].Commit = ""
|
|
321
|
+
|
|
322
|
+
_, err := fetch(opts)
|
|
323
|
+
if err == nil {
|
|
324
|
+
t.Fatal("an unpinned module was accepted offline")
|
|
325
|
+
}
|
|
326
|
+
if !strings.Contains(err.Error(), moduleName) {
|
|
327
|
+
t.Errorf("the error does not name the module: %v", err)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Online it still works, but it is said out loud: two runs a day apart would
|
|
332
|
+
// otherwise produce two different trees from one manifest.
|
|
333
|
+
func TestUnpinnedModuleWarnsOnline(t *testing.T) {
|
|
334
|
+
isolate(t)
|
|
335
|
+
server := registry(t, nil)
|
|
336
|
+
|
|
337
|
+
opts := options(t, server.URL, t.TempDir())
|
|
338
|
+
opts.Modules[0].Commit = ""
|
|
339
|
+
|
|
340
|
+
resp, err := fetch(opts)
|
|
341
|
+
if err != nil {
|
|
342
|
+
t.Fatal(err)
|
|
343
|
+
}
|
|
344
|
+
if len(resp.Warnings()) == 0 || !strings.Contains(resp.Warnings()[0].Message, "not pinned") {
|
|
345
|
+
t.Errorf("an unpinned module was resolved silently: %+v", resp.Warnings())
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// A pin the tree does not hold is a fetch waiting to happen, not a copy to
|
|
350
|
+
// serve. Serving the old one would make `gen:check` pass on the wrong bytes.
|
|
351
|
+
func TestCacheAtTheWrongCommitIsRefused(t *testing.T) {
|
|
352
|
+
isolate(t)
|
|
353
|
+
cache := t.TempDir()
|
|
354
|
+
|
|
355
|
+
server := registry(t, nil)
|
|
356
|
+
online, err := fetch(options(t, server.URL, cache))
|
|
357
|
+
if err != nil {
|
|
358
|
+
t.Fatal(err)
|
|
359
|
+
}
|
|
360
|
+
write(t, cache, online)
|
|
361
|
+
|
|
362
|
+
t.Setenv(OfflineEnv, "1")
|
|
363
|
+
opts := options(t, server.URL, cache)
|
|
364
|
+
opts.Modules[0].Commit = "0000000000000000000000000000000f"
|
|
365
|
+
|
|
366
|
+
if _, err := fetch(opts); err == nil {
|
|
367
|
+
t.Fatal("a copy at another commit was served for a different pin")
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// A credential changes whether the fetch succeeds, never what it says. If a
|
|
372
|
+
// token could reach the output, a public copy and a private one would differ.
|
|
373
|
+
func TestOutputIsIdenticalWithAndWithoutAToken(t *testing.T) {
|
|
374
|
+
isolate(t)
|
|
375
|
+
|
|
376
|
+
var seen []string
|
|
377
|
+
server := registry(t, &seen)
|
|
378
|
+
|
|
379
|
+
anonymous, err := fetch(options(t, server.URL, t.TempDir()))
|
|
380
|
+
if err != nil {
|
|
381
|
+
t.Fatal(err)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
t.Setenv(TokenEnv, "a-real-looking-secret")
|
|
385
|
+
authorised, err := fetch(options(t, server.URL, t.TempDir()))
|
|
386
|
+
if err != nil {
|
|
387
|
+
t.Fatal(err)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
for _, f := range anonymous.Files {
|
|
391
|
+
if contentsOf(authorised, f.Name) != f.Contents {
|
|
392
|
+
t.Errorf("%s differs depending on whether a token was set", f.Name)
|
|
393
|
+
}
|
|
394
|
+
if strings.Contains(contentsOf(authorised, f.Name), "a-real-looking-secret") {
|
|
395
|
+
t.Fatalf("the token reached %s", f.Name)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if len(seen) < 2 || seen[0] != "" {
|
|
400
|
+
t.Errorf("the anonymous request sent an Authorization header: %q", seen[0])
|
|
401
|
+
}
|
|
402
|
+
if seen[len(seen)-1] != "Bearer a-real-looking-secret" {
|
|
403
|
+
t.Errorf("the token was not sent: %q", seen[len(seen)-1])
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// The environment is where a credential comes from, and portolan.json - which
|
|
408
|
+
// is committed - is where it must never be.
|
|
409
|
+
func TestTokenPrecedence(t *testing.T) {
|
|
410
|
+
isolate(t)
|
|
411
|
+
|
|
412
|
+
home := t.TempDir()
|
|
413
|
+
t.Setenv("HOME", home)
|
|
414
|
+
netrc := "machine buf.build login someone password from-netrc\n"
|
|
415
|
+
if err := os.WriteFile(filepath.Join(home, ".netrc"), []byte(netrc), 0o600); err != nil {
|
|
416
|
+
t.Fatal(err)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if got := token("buf.build"); got != "from-netrc" {
|
|
420
|
+
t.Errorf("with no env var, the netrc password should be used: %q", got)
|
|
421
|
+
}
|
|
422
|
+
if got := token("other.example"); got != "" {
|
|
423
|
+
t.Errorf("a netrc entry for another host was used: %q", got)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
t.Setenv(TokenEnv, "from-env")
|
|
427
|
+
if got := token("buf.build"); got != "from-env" {
|
|
428
|
+
t.Errorf("the environment should win over the netrc: %q", got)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// A cache directory is not optional: without one an offline run has nowhere to
|
|
433
|
+
// replay from, and the failure would surface as a mysteriously empty step.
|
|
434
|
+
func TestCacheIsRequired(t *testing.T) {
|
|
435
|
+
isolate(t)
|
|
436
|
+
|
|
437
|
+
opts := options(t, "http://127.0.0.1:1", "")
|
|
438
|
+
if _, err := fetch(opts); err == nil || !strings.Contains(err.Error(), "cache") {
|
|
439
|
+
t.Errorf("a missing cache was not refused clearly: %v", err)
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
func TestModuleNameMustHaveThreeParts(t *testing.T) {
|
|
444
|
+
isolate(t)
|
|
445
|
+
|
|
446
|
+
opts := options(t, "http://127.0.0.1:1", t.TempDir())
|
|
447
|
+
opts.Modules[0].Module = "acme/shop"
|
|
448
|
+
|
|
449
|
+
if _, err := fetch(opts); err == nil || !strings.Contains(err.Error(), "module name") {
|
|
450
|
+
t.Errorf("a two-part module name was not refused: %v", err)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
func equal(a, b []string) bool {
|
|
455
|
+
if len(a) != len(b) {
|
|
456
|
+
return false
|
|
457
|
+
}
|
|
458
|
+
for i := range a {
|
|
459
|
+
if a[i] != b[i] {
|
|
460
|
+
return false
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return true
|
|
465
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// What was fetched, so it can be checked without fetching again.
|
|
4
|
+
//
|
|
5
|
+
// One lock per module directory rather than one for the whole step, so a
|
|
6
|
+
// vendored module is self-describing: the protos and the statement of where
|
|
7
|
+
// they came from sit together, and extract-proto finds the lock by looking
|
|
8
|
+
// beside the files it is already reading.
|
|
9
|
+
|
|
10
|
+
import (
|
|
11
|
+
"crypto/sha256"
|
|
12
|
+
"encoding/hex"
|
|
13
|
+
"encoding/json"
|
|
14
|
+
"sort"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// LockName is the file written beside each module's protos.
|
|
18
|
+
const LockName = "bsr.lock.json"
|
|
19
|
+
|
|
20
|
+
type Lock struct {
|
|
21
|
+
Modules []LockModule `json:"modules"`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type LockModule struct {
|
|
25
|
+
Module string `json:"module"`
|
|
26
|
+
|
|
27
|
+
// Commit is what was actually fetched. A BSR commit is immutable, so a
|
|
28
|
+
// pinned download is byte-reproducible and this is what makes replaying
|
|
29
|
+
// from disk equivalent to fetching again.
|
|
30
|
+
Commit string `json:"commit"`
|
|
31
|
+
Digest string `json:"digest,omitempty"`
|
|
32
|
+
|
|
33
|
+
Deps []string `json:"deps,omitempty"`
|
|
34
|
+
Files []LockEntry `json:"files"`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type LockEntry struct {
|
|
38
|
+
Path string `json:"path"`
|
|
39
|
+
|
|
40
|
+
// SHA256 is over the bytes as written. It is what turns a cache into
|
|
41
|
+
// something checkable: a vendored copy someone edited by hand stops
|
|
42
|
+
// matching, which is exactly the drift docs/adr/org.0001.md wants visible.
|
|
43
|
+
SHA256 string `json:"sha256"`
|
|
44
|
+
Size int `json:"size"`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func digestOf(content []byte) string {
|
|
48
|
+
sum := sha256.Sum256(content)
|
|
49
|
+
|
|
50
|
+
return hex.EncodeToString(sum[:])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// encode writes the lock the way every generated file in this repository is
|
|
54
|
+
// written: sorted, indented, newline-terminated. It is committed and compared
|
|
55
|
+
// by `gen:check`, so an unstable order would rewrite it on every run.
|
|
56
|
+
func (l Lock) encode() (string, error) {
|
|
57
|
+
for i := range l.Modules {
|
|
58
|
+
sort.Slice(l.Modules[i].Files, func(a, b int) bool {
|
|
59
|
+
return l.Modules[i].Files[a].Path < l.Modules[i].Files[b].Path
|
|
60
|
+
})
|
|
61
|
+
sort.Strings(l.Modules[i].Deps)
|
|
62
|
+
}
|
|
63
|
+
sort.Slice(l.Modules, func(a, b int) bool { return l.Modules[a].Module < l.Modules[b].Module })
|
|
64
|
+
|
|
65
|
+
out, err := json.MarshalIndent(l, "", " ")
|
|
66
|
+
if err != nil {
|
|
67
|
+
return "", err
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return string(out) + "\n", nil
|
|
71
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Package main is portolan-fetch-bsr: a schema registry on one side, .proto
|
|
2
|
+
// files committed to this repository on the other.
|
|
3
|
+
//
|
|
4
|
+
// It is the ONE plugin here that opens a socket, and it is split off from
|
|
5
|
+
// portolan-extract-proto for exactly that reason. Extraction stays a pure
|
|
6
|
+
// function of the tree; fetching is the step that can fail, need a credential,
|
|
7
|
+
// or come back with something different than it did yesterday. Keeping them
|
|
8
|
+
// apart is what lets CI verify an estate whose schemas live in a registry it
|
|
9
|
+
// never talks to.
|
|
10
|
+
//
|
|
11
|
+
// The fetched protos are the plugin's Response.Files, not a side effect. The
|
|
12
|
+
// host writes them, so they get a manifest entry, are compared by `gen:check`
|
|
13
|
+
// like any other generated file, and are removed when the step stops naming
|
|
14
|
+
// them. Refreshing a pin then produces ONE pull request holding the pin bump,
|
|
15
|
+
// the proto diff, the lock diff and the fragment diff - which is the review
|
|
16
|
+
// worth having.
|
|
17
|
+
//
|
|
18
|
+
// Wired into portolan.json for the order contract oms publishes. The shape of
|
|
19
|
+
// the two steps, fetch and then read:
|
|
20
|
+
//
|
|
21
|
+
// {
|
|
22
|
+
// "plugins": [
|
|
23
|
+
// { "name": "bsr", "process": { "command": "go", "args": ["run", "./plugins/fetch-bsr"] } },
|
|
24
|
+
// { "name": "proto", "process": { "command": "go", "args": ["run", "./plugins/extract-proto"] } }
|
|
25
|
+
// ],
|
|
26
|
+
// "extract": [
|
|
27
|
+
// {
|
|
28
|
+
// "plugin": "bsr",
|
|
29
|
+
// "in": "examples/shop",
|
|
30
|
+
// "out": "examples/shop/vendor/proto",
|
|
31
|
+
// "options": {
|
|
32
|
+
// "cache": "examples/shop/vendor/proto",
|
|
33
|
+
// "modules": [
|
|
34
|
+
// { "module": "buf.build/acme/shop", "commit": "c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6" }
|
|
35
|
+
// ]
|
|
36
|
+
// }
|
|
37
|
+
// },
|
|
38
|
+
// {
|
|
39
|
+
// "plugin": "proto",
|
|
40
|
+
// "in": "examples/shop",
|
|
41
|
+
// "out": "examples/shop/portolan",
|
|
42
|
+
// "options": {
|
|
43
|
+
// "context": "shop",
|
|
44
|
+
// "service": "oms",
|
|
45
|
+
// "paths": ["vendor/proto/acme/shop"],
|
|
46
|
+
// "out": "proto.json"
|
|
47
|
+
// }
|
|
48
|
+
// }
|
|
49
|
+
// ]
|
|
50
|
+
// }
|
|
51
|
+
//
|
|
52
|
+
// The bsr step is declared FIRST: extract steps run in list order, so its
|
|
53
|
+
// protos and locks are on disk by the time the proto step reads them.
|
|
54
|
+
//
|
|
55
|
+
// In CI, set PORTOLAN_OFFLINE=1. The step then replays the committed copies,
|
|
56
|
+
// checks them against their locks and emits an identical file list, so a fork's
|
|
57
|
+
// pull request needs no secret and the registry being down cannot turn the tree
|
|
58
|
+
// red.
|
|
59
|
+
package main
|
|
60
|
+
|
|
61
|
+
import (
|
|
62
|
+
"fmt"
|
|
63
|
+
"io"
|
|
64
|
+
"os"
|
|
65
|
+
|
|
66
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
// Options are what the manifest tells the fetcher.
|
|
70
|
+
//
|
|
71
|
+
// No token, ever. portolan.json is committed; the credential comes from the
|
|
72
|
+
// environment, and only auth.go looks at it.
|
|
73
|
+
type Options struct {
|
|
74
|
+
Modules []Module `json:"modules"`
|
|
75
|
+
|
|
76
|
+
// Registry overrides the host in each module name, for a self-hosted BSR.
|
|
77
|
+
Registry string `json:"registry,omitempty"`
|
|
78
|
+
|
|
79
|
+
// Cache is where the last run's files were written, repo-relative, and
|
|
80
|
+
// should be the same path as the step's `out`.
|
|
81
|
+
//
|
|
82
|
+
// Stated twice because a plugin is never told where its output goes. That
|
|
83
|
+
// is the whole shape of the protocol - a plugin returns files and the host
|
|
84
|
+
// decides what to do with them - and not an oversight to work around.
|
|
85
|
+
Cache string `json:"cache"`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type Module struct {
|
|
89
|
+
// Module is "<registry>/<owner>/<name>", e.g. "buf.build/acme/shop".
|
|
90
|
+
Module string `json:"module"`
|
|
91
|
+
|
|
92
|
+
// Commit pins it. A BSR commit is immutable, so a pinned download is
|
|
93
|
+
// byte-reproducible - which is the only reason replaying from disk is
|
|
94
|
+
// equivalent to fetching again. Leaving it out is allowed and warned about;
|
|
95
|
+
// offline it is refused, because there is nothing to replay against.
|
|
96
|
+
Commit string `json:"commit,omitempty"`
|
|
97
|
+
|
|
98
|
+
// Ref is the label to resolve when there is no commit. Defaults to "main".
|
|
99
|
+
Ref string `json:"ref,omitempty"`
|
|
100
|
+
|
|
101
|
+
// Paths narrows the download to the directories actually used, which is how
|
|
102
|
+
// a vendored copy stays small.
|
|
103
|
+
Paths []string `json:"paths,omitempty"`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func main() {
|
|
107
|
+
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
108
|
+
fmt.Fprintln(os.Stderr, "portolan-fetch-bsr:", err)
|
|
109
|
+
os.Exit(1)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
func run(stdin io.Reader, stdout io.Writer) error {
|
|
114
|
+
return plugin.Serve(stdin, stdout, descriptor(), func(_ plugin.Request, opts Options) (plugin.Response, error) {
|
|
115
|
+
return fetch(opts)
|
|
116
|
+
})
|
|
117
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"required": ["modules", "cache"],
|
|
5
|
+
"properties": {
|
|
6
|
+
"modules": {
|
|
7
|
+
"type": "array",
|
|
8
|
+
"minItems": 1,
|
|
9
|
+
"description": "The modules to fetch, each pinned to a commit.",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"required": ["module"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"module": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "The module's registry-global name: registry, owner and name.",
|
|
18
|
+
"examples": ["buf.build/shortlink-org/portolan-shop-order"]
|
|
19
|
+
},
|
|
20
|
+
"commit": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "The commit to fetch. A BSR commit is immutable, so a pinned fetch is byte-reproducible, which is what lets an offline run replay it from the tree. Left out, `ref` is resolved online and warned about, and refused offline."
|
|
23
|
+
},
|
|
24
|
+
"ref": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "The label to resolve when there is no commit. Defaults to main."
|
|
27
|
+
},
|
|
28
|
+
"paths": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": { "type": "string" },
|
|
31
|
+
"description": "Directories of the module to keep, relative to its root; the rest is left behind. Left out, the whole module.",
|
|
32
|
+
"examples": [["shop/v1"]]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"cache": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Where the last run's files were written, repo-relative: the same path as the step's out. An offline run replays what is there and checks it against the lock beside it."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|