@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,164 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"strings"
|
|
9
|
+
|
|
10
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// Where a forge looks for the file, in the order it looks.
|
|
15
|
+
var places = []string{"CODEOWNERS", ".github/CODEOWNERS", "docs/CODEOWNERS"}
|
|
16
|
+
|
|
17
|
+
// verify reads the file and answers with who owns each service.
|
|
18
|
+
//
|
|
19
|
+
// The catalog it is handed is the merged one, which is the only thing that
|
|
20
|
+
// knows where each service is: a rule is a path, a service has a path, and
|
|
21
|
+
// nothing else in the estate holds both. That is also why this is a verifier
|
|
22
|
+
// rather than an extractor - an extractor is handed a directory and would have
|
|
23
|
+
// to be told, service by service, in the manifest, what the catalog already
|
|
24
|
+
// says.
|
|
25
|
+
func verify(req plugin.Request, opts Options) (plugin.Response, error) {
|
|
26
|
+
b := &plugin.Builder{}
|
|
27
|
+
|
|
28
|
+
at, src, err := read(req.Input.Root, opts.File)
|
|
29
|
+
if err != nil {
|
|
30
|
+
return plugin.Response{}, err
|
|
31
|
+
}
|
|
32
|
+
rel := filepath.ToSlash(at)
|
|
33
|
+
|
|
34
|
+
parsed := parseCodeowners(src)
|
|
35
|
+
if parsed.Sections {
|
|
36
|
+
b.Warn(rel, "uses section headers, which change whose rule wins; owners are resolved the way a forge without sections resolves them, so a service may be missing one")
|
|
37
|
+
}
|
|
38
|
+
if len(parsed.Rules) == 0 {
|
|
39
|
+
b.Warn(rel, "states no rules; no service is owned by anybody")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Two different things a rule can fail at, told apart because the fix is
|
|
43
|
+
// different. A rule that matches nothing names a path the estate does not
|
|
44
|
+
// have - a typo, or a directory that moved. A rule that matches something
|
|
45
|
+
// and never wins is alive but unreachable: everything it covers is claimed
|
|
46
|
+
// further down, and deleting it would change nothing.
|
|
47
|
+
matched := make([]bool, len(parsed.Rules))
|
|
48
|
+
won := make([]bool, len(parsed.Rules))
|
|
49
|
+
contexts := []catalog.BoundedContext{}
|
|
50
|
+
|
|
51
|
+
for _, context := range req.Catalog.Contexts {
|
|
52
|
+
owned := []catalog.Service{}
|
|
53
|
+
|
|
54
|
+
for _, service := range context.Services {
|
|
55
|
+
if service.Path == "" {
|
|
56
|
+
b.Warn(service.ID, "has no path, so no rule can be matched against it")
|
|
57
|
+
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// LAST match wins, so the search runs backwards and stops at the
|
|
62
|
+
// first hit. A rule that matches and names nobody still wins:
|
|
63
|
+
// taking ownership back is what an empty rule is written for.
|
|
64
|
+
handles := []string{}
|
|
65
|
+
first := true
|
|
66
|
+
for i := len(parsed.Rules) - 1; i >= 0; i-- {
|
|
67
|
+
if !owns(parsed.Rules[i].Pattern, service.Path) {
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
matched[i] = true
|
|
71
|
+
if first {
|
|
72
|
+
first = false
|
|
73
|
+
won[i] = true
|
|
74
|
+
handles = append(handles, parsed.Rules[i].Owners...)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if len(handles) == 0 {
|
|
78
|
+
b.Warn(service.ID, "is owned by nobody in "+rel)
|
|
79
|
+
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
owned = append(owned, catalog.Service{
|
|
84
|
+
ID: service.ID,
|
|
85
|
+
Slug: service.Slug,
|
|
86
|
+
Provides: []catalog.RpcService{},
|
|
87
|
+
Consumes: []catalog.RpcCall{},
|
|
88
|
+
Aggregates: []catalog.Aggregate{},
|
|
89
|
+
Owners: handles,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if len(owned) == 0 {
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
contexts = append(contexts, catalog.BoundedContext{
|
|
97
|
+
ID: context.ID,
|
|
98
|
+
Slug: context.ID,
|
|
99
|
+
Services: owned,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// The half of this that is a verification rather than a reading: a rule
|
|
104
|
+
// nobody matched is a team that believes it owns something it does not,
|
|
105
|
+
// which is the failure a CODEOWNERS file has and never reports.
|
|
106
|
+
for i, rule := range parsed.Rules {
|
|
107
|
+
where := fmt.Sprintf("%s:%d", rel, rule.Line)
|
|
108
|
+
switch {
|
|
109
|
+
case !matched[i]:
|
|
110
|
+
b.Warn(where, rule.Pattern+" matches no service in the catalog")
|
|
111
|
+
case !won[i]:
|
|
112
|
+
b.Warn(where, rule.Pattern+" owns no service: everything it matches is claimed by a rule below it")
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fragment := catalog.Catalog{
|
|
117
|
+
GeneratedAt: req.Input.GeneratedAt,
|
|
118
|
+
Commit: req.Input.Commit,
|
|
119
|
+
Contexts: contexts,
|
|
120
|
+
Defs: map[string]catalog.TypeDef{},
|
|
121
|
+
Flows: []catalog.Flow{},
|
|
122
|
+
Adrs: []catalog.Adr{},
|
|
123
|
+
}
|
|
124
|
+
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
125
|
+
if err != nil {
|
|
126
|
+
return plugin.Response{}, err
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
out := opts.Out
|
|
130
|
+
if out == "" {
|
|
131
|
+
out = "owners.json"
|
|
132
|
+
}
|
|
133
|
+
b.File(out, string(encoded)+"\n")
|
|
134
|
+
|
|
135
|
+
return b.Response(), nil
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// read finds the file and returns where it was and what it says.
|
|
139
|
+
//
|
|
140
|
+
// A named file that is not there fails the run: somebody wrote that path in
|
|
141
|
+
// the manifest, and answering "nobody owns anything" to a typo is the kind of
|
|
142
|
+
// wrong that is only noticed when a page has been missing an owner for a
|
|
143
|
+
// month. Nothing named and nothing found is the same failure said differently,
|
|
144
|
+
// because a step declared at all is a step that expected a file.
|
|
145
|
+
func read(root, named string) (string, string, error) {
|
|
146
|
+
tried := places
|
|
147
|
+
if named != "" {
|
|
148
|
+
tried = []string{named}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for _, place := range tried {
|
|
152
|
+
at := filepath.Join(root, filepath.FromSlash(place))
|
|
153
|
+
src, err := os.ReadFile(at)
|
|
154
|
+
if err == nil {
|
|
155
|
+
return at, string(src), nil
|
|
156
|
+
}
|
|
157
|
+
if !os.IsNotExist(err) {
|
|
158
|
+
return "", "", err
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return "", "", fmt.Errorf("no CODEOWNERS under %s: looked for %s",
|
|
163
|
+
filepath.ToSlash(root), strings.Join(tried, ", "))
|
|
164
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
|
|
10
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// An estate of two services in two contexts, which is enough for every rule
|
|
15
|
+
// this has: one owned, one not, one rule that owns nothing.
|
|
16
|
+
func estate() catalog.Catalog {
|
|
17
|
+
return catalog.Catalog{
|
|
18
|
+
Contexts: []catalog.BoundedContext{
|
|
19
|
+
{
|
|
20
|
+
ID: "shop",
|
|
21
|
+
Slug: "shop",
|
|
22
|
+
Services: []catalog.Service{
|
|
23
|
+
{ID: "shop.oms", Slug: "oms", Name: "OMS", Path: "examples/shop/oms"},
|
|
24
|
+
{ID: "shop.cart", Slug: "cart", Name: "Cart", Path: "examples/shop/cart"},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
ID: "auth",
|
|
29
|
+
Slug: "auth",
|
|
30
|
+
Services: []catalog.Service{
|
|
31
|
+
{ID: "auth.auth", Slug: "auth", Name: "Auth", Path: "examples/auth"},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func request(t *testing.T, codeowners string) plugin.Request {
|
|
39
|
+
t.Helper()
|
|
40
|
+
root := t.TempDir()
|
|
41
|
+
if err := os.MkdirAll(filepath.Join(root, ".github"), 0o755); err != nil {
|
|
42
|
+
t.Fatal(err)
|
|
43
|
+
}
|
|
44
|
+
if err := os.WriteFile(filepath.Join(root, ".github", "CODEOWNERS"), []byte(codeowners), 0o644); err != nil {
|
|
45
|
+
t.Fatal(err)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return plugin.Request{
|
|
49
|
+
Input: plugin.Input{Root: root, Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"},
|
|
50
|
+
Catalog: estate(),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func fragmentOf(t *testing.T, resp plugin.Response) catalog.Catalog {
|
|
55
|
+
t.Helper()
|
|
56
|
+
if len(resp.Files) != 1 {
|
|
57
|
+
t.Fatalf("files = %d, want one fragment", len(resp.Files))
|
|
58
|
+
}
|
|
59
|
+
var parsed catalog.Catalog
|
|
60
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &parsed); err != nil {
|
|
61
|
+
t.Fatalf("fragment is not a catalog: %v", err)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return parsed
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func ownersOf(c catalog.Catalog, id string) []string {
|
|
68
|
+
for _, context := range c.Contexts {
|
|
69
|
+
for _, service := range context.Services {
|
|
70
|
+
if service.ID == id {
|
|
71
|
+
return service.Owners
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The rule that decides every answer: the file is read top to bottom and the
|
|
80
|
+
// LAST rule that matches wins, which is what lets a broad rule at the top be
|
|
81
|
+
// narrowed underneath.
|
|
82
|
+
func TestTheLastMatchingRuleWins(t *testing.T) {
|
|
83
|
+
resp, err := verify(request(t, `
|
|
84
|
+
* @acme/platform
|
|
85
|
+
examples/shop @acme/shop-team
|
|
86
|
+
examples/shop/oms @acme/oms-team
|
|
87
|
+
`), Options{})
|
|
88
|
+
if err != nil {
|
|
89
|
+
t.Fatal(err)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fragment := fragmentOf(t, resp)
|
|
93
|
+
if got := ownersOf(fragment, "shop.oms"); len(got) != 1 || got[0] != "@acme/oms-team" {
|
|
94
|
+
t.Errorf("shop.oms owners = %v, want the narrowest rule's", got)
|
|
95
|
+
}
|
|
96
|
+
if got := ownersOf(fragment, "shop.cart"); len(got) != 1 || got[0] != "@acme/shop-team" {
|
|
97
|
+
t.Errorf("shop.cart owners = %v", got)
|
|
98
|
+
}
|
|
99
|
+
if got := ownersOf(fragment, "auth.auth"); len(got) != 1 || got[0] != "@acme/platform" {
|
|
100
|
+
t.Errorf("auth.auth owners = %v, want the catch-all", got)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// A pattern that matches and names nobody is how a forge takes ownership back.
|
|
105
|
+
// It has to win, or the rule it was written to cancel keeps applying.
|
|
106
|
+
func TestARuleNamingNobodyTakesOwnershipBack(t *testing.T) {
|
|
107
|
+
resp, err := verify(request(t, "* @acme/platform\nexamples/shop/oms\n"), Options{})
|
|
108
|
+
if err != nil {
|
|
109
|
+
t.Fatal(err)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if got := ownersOf(fragmentOf(t, resp), "shop.oms"); len(got) != 0 {
|
|
113
|
+
t.Errorf("shop.oms owners = %v, want nobody", got)
|
|
114
|
+
}
|
|
115
|
+
if !warned(resp, "shop.oms", "owned by nobody") {
|
|
116
|
+
t.Errorf("diagnostics = %+v, want the one saying nobody owns it", resp.Warnings())
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The half that earns the name: a rule matching no service is a team that
|
|
121
|
+
// believes it owns something the estate does not have, which is the failure a
|
|
122
|
+
// CODEOWNERS file has and can never report about itself.
|
|
123
|
+
func TestARuleThatOwnsNothingIsReported(t *testing.T) {
|
|
124
|
+
resp, err := verify(request(t, "* @acme/platform\nservices/billing @acme/billing\n"), Options{})
|
|
125
|
+
if err != nil {
|
|
126
|
+
t.Fatal(err)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if !warned(resp, "CODEOWNERS:2", "matches no service") {
|
|
130
|
+
t.Errorf("diagnostics = %+v, want the one about the unused rule", resp.Warnings())
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// The other way a rule can be dead, and a different fix: it matches, it just
|
|
135
|
+
// never wins, because everything it covers is claimed further down. Saying
|
|
136
|
+
// "matches no service" about a catch-all would be false and would teach a
|
|
137
|
+
// reader to ignore the warning that is true.
|
|
138
|
+
func TestARuleAlwaysOverriddenIsReportedAsSuch(t *testing.T) {
|
|
139
|
+
resp, err := verify(request(t, "* @acme/platform\nexamples @acme/estate\n"), Options{})
|
|
140
|
+
if err != nil {
|
|
141
|
+
t.Fatal(err)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if !warned(resp, "CODEOWNERS:1", "claimed by a rule below it") {
|
|
145
|
+
t.Errorf("diagnostics = %+v, want the one about the overridden catch-all", resp.Warnings())
|
|
146
|
+
}
|
|
147
|
+
if warned(resp, "CODEOWNERS:1", "matches no service") {
|
|
148
|
+
t.Error("the catch-all matches every service; it just never wins")
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
func TestSectionsAreReadFlatAndSaidSoOutLoud(t *testing.T) {
|
|
153
|
+
resp, err := verify(request(t, "[Backend]\n* @acme/platform\n"), Options{})
|
|
154
|
+
if err != nil {
|
|
155
|
+
t.Fatal(err)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if !warned(resp, "CODEOWNERS", "section headers") {
|
|
159
|
+
t.Errorf("diagnostics = %+v, want the one about sections", resp.Warnings())
|
|
160
|
+
}
|
|
161
|
+
if got := ownersOf(fragmentOf(t, resp), "shop.oms"); len(got) != 1 {
|
|
162
|
+
t.Errorf("owners = %v; the rules under a section are still read", got)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Determinism, the second obligation: the same catalog and the same file
|
|
167
|
+
// produce the same bytes, or `gen:check` reports drift on a run that changed
|
|
168
|
+
// nothing.
|
|
169
|
+
func TestTheFragmentIsStable(t *testing.T) {
|
|
170
|
+
req := request(t, "* @acme/platform\nexamples/shop/oms @acme/oms-team @someone\n")
|
|
171
|
+
|
|
172
|
+
first, err := verify(req, Options{})
|
|
173
|
+
if err != nil {
|
|
174
|
+
t.Fatal(err)
|
|
175
|
+
}
|
|
176
|
+
second, err := verify(req, Options{})
|
|
177
|
+
if err != nil {
|
|
178
|
+
t.Fatal(err)
|
|
179
|
+
}
|
|
180
|
+
if first.Files[0].Contents != second.Files[0].Contents {
|
|
181
|
+
t.Error("two runs wrote different bytes")
|
|
182
|
+
}
|
|
183
|
+
// Named in the order the rule names them: a file that lists the team
|
|
184
|
+
// before the person is showing an order somebody chose.
|
|
185
|
+
if got := ownersOf(fragmentOf(t, first), "shop.oms"); strings.Join(got, " ") != "@acme/oms-team @someone" {
|
|
186
|
+
t.Errorf("owners = %v, want the file's order", got)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// A path in the manifest that is not there is a typo, and answering "nobody
|
|
191
|
+
// owns anything" to a typo is only noticed a month later.
|
|
192
|
+
func TestANamedFileThatIsNotThereFailsTheRun(t *testing.T) {
|
|
193
|
+
if _, err := verify(request(t, "* @acme/platform\n"), Options{File: "OWNERS"}); err == nil {
|
|
194
|
+
t.Fatal("a missing file was accepted")
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
func TestTheFragmentCarriesTheHostsStamp(t *testing.T) {
|
|
199
|
+
resp, err := verify(request(t, "* @acme/platform\n"), Options{})
|
|
200
|
+
if err != nil {
|
|
201
|
+
t.Fatal(err)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fragment := fragmentOf(t, resp)
|
|
205
|
+
if fragment.Commit != "abc1234" || fragment.GeneratedAt != "2026-01-01T00:00:00Z" {
|
|
206
|
+
t.Errorf("stamp = %q %q", fragment.Commit, fragment.GeneratedAt)
|
|
207
|
+
}
|
|
208
|
+
// A context in the fragment is a shell the merge fills in, and its slug
|
|
209
|
+
// has to equal its id or the validator refuses the union.
|
|
210
|
+
for _, context := range fragment.Contexts {
|
|
211
|
+
if context.Slug != context.ID {
|
|
212
|
+
t.Errorf("context %q has slug %q", context.ID, context.Slug)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
func warned(resp plugin.Response, ref, contains string) bool {
|
|
218
|
+
for _, w := range resp.Warnings() {
|
|
219
|
+
if strings.Contains(w.Ref, ref) && strings.Contains(w.Message, contains) {
|
|
220
|
+
return true
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return false
|
|
225
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// The CODEOWNERS grammar, which is smaller than its reputation.
|
|
4
|
+
//
|
|
5
|
+
// A line is a path pattern and the handles that own what it matches. Blank
|
|
6
|
+
// lines and `#` comments are skipped. Later lines win: the file is read top to
|
|
7
|
+
// bottom and the LAST rule that matches a path is the one that owns it, which
|
|
8
|
+
// is what lets a broad rule at the top be narrowed underneath.
|
|
9
|
+
//
|
|
10
|
+
// GitLab adds sections - `[Backend]`, `^[Optional]`, `[Frontend][2]` - and
|
|
11
|
+
// under them the last-match rule applies per section rather than per file. It
|
|
12
|
+
// is read here as the flatter thing GitHub means, and a file that uses them is
|
|
13
|
+
// warned about rather than half-understood in silence, because the difference
|
|
14
|
+
// only ever shows up as an owner quietly missing from a page.
|
|
15
|
+
//
|
|
16
|
+
// Two pieces of gitignore syntax that CODEOWNERS does not have are not
|
|
17
|
+
// implemented, on purpose: `!` negation, which no forge supports here, and
|
|
18
|
+
// character ranges, which they do but nobody writes.
|
|
19
|
+
|
|
20
|
+
import (
|
|
21
|
+
"strings"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
// Rule is one line: what it matches, who owns it, and where it was written.
|
|
25
|
+
type Rule struct {
|
|
26
|
+
Pattern string
|
|
27
|
+
Owners []string
|
|
28
|
+
Line int
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Parsed is a file's rules and what was odd about it.
|
|
32
|
+
type Parsed struct {
|
|
33
|
+
Rules []Rule
|
|
34
|
+
|
|
35
|
+
// Sections is true when the file uses GitLab's section headers, which
|
|
36
|
+
// change the precedence rule this does not implement.
|
|
37
|
+
Sections bool
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// parseCodeowners reads the file. It does not fail: a line that is not a rule
|
|
41
|
+
// is not a rule, and refusing to describe an estate over a stray word in a
|
|
42
|
+
// file the forge itself skips would be a worse answer than the ownership the
|
|
43
|
+
// rest of the file states.
|
|
44
|
+
func parseCodeowners(src string) Parsed {
|
|
45
|
+
out := Parsed{Rules: []Rule{}}
|
|
46
|
+
|
|
47
|
+
for i, raw := range strings.Split(src, "\n") {
|
|
48
|
+
line := strings.TrimSpace(stripComment(raw))
|
|
49
|
+
if line == "" {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
if isSection(line) {
|
|
53
|
+
out.Sections = true
|
|
54
|
+
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fields := strings.Fields(line)
|
|
59
|
+
// A pattern with no owners is how a forge takes ownership BACK: it
|
|
60
|
+
// matches, and names nobody. Kept as a rule so that it can win over an
|
|
61
|
+
// earlier one, which is the only reason anybody writes it.
|
|
62
|
+
out.Rules = append(out.Rules, Rule{
|
|
63
|
+
Pattern: fields[0],
|
|
64
|
+
Owners: append([]string(nil), fields[1:]...),
|
|
65
|
+
Line: i + 1,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return out
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// stripComment removes a trailing comment. An escaped `\#` is a literal in a
|
|
73
|
+
// path, and paths with a `#` in them exist, so the escape is honoured.
|
|
74
|
+
func stripComment(line string) string {
|
|
75
|
+
for i := 0; i < len(line); i++ {
|
|
76
|
+
if line[i] == '#' && (i == 0 || line[i-1] != '\\') {
|
|
77
|
+
return line[:i]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return line
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// isSection reports a GitLab section header: `[Name]`, optionally required
|
|
85
|
+
// with `^`, optionally with an approval count `[Name][2]`.
|
|
86
|
+
func isSection(line string) bool {
|
|
87
|
+
line = strings.TrimPrefix(line, "^")
|
|
88
|
+
|
|
89
|
+
return strings.HasPrefix(line, "[") && strings.Contains(line, "]")
|
|
90
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
func TestParseReadsRulesAndSkipsTheRest(t *testing.T) {
|
|
6
|
+
parsed := parseCodeowners(`# Who to ask.
|
|
7
|
+
|
|
8
|
+
* @acme/platform
|
|
9
|
+
/docs @acme/writers @someone
|
|
10
|
+
examples/shop/oms @acme/oms-team # the team that wrote it
|
|
11
|
+
|
|
12
|
+
examples/legacy
|
|
13
|
+
`)
|
|
14
|
+
|
|
15
|
+
if parsed.Sections {
|
|
16
|
+
t.Error("no sections in this file")
|
|
17
|
+
}
|
|
18
|
+
if len(parsed.Rules) != 4 {
|
|
19
|
+
t.Fatalf("rules = %+v, want four", parsed.Rules)
|
|
20
|
+
}
|
|
21
|
+
if parsed.Rules[1].Pattern != "/docs" || len(parsed.Rules[1].Owners) != 2 {
|
|
22
|
+
t.Errorf("second rule = %+v", parsed.Rules[1])
|
|
23
|
+
}
|
|
24
|
+
// A trailing comment is not an owner.
|
|
25
|
+
if got := parsed.Rules[2].Owners; len(got) != 1 || got[0] != "@acme/oms-team" {
|
|
26
|
+
t.Errorf("third rule owners = %v", got)
|
|
27
|
+
}
|
|
28
|
+
// A pattern with nobody after it is how ownership is taken back, so it is
|
|
29
|
+
// a rule and not a blank line.
|
|
30
|
+
if parsed.Rules[3].Pattern != "examples/legacy" || len(parsed.Rules[3].Owners) != 0 {
|
|
31
|
+
t.Errorf("fourth rule = %+v", parsed.Rules[3])
|
|
32
|
+
}
|
|
33
|
+
// The line number is what a warning about a rule that owns nothing points at.
|
|
34
|
+
if parsed.Rules[2].Line != 5 {
|
|
35
|
+
t.Errorf("line = %d, want 5", parsed.Rules[2].Line)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func TestParseNoticesSections(t *testing.T) {
|
|
40
|
+
for _, header := range []string{"[Backend]", "^[Optional]", "[Frontend][2]"} {
|
|
41
|
+
parsed := parseCodeowners(header + "\n* @acme/platform\n")
|
|
42
|
+
if !parsed.Sections {
|
|
43
|
+
t.Errorf("%q was not read as a section header", header)
|
|
44
|
+
}
|
|
45
|
+
if len(parsed.Rules) != 1 {
|
|
46
|
+
t.Errorf("%q: rules = %+v, want the one below it", header, parsed.Rules)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// A `#` inside a path is a path, not a comment, when it is escaped - which is
|
|
52
|
+
// the one place the grammar is not "split on the first hash".
|
|
53
|
+
func TestParseKeepsAnEscapedHash(t *testing.T) {
|
|
54
|
+
parsed := parseCodeowners(`docs/c\#sharp @acme/writers`)
|
|
55
|
+
|
|
56
|
+
if len(parsed.Rules) != 1 || parsed.Rules[0].Pattern != `docs/c\#sharp` {
|
|
57
|
+
t.Fatalf("rules = %+v", parsed.Rules)
|
|
58
|
+
}
|
|
59
|
+
if len(parsed.Rules[0].Owners) != 1 {
|
|
60
|
+
t.Errorf("owners = %v", parsed.Rules[0].Owners)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
_ "embed"
|
|
5
|
+
|
|
6
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
//go:embed options.schema.json
|
|
10
|
+
var optionsSchema []byte
|
|
11
|
+
|
|
12
|
+
func descriptor() plugin.Descriptor {
|
|
13
|
+
return plugin.Descriptor{
|
|
14
|
+
Name: "verify-otel",
|
|
15
|
+
Summary: "Reads OpenTelemetry traces against the catalog and says which hops have been seen running.",
|
|
16
|
+
Phases: []string{plugin.PhaseVerify},
|
|
17
|
+
Options: optionsSchema,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Command verify-otel reads OpenTelemetry traces and says which hops of the
|
|
2
|
+
// catalog have been seen running.
|
|
3
|
+
//
|
|
4
|
+
// It is a verifier, not an extractor: it runs after the merge, is handed the
|
|
5
|
+
// catalog, and answers with a fragment that mostly re-declares what is already
|
|
6
|
+
// there - the same flows, with `verified` on the steps a trace shows - plus the
|
|
7
|
+
// consumers and calls the traces prove. A sequence no flow describes is written
|
|
8
|
+
// down as observed, so that a reader learns it exists.
|
|
9
|
+
//
|
|
10
|
+
// What a trace can and cannot say is kept strict. A span is a message going one
|
|
11
|
+
// way; it says the hop happened. It does not say a repository method was called,
|
|
12
|
+
// so `call` steps stay declared; and it does not put a service in the catalog,
|
|
13
|
+
// so a call whose far end is not there stays unresolved however often it ran.
|
|
14
|
+
package main
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"fmt"
|
|
18
|
+
"io"
|
|
19
|
+
"os"
|
|
20
|
+
|
|
21
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
type Options struct {
|
|
25
|
+
// Traces are the recordings to read, as globs relative to the input root.
|
|
26
|
+
// OTLP JSON, one batch per file or one per line.
|
|
27
|
+
Traces []string `json:"traces"`
|
|
28
|
+
|
|
29
|
+
// Services maps a resource's service.name to a catalog service id, for an
|
|
30
|
+
// estate where the two differ. Without a line here a name is matched to
|
|
31
|
+
// the one service whose slug it is.
|
|
32
|
+
Services map[string]string `json:"services,omitempty"`
|
|
33
|
+
|
|
34
|
+
// Events maps an event's wire name - the `event.name` attribute,
|
|
35
|
+
// "auth.PasswordChanged" - to a catalog event id, for an estate where the
|
|
36
|
+
// event does not declare its wire name and the last segment of the wire
|
|
37
|
+
// name is not the event's name in the model.
|
|
38
|
+
Events map[string]string `json:"events,omitempty"`
|
|
39
|
+
|
|
40
|
+
// Out names the fragment file.
|
|
41
|
+
Out string `json:"out,omitempty"`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func main() {
|
|
45
|
+
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
46
|
+
fmt.Fprintln(os.Stderr, "portolan-verify-otel:", err)
|
|
47
|
+
os.Exit(1)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func run(in io.Reader, out io.Writer) error {
|
|
52
|
+
return plugin.Serve(in, out, descriptor(), verify)
|
|
53
|
+
}
|