@shortlink-org/portolan 0.1.0 → 0.2.1
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/README.md +69 -205
- package/cli/init.mjs +285 -0
- package/cli/init.test.mjs +158 -0
- package/cli/portolan.mjs +37 -73
- package/index.html +19 -0
- package/package.json +5 -10
- package/plugins/README.md +66 -27
- package/plugins/portolan-go.wasm +0 -0
- package/portolan.json +52 -73
- package/public/landing/cats/bug-hunter.webp +0 -0
- package/public/landing/cats/compass-nap.webp +0 -0
- package/public/landing/cats/diagrammer.webp +0 -0
- package/public/landing/cats/docs-reader.webp +0 -0
- package/public/landing/cats/laptop.webp +0 -0
- package/public/landing/cats/map-inspector.webp +0 -0
- package/public/landing/cats/server-break.webp +0 -0
- package/public/landing/cats/star-mapper.webp +0 -0
- package/public/landing/cats/system-builder.webp +0 -0
- package/public/landing/cats/thread-tangle.webp +0 -0
- package/public/og.png +0 -0
- package/public/readme-header.webp +0 -0
- package/schema/portolan.schema.json +180 -0
- package/scripts/builtin-plugins.mjs +14 -0
- package/scripts/delivery-presets.mjs +353 -0
- package/scripts/gen.mjs +37 -4
- package/scripts/history.mjs +126 -0
- package/scripts/history.test.mjs +110 -0
- package/scripts/host-plugins/fetch-bsr.mjs +351 -0
- package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
- package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
- package/scripts/host-plugins/fetch-csr.mjs +386 -0
- package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
- package/scripts/host-plugins/fetch-git.mjs +379 -0
- package/scripts/host-plugins/fetch-git.test.mjs +210 -0
- package/scripts/local-api.mjs +27 -10
- package/scripts/local-api.test.mjs +85 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +24 -3
- package/scripts/plugin-host.mjs +77 -18
- package/scripts/plugin-host.test.mjs +50 -1
- package/scripts/plugin-wasm-worker.mjs +6 -3
- package/scripts/run-builtin.mjs +6 -26
- package/scripts/schema.mjs +6 -1
- package/src/app/App.tsx +24 -429
- package/src/app/Breadcrumbs.test.ts +7 -0
- package/src/app/Breadcrumbs.tsx +15 -2
- package/src/app/CatalogApp.tsx +436 -0
- package/src/app/Sidebar.tsx +0 -1
- package/src/chat/ChatPanel.tsx +98 -41
- package/src/chat/Composer.tsx +4 -2
- package/src/chat/Conversation.tsx +79 -15
- package/src/chat/Header.tsx +43 -9
- package/src/chat/Starter.tsx +64 -3
- package/src/chat/Steps.tsx +1 -1
- package/src/chat/flags.test.ts +51 -14
- package/src/chat/flags.ts +8 -2
- package/src/chat/page-context.test.ts +51 -0
- package/src/chat/page-context.ts +128 -0
- package/src/chat/prompt.test.ts +49 -1
- package/src/chat/prompt.ts +42 -1
- package/src/chat/tools.ts +7 -2
- package/src/chat/transport.ts +16 -4
- package/src/components/ProblemRow.tsx +182 -0
- package/src/data.ts +20 -5
- package/src/er/ErCanvas.tsx +1 -1
- package/src/graph/DependencyGraph.tsx +10 -1
- package/src/graph/FocusedEventGraph.tsx +1 -1
- package/src/graph/GraphToolbar.tsx +2 -4
- package/src/index.css +220 -2
- package/src/landing/DraggableReveal.tsx +168 -0
- package/src/landing/EstateGraph.tsx +28 -0
- package/src/landing/FlowPlayback.tsx +282 -0
- package/src/landing/HeroMap.tsx +128 -0
- package/src/landing/LandingPage.tsx +757 -0
- package/src/landing/ProductFrame.tsx +68 -0
- package/src/landing/ProductTour.tsx +371 -0
- package/src/landing/catalog.ts +25 -0
- package/src/landing/motion.tsx +52 -0
- package/src/lib/all-problems.ts +28 -0
- package/src/lib/local-api.ts +35 -1
- package/src/lib/setup-info.test.ts +8 -0
- package/src/lib/setup-info.ts +3 -2
- package/src/lib/source-code.ts +2 -1
- package/src/likec4/C4View.tsx +5 -0
- package/src/likec4/InteractiveView.tsx +5 -1
- package/src/map/ContextMapGraph.tsx +47 -20
- package/src/pages/ContextMap.tsx +5 -1
- package/src/pages/FlowDetail.tsx +24 -7
- package/src/pages/Overview.tsx +68 -12
- package/src/pages/Problems.tsx +6 -193
- package/src/pages/Settings.tsx +133 -108
- package/src/pages/settings/DeliverySettings.tsx +229 -0
- package/src/pages/settings/PreferencesSettings.tsx +101 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +13 -1
- package/vite.config.ts +17 -9
- package/catalog/enum_test.go +0 -46
- package/catalog/model.go +0 -932
- package/catalog/roundtrip_test.go +0 -185
- package/catalog/via_test.go +0 -38
- package/go.mod +0 -14
- package/go.sum +0 -14
- package/internal/commands/cargo.go +0 -154
- package/internal/commands/commands.go +0 -143
- package/internal/commands/commands_test.go +0 -323
- package/internal/commands/gradle.go +0 -77
- package/internal/commands/justfile.go +0 -82
- package/internal/commands/makefile.go +0 -106
- package/internal/commands/maven.go +0 -93
- package/internal/commands/packagejson.go +0 -159
- package/internal/commands/pyproject.go +0 -169
- package/internal/commands/taskfile.go +0 -101
- package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
- package/internal/commands/testdata/estate/Makefile +0 -33
- package/internal/commands/testdata/estate/Taskfile.yml +0 -21
- package/internal/commands/testdata/estate/build.gradle.kts +0 -21
- package/internal/commands/testdata/estate/justfile +0 -20
- package/internal/commands/testdata/estate/package.json +0 -13
- package/internal/commands/testdata/estate/pom.xml +0 -29
- package/internal/commands/testdata/estate/pyproject.toml +0 -27
- package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
- package/internal/commands/testdata/golden/commands.json +0 -305
- package/internal/gohttp/analyze.go +0 -2500
- package/internal/gohttp/endpoints.go +0 -1067
- package/internal/gohttp/roots.go +0 -320
- package/internal/gohttp/typed.go +0 -143
- package/internal/goscan/constants.go +0 -85
- package/internal/goscan/goscan_test.go +0 -227
- package/internal/goscan/names.go +0 -52
- package/internal/goscan/parse_test.go +0 -11
- package/internal/goscan/source.go +0 -36
- package/internal/goscan/tree.go +0 -155
- package/internal/goscan/types.go +0 -99
- package/internal/wsdl/ids.go +0 -127
- package/internal/wsdl/ids_test.go +0 -21
- package/internal/wsdl/model.go +0 -62
- package/internal/wsdl/parse.go +0 -920
- package/internal/wsdl/parse_test.go +0 -133
- package/plugin/describe.go +0 -107
- package/plugin/describe_test.go +0 -114
- package/plugin/protocol.go +0 -117
- package/plugin/schematest/schematest.go +0 -126
- package/plugins/extract-adr/describe.go +0 -19
- package/plugins/extract-adr/describe_test.go +0 -11
- package/plugins/extract-adr/extract.go +0 -153
- package/plugins/extract-adr/extract_test.go +0 -350
- package/plugins/extract-adr/history.go +0 -99
- package/plugins/extract-adr/main.go +0 -65
- package/plugins/extract-adr/parse.go +0 -642
- package/plugins/extract-adr/parse_test.go +0 -405
- package/plugins/extract-asyncapi/describe.go +0 -19
- package/plugins/extract-asyncapi/describe_test.go +0 -11
- package/plugins/extract-asyncapi/extract.go +0 -315
- package/plugins/extract-asyncapi/extract_test.go +0 -197
- package/plugins/extract-asyncapi/main.go +0 -48
- package/plugins/extract-asyncapi/spec.go +0 -151
- package/plugins/extract-commands/describe.go +0 -19
- package/plugins/extract-commands/describe_test.go +0 -11
- package/plugins/extract-commands/extract.go +0 -79
- package/plugins/extract-commands/extract_test.go +0 -92
- package/plugins/extract-commands/main.go +0 -55
- package/plugins/extract-csr/avro.go +0 -258
- package/plugins/extract-csr/describe.go +0 -19
- package/plugins/extract-csr/describe_test.go +0 -11
- package/plugins/extract-csr/extract.go +0 -338
- package/plugins/extract-csr/extract_test.go +0 -374
- package/plugins/extract-csr/jsonschema.go +0 -343
- package/plugins/extract-csr/lock.go +0 -25
- package/plugins/extract-csr/main.go +0 -84
- package/plugins/extract-csr/subject.go +0 -102
- package/plugins/extract-flows/describe.go +0 -19
- package/plugins/extract-flows/describe_test.go +0 -11
- package/plugins/extract-flows/extract.go +0 -91
- package/plugins/extract-flows/main.go +0 -45
- package/plugins/extract-flows/parse.go +0 -593
- package/plugins/extract-flows/parse_test.go +0 -204
- package/plugins/extract-glossary/describe.go +0 -19
- package/plugins/extract-glossary/describe_test.go +0 -11
- package/plugins/extract-glossary/extract.go +0 -115
- package/plugins/extract-glossary/extract_test.go +0 -220
- package/plugins/extract-glossary/main.go +0 -59
- package/plugins/extract-glossary/parse.go +0 -214
- package/plugins/extract-glossary/parse_test.go +0 -203
- package/plugins/extract-go/aggregate.go +0 -214
- package/plugins/extract-go/client.go +0 -409
- package/plugins/extract-go/client_test.go +0 -266
- package/plugins/extract-go/describe.go +0 -19
- package/plugins/extract-go/describe_test.go +0 -11
- package/plugins/extract-go/enum.go +0 -195
- package/plugins/extract-go/enum_test.go +0 -82
- package/plugins/extract-go/event.go +0 -99
- package/plugins/extract-go/extract.go +0 -191
- package/plugins/extract-go/extract_test.go +0 -261
- package/plugins/extract-go/flow.go +0 -1441
- package/plugins/extract-go/flow_test.go +0 -609
- package/plugins/extract-go/httpclient.go +0 -174
- package/plugins/extract-go/httpclient_test.go +0 -296
- package/plugins/extract-go/ids.go +0 -92
- package/plugins/extract-go/layout.go +0 -225
- package/plugins/extract-go/layout_test.go +0 -125
- package/plugins/extract-go/lifecycle.go +0 -324
- package/plugins/extract-go/lifecycle_test.go +0 -108
- package/plugins/extract-go/main.go +0 -79
- package/plugins/extract-go/operation.go +0 -157
- package/plugins/extract-go/source.go +0 -316
- package/plugins/extract-go/transport.go +0 -321
- package/plugins/extract-go/transport_test.go +0 -145
- package/plugins/extract-go/wiring.go +0 -434
- package/plugins/extract-go-nats/describe.go +0 -19
- package/plugins/extract-go-nats/describe_test.go +0 -11
- package/plugins/extract-go-nats/extract.go +0 -177
- package/plugins/extract-go-nats/extract_test.go +0 -389
- package/plugins/extract-go-nats/index.go +0 -379
- package/plugins/extract-go-nats/main.go +0 -42
- package/plugins/extract-go-nats/resolve.go +0 -161
- package/plugins/extract-go-nats/sites.go +0 -224
- package/plugins/extract-graphql/describe.go +0 -19
- package/plugins/extract-graphql/describe_test.go +0 -11
- package/plugins/extract-graphql/extract.go +0 -433
- package/plugins/extract-graphql/extract_test.go +0 -256
- package/plugins/extract-graphql/ids.go +0 -49
- package/plugins/extract-graphql/lex.go +0 -237
- package/plugins/extract-graphql/main.go +0 -51
- package/plugins/extract-graphql/parse.go +0 -621
- package/plugins/extract-graphql/parse_test.go +0 -122
- package/plugins/extract-http-clients/describe.go +0 -19
- package/plugins/extract-http-clients/describe_test.go +0 -11
- package/plugins/extract-http-clients/extract.go +0 -705
- package/plugins/extract-http-clients/extract_test.go +0 -1263
- package/plugins/extract-http-clients/main.go +0 -42
- package/plugins/extract-openapi/describe.go +0 -19
- package/plugins/extract-openapi/describe_test.go +0 -11
- package/plugins/extract-openapi/discover.go +0 -243
- package/plugins/extract-openapi/extract.go +0 -526
- package/plugins/extract-openapi/extract_test.go +0 -545
- package/plugins/extract-openapi/main.go +0 -75
- package/plugins/extract-openapi/spec.go +0 -350
- package/plugins/extract-project/describe.go +0 -19
- package/plugins/extract-project/describe_test.go +0 -11
- package/plugins/extract-project/extract.go +0 -221
- package/plugins/extract-project/extract_test.go +0 -109
- package/plugins/extract-project/main.go +0 -41
- package/plugins/extract-proto/ast.go +0 -125
- package/plugins/extract-proto/consumes.go +0 -77
- package/plugins/extract-proto/describe.go +0 -19
- package/plugins/extract-proto/extract.go +0 -293
- package/plugins/extract-proto/extract_test.go +0 -459
- package/plugins/extract-proto/ids.go +0 -89
- package/plugins/extract-proto/ids_test.go +0 -57
- package/plugins/extract-proto/lex.go +0 -285
- package/plugins/extract-proto/main.go +0 -100
- package/plugins/extract-proto/module.go +0 -120
- package/plugins/extract-proto/parse.go +0 -720
- package/plugins/extract-proto/parse_test.go +0 -307
- package/plugins/extract-proto/provides.go +0 -236
- package/plugins/extract-proto/resolve.go +0 -222
- package/plugins/extract-proto/resolve_test.go +0 -119
- package/plugins/extract-redis/describe.go +0 -19
- package/plugins/extract-redis/describe_test.go +0 -11
- package/plugins/extract-redis/extract.go +0 -183
- package/plugins/extract-redis/extract_test.go +0 -168
- package/plugins/extract-redis/keyspaces.go +0 -469
- package/plugins/extract-redis/main.go +0 -44
- package/plugins/extract-river/describe.go +0 -19
- package/plugins/extract-river/describe_test.go +0 -11
- package/plugins/extract-river/extract.go +0 -507
- package/plugins/extract-river/extract_test.go +0 -132
- package/plugins/extract-river/main.go +0 -42
- package/plugins/extract-sql/ddl.go +0 -893
- package/plugins/extract-sql/ddl_test.go +0 -401
- package/plugins/extract-sql/describe.go +0 -19
- package/plugins/extract-sql/describe_test.go +0 -11
- package/plugins/extract-sql/layout.go +0 -221
- package/plugins/extract-sql/layout_test.go +0 -55
- package/plugins/extract-sql/lineage.go +0 -107
- package/plugins/extract-sql/main.go +0 -141
- package/plugins/extract-sql/maps.go +0 -564
- package/plugins/extract-sql/maps_java.go +0 -117
- package/plugins/extract-sql/maps_rust.go +0 -333
- package/plugins/extract-sql/maps_rust_test.go +0 -70
- package/plugins/extract-sql/maps_test.go +0 -204
- package/plugins/extract-sql/maps_ts.go +0 -398
- package/plugins/extract-sql/maps_ts_test.go +0 -136
- package/plugins/extract-sql/projection.go +0 -70
- package/plugins/extract-sql/projection_test.go +0 -50
- package/plugins/extract-sql/store.go +0 -420
- package/plugins/extract-sql/store_test.go +0 -233
- package/plugins/extract-sql/view.go +0 -295
- package/plugins/extract-watermill/describe.go +0 -19
- package/plugins/extract-watermill/describe_test.go +0 -11
- package/plugins/extract-watermill/extract.go +0 -1228
- package/plugins/extract-watermill/extract_test.go +0 -234
- package/plugins/extract-watermill/main.go +0 -41
- package/plugins/extract-wsdl/describe.go +0 -19
- package/plugins/extract-wsdl/describe_test.go +0 -11
- package/plugins/extract-wsdl/extract.go +0 -148
- package/plugins/extract-wsdl/extract_test.go +0 -45
- package/plugins/extract-wsdl/main.go +0 -53
- package/plugins/fetch-bsr/auth.go +0 -114
- package/plugins/fetch-bsr/bsr.go +0 -240
- package/plugins/fetch-bsr/cache.go +0 -66
- package/plugins/fetch-bsr/describe.go +0 -19
- package/plugins/fetch-bsr/fetch.go +0 -192
- package/plugins/fetch-bsr/fetch_test.go +0 -465
- package/plugins/fetch-bsr/lock.go +0 -71
- package/plugins/fetch-bsr/main.go +0 -117
- package/plugins/fetch-csr/auth.go +0 -95
- package/plugins/fetch-csr/cache.go +0 -69
- package/plugins/fetch-csr/describe.go +0 -19
- package/plugins/fetch-csr/describe_test.go +0 -11
- package/plugins/fetch-csr/fetch.go +0 -254
- package/plugins/fetch-csr/fetch_test.go +0 -494
- package/plugins/fetch-csr/lock.go +0 -95
- package/plugins/fetch-csr/main.go +0 -122
- package/plugins/fetch-csr/registry.go +0 -209
- package/plugins/fetch-git/cache.go +0 -65
- package/plugins/fetch-git/describe.go +0 -19
- package/plugins/fetch-git/describe_test.go +0 -11
- package/plugins/fetch-git/fetch.go +0 -181
- package/plugins/fetch-git/fetch_test.go +0 -332
- package/plugins/fetch-git/git.go +0 -169
- package/plugins/fetch-git/lock.go +0 -72
- package/plugins/fetch-git/main.go +0 -127
- package/plugins/fetch-git/offline.go +0 -39
- package/plugins/fetch-git/pin.go +0 -90
- package/plugins/fetch-git/pin_test.go +0 -74
- package/plugins/gen-backstage/describe.go +0 -17
- package/plugins/gen-backstage/main.go +0 -22
- package/plugins/gen-backstage/plugin.go +0 -473
- package/plugins/gen-backstage/plugin_test.go +0 -145
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +0 -162
- package/plugins/gen-markdown/aggregate.go +0 -362
- package/plugins/gen-markdown/canonical.go +0 -93
- package/plugins/gen-markdown/context.go +0 -90
- package/plugins/gen-markdown/coverage_test.go +0 -89
- package/plugins/gen-markdown/describe.go +0 -19
- package/plugins/gen-markdown/describe_test.go +0 -11
- package/plugins/gen-markdown/external.go +0 -71
- package/plugins/gen-markdown/flow.go +0 -277
- package/plugins/gen-markdown/glossary.go +0 -76
- package/plugins/gen-markdown/glossary_test.go +0 -148
- package/plugins/gen-markdown/llms.go +0 -302
- package/plugins/gen-markdown/main.go +0 -22
- package/plugins/gen-markdown/markdown.go +0 -254
- package/plugins/gen-markdown/markdown_test.go +0 -100
- package/plugins/gen-markdown/module.go +0 -107
- package/plugins/gen-markdown/plugin.go +0 -39
- package/plugins/gen-markdown/quality_test.go +0 -176
- package/plugins/gen-markdown/redis_test.go +0 -38
- package/plugins/gen-markdown/render.go +0 -344
- package/plugins/gen-markdown/render_test.go +0 -186
- package/plugins/gen-markdown/service.go +0 -456
- package/plugins/gen-markdown/source.go +0 -177
- package/plugins/gen-markdown/store.go +0 -201
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +0 -17
- package/plugins/gen-mermaid/main.go +0 -22
- package/plugins/gen-mermaid/plugin.go +0 -104
- package/plugins/gen-mermaid/plugin_test.go +0 -33
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +0 -261
- package/plugins/openapi/ids_test.go +0 -98
- package/plugins/verify-codeowners/describe.go +0 -19
- package/plugins/verify-codeowners/describe_test.go +0 -11
- package/plugins/verify-codeowners/main.go +0 -75
- package/plugins/verify-codeowners/match.go +0 -85
- package/plugins/verify-codeowners/match_test.go +0 -47
- package/plugins/verify-codeowners/owners.go +0 -164
- package/plugins/verify-codeowners/owners_test.go +0 -225
- package/plugins/verify-codeowners/parse.go +0 -90
- package/plugins/verify-codeowners/parse_test.go +0 -62
- package/plugins/verify-otel/describe.go +0 -19
- package/plugins/verify-otel/describe_test.go +0 -11
- package/plugins/verify-otel/main.go +0 -53
- package/plugins/verify-otel/match.go +0 -336
- package/plugins/verify-otel/otlp.go +0 -200
- package/plugins/verify-otel/verify.go +0 -734
- package/plugins/verify-otel/verify_test.go +0 -460
- /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
- /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
package commands
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"flag"
|
|
6
|
-
"os"
|
|
7
|
-
"path/filepath"
|
|
8
|
-
"strings"
|
|
9
|
-
"testing"
|
|
10
|
-
|
|
11
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
var update = flag.Bool("update", false, "rewrite the golden list instead of comparing against it")
|
|
15
|
-
|
|
16
|
-
const goldenPath = "testdata/golden/commands.json"
|
|
17
|
-
|
|
18
|
-
func read(t *testing.T) []catalog.Command {
|
|
19
|
-
t.Helper()
|
|
20
|
-
cmds, warnings := Read("testdata/estate")
|
|
21
|
-
if len(warnings) != 0 {
|
|
22
|
-
t.Fatalf("warnings: %v", warnings)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return cmds
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
func find(t *testing.T, cmds []catalog.Command, run string) catalog.Command {
|
|
29
|
-
t.Helper()
|
|
30
|
-
for _, cmd := range cmds {
|
|
31
|
-
if cmd.Run == run {
|
|
32
|
-
return cmd
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
t.Fatalf("no command %q in %s", run, names(cmds))
|
|
36
|
-
|
|
37
|
-
return catalog.Command{}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func names(cmds []catalog.Command) string {
|
|
41
|
-
out := make([]string, 0, len(cmds))
|
|
42
|
-
for _, cmd := range cmds {
|
|
43
|
-
out = append(out, cmd.Run)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return strings.Join(out, ", ")
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
func absent(t *testing.T, cmds []catalog.Command, run string) {
|
|
50
|
-
t.Helper()
|
|
51
|
-
for _, cmd := range cmds {
|
|
52
|
-
if cmd.Run == run {
|
|
53
|
-
t.Errorf("%q is listed and should not be", run)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
func TestMakefileTargetsAndDocs(t *testing.T) {
|
|
59
|
-
cmds := read(t)
|
|
60
|
-
|
|
61
|
-
gen := find(t, cmds, "make gen")
|
|
62
|
-
if gen.Doc != "Regenerate the gRPC stubs" {
|
|
63
|
-
t.Errorf("gen doc = %q", gen.Doc)
|
|
64
|
-
}
|
|
65
|
-
if !strings.HasPrefix(gen.Body, "buf generate $(GRPC)/quote/proto") || strings.Count(gen.Body, "\n") != 1 {
|
|
66
|
-
t.Errorf("gen body = %q", gen.Body)
|
|
67
|
-
}
|
|
68
|
-
if gen.Source != "testdata/estate/Makefile:10" {
|
|
69
|
-
t.Errorf("gen source = %q", gen.Source)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
build := find(t, cmds, "make build")
|
|
73
|
-
if build.Doc != "Compiles every package. Nothing is installed." {
|
|
74
|
-
t.Errorf("comment over a target is its doc; got %q", build.Doc)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Two targets on one rule are two commands.
|
|
78
|
-
find(t, cmds, "make test")
|
|
79
|
-
find(t, cmds, "make lint")
|
|
80
|
-
// Inside a conditional is still a target.
|
|
81
|
-
find(t, cmds, "make ci")
|
|
82
|
-
|
|
83
|
-
for _, hidden := range []string{"make .PHONY", "make _internal", "make $(BIN)", "make %.pb.go", "make GRPC", "make BIN", "make ifeq"} {
|
|
84
|
-
absent(t, cmds, hidden)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
func TestJustfileRecipes(t *testing.T) {
|
|
89
|
-
cmds := read(t)
|
|
90
|
-
|
|
91
|
-
dev := find(t, cmds, "just dev")
|
|
92
|
-
if dev.Doc != "Run the service against the compose stack" || dev.Body != "docker compose up -d\ncargo run" {
|
|
93
|
-
t.Errorf("dev = %+v", dev)
|
|
94
|
-
}
|
|
95
|
-
test := find(t, cmds, "just test")
|
|
96
|
-
if test.Doc != "Run the tests, unit and integration" {
|
|
97
|
-
t.Errorf("doc attribute beats the comment; got %q", test.Doc)
|
|
98
|
-
}
|
|
99
|
-
for _, hidden := range []string{"just release", "just _setup", "just alias", "just set", "just port"} {
|
|
100
|
-
absent(t, cmds, hidden)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
func TestTaskfileTasks(t *testing.T) {
|
|
105
|
-
cmds := read(t)
|
|
106
|
-
|
|
107
|
-
build := find(t, cmds, "task build")
|
|
108
|
-
if build.Doc != "Compile the binary" || build.Body != "go build -o bin/svc ./cmd/svc" {
|
|
109
|
-
t.Errorf("build = %+v", build)
|
|
110
|
-
}
|
|
111
|
-
if migrate := find(t, cmds, "task migrate"); migrate.Doc != "Apply pending migrations" || migrate.Body != "goose up" {
|
|
112
|
-
t.Errorf("migrate = %+v", migrate)
|
|
113
|
-
}
|
|
114
|
-
if def := find(t, cmds, "task default"); def.Body != "task build" {
|
|
115
|
-
t.Errorf("default = %+v", def)
|
|
116
|
-
}
|
|
117
|
-
if fmt := find(t, cmds, "task fmt"); fmt.Body != "gofmt -w ." {
|
|
118
|
-
t.Errorf("fmt = %+v", fmt)
|
|
119
|
-
}
|
|
120
|
-
absent(t, cmds, "task lint")
|
|
121
|
-
absent(t, cmds, "task _shared")
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
func TestPackageScriptsInFileOrder(t *testing.T) {
|
|
125
|
-
cmds := read(t)
|
|
126
|
-
|
|
127
|
-
var npm []string
|
|
128
|
-
for _, cmd := range cmds {
|
|
129
|
-
if cmd.Runner == "npm" {
|
|
130
|
-
npm = append(npm, cmd.Run)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
want := "npm run build, npm start, npm test, npm run build:watch, npm run generate, npm run generate:api"
|
|
134
|
-
if got := strings.Join(npm, ", "); got != want {
|
|
135
|
-
t.Errorf("scripts = %s\n want %s", got, want)
|
|
136
|
-
}
|
|
137
|
-
if build := find(t, cmds, "npm run build"); build.Body != "tsc -p tsconfig.build.json" || build.Source != "testdata/estate/package.json:4" {
|
|
138
|
-
t.Errorf("build = %+v", build)
|
|
139
|
-
}
|
|
140
|
-
if watch := find(t, cmds, "npm run build:watch"); watch.Source != "testdata/estate/package.json:8" {
|
|
141
|
-
t.Errorf("a name containing another name has its own line; got %q", watch.Source)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
func TestPackageRunnerFollowsTheLockfile(t *testing.T) {
|
|
146
|
-
root := t.TempDir()
|
|
147
|
-
if err := os.WriteFile(filepath.Join(root, "package.json"), []byte(`{"scripts":{"test":"vitest"}}`), 0o644); err != nil {
|
|
148
|
-
t.Fatal(err)
|
|
149
|
-
}
|
|
150
|
-
if err := os.WriteFile(filepath.Join(root, "pnpm-lock.yaml"), nil, 0o644); err != nil {
|
|
151
|
-
t.Fatal(err)
|
|
152
|
-
}
|
|
153
|
-
cmds, _ := Read(root)
|
|
154
|
-
if len(cmds) != 1 || cmds[0].Run != "pnpm test" || cmds[0].Runner != "pnpm" {
|
|
155
|
-
t.Errorf("commands = %+v", cmds)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
func TestPyprojectTasks(t *testing.T) {
|
|
160
|
-
cmds := read(t)
|
|
161
|
-
|
|
162
|
-
if test := find(t, cmds, "poe test"); test.Body != "pytest" {
|
|
163
|
-
t.Errorf("test = %+v", test)
|
|
164
|
-
}
|
|
165
|
-
if lint := find(t, cmds, "poe lint"); lint.Body != "ruff check ." {
|
|
166
|
-
t.Errorf("lint = %+v", lint)
|
|
167
|
-
}
|
|
168
|
-
if serve := find(t, cmds, "poe serve"); serve.Doc != "Run the dev server" || serve.Body != "python manage.py runserver" {
|
|
169
|
-
t.Errorf("serve = %+v", serve)
|
|
170
|
-
}
|
|
171
|
-
migrate := find(t, cmds, "poe migrate")
|
|
172
|
-
if migrate.Doc != "Apply pending migrations" || migrate.Body != "python manage.py migrate" || migrate.Source != "testdata/estate/pyproject.toml:14" {
|
|
173
|
-
t.Errorf("migrate = %+v", migrate)
|
|
174
|
-
}
|
|
175
|
-
if check := find(t, cmds, "poe check"); check.Body != "lint\ntest" {
|
|
176
|
-
t.Errorf("check = %+v", check)
|
|
177
|
-
}
|
|
178
|
-
if worker := find(t, cmds, "pdm run worker"); worker.Doc != "Run the celery worker" || worker.Body != "celery -A billing worker" {
|
|
179
|
-
t.Errorf("worker = %+v", worker)
|
|
180
|
-
}
|
|
181
|
-
find(t, cmds, "pdm run test")
|
|
182
|
-
absent(t, cmds, "pdm run pre_test")
|
|
183
|
-
absent(t, cmds, "poe _hidden")
|
|
184
|
-
// Entry points are programs the package installs, not tasks.
|
|
185
|
-
absent(t, cmds, "poe billing")
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
func TestPomPhasesAndPluginGoals(t *testing.T) {
|
|
189
|
-
cmds := read(t)
|
|
190
|
-
|
|
191
|
-
if test := find(t, cmds, "mvn test"); test.Source != "testdata/estate/pom.xml:5" || test.Doc == "" {
|
|
192
|
-
t.Errorf("test = %+v", test)
|
|
193
|
-
}
|
|
194
|
-
find(t, cmds, "mvn package")
|
|
195
|
-
if run := find(t, cmds, "mvn spring-boot:run"); run.Doc != "Run the application" || run.Source != "testdata/estate/pom.xml:22" {
|
|
196
|
-
t.Errorf("spring-boot:run = %+v", run)
|
|
197
|
-
}
|
|
198
|
-
// A plugin bound to a phase is run by the phase, not typed; a plugin
|
|
199
|
-
// under pluginManagement or in dependencies is not part of the build.
|
|
200
|
-
for _, hidden := range []string{"mvn protobuf:compile", "mvn jib:dockerBuild", "mvn flyway:migrate"} {
|
|
201
|
-
absent(t, cmds, hidden)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
func TestPomIsTypedAtTheWrapper(t *testing.T) {
|
|
206
|
-
root := t.TempDir()
|
|
207
|
-
if err := os.WriteFile(filepath.Join(root, "pom.xml"), []byte("<project><packaging>jar</packaging></project>"), 0o644); err != nil {
|
|
208
|
-
t.Fatal(err)
|
|
209
|
-
}
|
|
210
|
-
if err := os.WriteFile(filepath.Join(root, "mvnw"), []byte("#!/bin/sh\n"), 0o755); err != nil {
|
|
211
|
-
t.Fatal(err)
|
|
212
|
-
}
|
|
213
|
-
cmds, _ := Read(root)
|
|
214
|
-
if len(cmds) != 2 || cmds[0].Run != "./mvnw test" || cmds[0].Runner != "./mvnw" {
|
|
215
|
-
t.Errorf("commands = %+v", cmds)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
func TestGradleTasks(t *testing.T) {
|
|
220
|
-
cmds := read(t)
|
|
221
|
-
|
|
222
|
-
find(t, cmds, "gradle build")
|
|
223
|
-
find(t, cmds, "gradle test")
|
|
224
|
-
if run := find(t, cmds, "gradle run"); run.Source != "testdata/estate/build.gradle.kts:3" {
|
|
225
|
-
t.Errorf("run = %+v", run)
|
|
226
|
-
}
|
|
227
|
-
if boot := find(t, cmds, "gradle bootRun"); boot.Source != "testdata/estate/build.gradle.kts:2" {
|
|
228
|
-
t.Errorf("bootRun = %+v", boot)
|
|
229
|
-
}
|
|
230
|
-
if gen := find(t, cmds, "gradle generateProto"); gen.Doc != "Regenerate the gRPC stubs" || gen.Source != "testdata/estate/build.gradle.kts:6" {
|
|
231
|
-
t.Errorf("generateProto = %+v", gen)
|
|
232
|
-
}
|
|
233
|
-
if smoke := find(t, cmds, "gradle smokeTest"); smoke.Doc != "Run the tests that need the compose stack" {
|
|
234
|
-
t.Errorf("smokeTest = %+v", smoke)
|
|
235
|
-
}
|
|
236
|
-
find(t, cmds, "gradle legacyTask")
|
|
237
|
-
absent(t, cmds, "gradle _shared")
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
func TestCargoAliasesAndXtask(t *testing.T) {
|
|
241
|
-
cmds := read(t)
|
|
242
|
-
|
|
243
|
-
if xtask := find(t, cmds, "cargo xtask"); xtask.Body != "run --package xtask --" || xtask.Source != "testdata/estate/.cargo/config.toml:5" {
|
|
244
|
-
t.Errorf("xtask = %+v", xtask)
|
|
245
|
-
}
|
|
246
|
-
if gen := find(t, cmds, "cargo gen"); gen.Body != "run --package xtask -- gen" {
|
|
247
|
-
t.Errorf("gen = %+v", gen)
|
|
248
|
-
}
|
|
249
|
-
absent(t, cmds, "cargo _secret")
|
|
250
|
-
|
|
251
|
-
if gen := find(t, cmds, "cargo xtask gen"); gen.Doc != "Regenerate the gRPC stubs from the vendored protos" || gen.Source != "testdata/estate/xtask/src/main.rs:12" {
|
|
252
|
-
t.Errorf("xtask gen = %+v", gen)
|
|
253
|
-
}
|
|
254
|
-
if ci := find(t, cmds, "cargo xtask ci"); ci.Doc != "Run fmt, clippy and the tests the way CI does" {
|
|
255
|
-
t.Errorf("xtask ci = %+v", ci)
|
|
256
|
-
}
|
|
257
|
-
if dist := find(t, cmds, "cargo xtask dist"); dist.Doc != "Build the release artefacts" {
|
|
258
|
-
t.Errorf("xtask dist = %+v", dist)
|
|
259
|
-
}
|
|
260
|
-
// Match arms add what the enum does not name, once each.
|
|
261
|
-
if bench := find(t, cmds, "cargo xtask bench"); bench.Doc != "Run the benchmarks against the compose stack" {
|
|
262
|
-
t.Errorf("bench = %+v", bench)
|
|
263
|
-
}
|
|
264
|
-
if perf := find(t, cmds, "cargo xtask perf"); perf.Doc != "Run the benchmarks against the compose stack" {
|
|
265
|
-
t.Errorf("perf = %+v", perf)
|
|
266
|
-
}
|
|
267
|
-
count := 0
|
|
268
|
-
for _, cmd := range cmds {
|
|
269
|
-
if cmd.Run == "cargo xtask gen" {
|
|
270
|
-
count++
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if count != 1 {
|
|
274
|
-
t.Errorf("cargo xtask gen listed %d times", count)
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
func TestNothingToReadIsNothing(t *testing.T) {
|
|
279
|
-
cmds, warnings := Read(t.TempDir())
|
|
280
|
-
if len(cmds) != 0 || len(warnings) != 0 {
|
|
281
|
-
t.Errorf("commands = %+v, warnings = %v", cmds, warnings)
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
func TestBrokenFilesWarnRatherThanFail(t *testing.T) {
|
|
286
|
-
root := t.TempDir()
|
|
287
|
-
if err := os.WriteFile(filepath.Join(root, "Taskfile.yml"), []byte("tasks: [\n"), 0o644); err != nil {
|
|
288
|
-
t.Fatal(err)
|
|
289
|
-
}
|
|
290
|
-
if err := os.WriteFile(filepath.Join(root, "package.json"), []byte(`{"scripts": "nope"}`), 0o644); err != nil {
|
|
291
|
-
t.Fatal(err)
|
|
292
|
-
}
|
|
293
|
-
cmds, warnings := Read(root)
|
|
294
|
-
if len(cmds) != 0 || len(warnings) != 2 {
|
|
295
|
-
t.Errorf("commands = %+v, warnings = %v", cmds, warnings)
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
func TestGolden(t *testing.T) {
|
|
300
|
-
cmds := read(t)
|
|
301
|
-
got, err := json.MarshalIndent(cmds, "", " ")
|
|
302
|
-
if err != nil {
|
|
303
|
-
t.Fatal(err)
|
|
304
|
-
}
|
|
305
|
-
got = append(got, '\n')
|
|
306
|
-
if *update {
|
|
307
|
-
if err := os.MkdirAll(filepath.Dir(goldenPath), 0o755); err != nil {
|
|
308
|
-
t.Fatal(err)
|
|
309
|
-
}
|
|
310
|
-
if err := os.WriteFile(goldenPath, got, 0o644); err != nil {
|
|
311
|
-
t.Fatal(err)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
want, err := os.ReadFile(goldenPath)
|
|
317
|
-
if err != nil {
|
|
318
|
-
t.Fatalf("%v (run with -update to write it)", err)
|
|
319
|
-
}
|
|
320
|
-
if string(want) != string(got) {
|
|
321
|
-
t.Errorf("golden differs from what is read now; run with -update and review the diff\n%s", got)
|
|
322
|
-
}
|
|
323
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
package commands
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"os"
|
|
5
|
-
"path/filepath"
|
|
6
|
-
"regexp"
|
|
7
|
-
"strings"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
var gradleRegister = regexp.MustCompile(`(?m)^[ \t]*(?:tasks\.register(?:<[^>]+>)?\(\s*["']([A-Za-z][\w-]*)["']|task\s+([A-Za-z][\w-]*)\s*[({]|val\s+([A-Za-z][\w-]*)\s+by\s+tasks\.(?:registering|creating))`)
|
|
13
|
-
var gradleDescription = regexp.MustCompile(`description\s*(?:=|\.set\()\s*["']([^"']*)["']`)
|
|
14
|
-
var gradlePlugin = regexp.MustCompile(`(?m)^[ \t]*(?:id\s*\(?\s*["']([\w.-]+)["']\)?|apply\s+plugin:\s*["']([\w.-]+)["']|\b(application|java|java-library|kotlin)\b\s*$)`)
|
|
15
|
-
|
|
16
|
-
// readGradle lists what a Gradle build answers to: `build` and `test`, which
|
|
17
|
-
// every JVM build has; `run` when the application plugin is applied and
|
|
18
|
-
// `bootRun` for Spring Boot; and the tasks the script registers itself, with
|
|
19
|
-
// the description it sets on them. The line is typed at the wrapper when the
|
|
20
|
-
// checkout keeps one.
|
|
21
|
-
//
|
|
22
|
-
// A build script is a program, and this reads it as text: a task registered
|
|
23
|
-
// inside a loop or by a plugin the script does not name is not here.
|
|
24
|
-
func readGradle(root, file, src string) []catalog.Command {
|
|
25
|
-
runner, prefix := "gradle", "gradle "
|
|
26
|
-
if _, err := os.Stat(filepath.Join(root, "gradlew")); err == nil {
|
|
27
|
-
runner, prefix = "./gradlew", "./gradlew "
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
out := []catalog.Command{
|
|
31
|
-
{Runner: runner, Name: "build", Run: prefix + "build", Doc: "Assemble and test the project", Source: file},
|
|
32
|
-
{Runner: runner, Name: "test", Run: prefix + "test", Doc: "Run the tests", Source: file},
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
plugins := map[string]int{}
|
|
36
|
-
for _, m := range gradlePlugin.FindAllStringSubmatchIndex(src, -1) {
|
|
37
|
-
for _, group := range [][2]int{{m[2], m[3]}, {m[4], m[5]}, {m[6], m[7]}} {
|
|
38
|
-
if group[0] >= 0 {
|
|
39
|
-
plugins[src[group[0]:group[1]]] = 1 + strings.Count(src[:group[0]], "\n")
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if line, ok := plugins["application"]; ok {
|
|
44
|
-
out = append(out, catalog.Command{Runner: runner, Name: "run", Run: prefix + "run", Doc: "Run the application", Source: at(file, line)})
|
|
45
|
-
}
|
|
46
|
-
if line, ok := plugins["org.springframework.boot"]; ok {
|
|
47
|
-
out = append(out, catalog.Command{Runner: runner, Name: "bootRun", Run: prefix + "bootRun", Doc: "Run the Spring Boot application", Source: at(file, line)})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
matches := gradleRegister.FindAllStringSubmatchIndex(src, -1)
|
|
51
|
-
for i, m := range matches {
|
|
52
|
-
name := ""
|
|
53
|
-
for _, group := range [][2]int{{m[2], m[3]}, {m[4], m[5]}, {m[6], m[7]}} {
|
|
54
|
-
if group[0] >= 0 {
|
|
55
|
-
name = src[group[0]:group[1]]
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if !typeable(name) {
|
|
59
|
-
continue
|
|
60
|
-
}
|
|
61
|
-
// The description is read from the task's own block: from its
|
|
62
|
-
// registration up to the next one. A script that sets one task's
|
|
63
|
-
// description after registering the next is read wrong, and that
|
|
64
|
-
// is the price of not running Gradle.
|
|
65
|
-
end := len(src)
|
|
66
|
-
if i+1 < len(matches) {
|
|
67
|
-
end = matches[i+1][0]
|
|
68
|
-
}
|
|
69
|
-
doc := ""
|
|
70
|
-
if d := gradleDescription.FindStringSubmatch(src[m[0]:end]); d != nil {
|
|
71
|
-
doc = d[1]
|
|
72
|
-
}
|
|
73
|
-
out = append(out, catalog.Command{Runner: runner, Name: name, Run: prefix + name, Doc: doc, Source: at(file, 1+strings.Count(src[:m[0]], "\n"))})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return out
|
|
77
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
package commands
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"regexp"
|
|
5
|
-
"strings"
|
|
6
|
-
|
|
7
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
var justRecipe = regexp.MustCompile(`^(@?)([A-Za-z_][A-Za-z0-9_-]*)((?:\s+[+*$]?[A-Za-z_][A-Za-z0-9_-]*(?:=[^\s]+)?)*)\s*:(?:\s|$)`)
|
|
11
|
-
var justDocAttribute = regexp.MustCompile(`\[doc\((['"])(.*?)['"]\)\]`)
|
|
12
|
-
|
|
13
|
-
// readJustfile lists the recipes of a justfile.
|
|
14
|
-
//
|
|
15
|
-
// A recipe is a name at column 0 followed by its parameters and a colon; the
|
|
16
|
-
// lines indented under it are the body. `#` lines directly over the recipe
|
|
17
|
-
// are its doc, which is what `just --list` shows too, and a `[doc("...")]`
|
|
18
|
-
// attribute beats them. `[private]`, `_`-prefixed and aliased names are left
|
|
19
|
-
// out, as `--list` leaves them out.
|
|
20
|
-
func readJustfile(file, src string) []catalog.Command {
|
|
21
|
-
lines := strings.Split(src, "\n")
|
|
22
|
-
var out []catalog.Command
|
|
23
|
-
for i := 0; i < len(lines); i++ {
|
|
24
|
-
line := lines[i]
|
|
25
|
-
match := justRecipe.FindStringSubmatch(line)
|
|
26
|
-
if match == nil || strings.HasPrefix(line, "alias ") || strings.HasPrefix(line, "set ") || strings.HasPrefix(line, "mod ") || strings.HasPrefix(line, "import ") {
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
29
|
-
name := match[2]
|
|
30
|
-
|
|
31
|
-
// Attributes sit on their own lines above the recipe, above the
|
|
32
|
-
// comment or between it and the recipe; read up through both.
|
|
33
|
-
private := !typeable(name)
|
|
34
|
-
doc := ""
|
|
35
|
-
top := i
|
|
36
|
-
for j := i - 1; j >= 0; j-- {
|
|
37
|
-
text := strings.TrimSpace(lines[j])
|
|
38
|
-
if !strings.HasPrefix(text, "[") {
|
|
39
|
-
break
|
|
40
|
-
}
|
|
41
|
-
if strings.Contains(text, "[private]") {
|
|
42
|
-
private = true
|
|
43
|
-
}
|
|
44
|
-
if m := justDocAttribute.FindStringSubmatch(text); m != nil {
|
|
45
|
-
doc = m[2]
|
|
46
|
-
}
|
|
47
|
-
top = j
|
|
48
|
-
}
|
|
49
|
-
if doc == "" {
|
|
50
|
-
doc = commentAbove(lines, top, "#")
|
|
51
|
-
}
|
|
52
|
-
if private {
|
|
53
|
-
continue
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
var body []string
|
|
57
|
-
for j := i + 1; j < len(lines); j++ {
|
|
58
|
-
next := lines[j]
|
|
59
|
-
if strings.HasPrefix(next, " ") || strings.HasPrefix(next, "\t") {
|
|
60
|
-
body = append(body, strings.TrimSpace(next))
|
|
61
|
-
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
if strings.TrimSpace(next) == "" {
|
|
65
|
-
continue
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
break
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
out = append(out, catalog.Command{
|
|
72
|
-
Runner: "just",
|
|
73
|
-
Name: name,
|
|
74
|
-
Run: "just " + name,
|
|
75
|
-
Doc: doc,
|
|
76
|
-
Body: strings.Join(body, "\n"),
|
|
77
|
-
Source: at(file, i+1),
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return out
|
|
82
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
package commands
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"strings"
|
|
5
|
-
|
|
6
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
// readMakefile lists the targets of a Makefile a person would type.
|
|
10
|
-
//
|
|
11
|
-
// A target is a line at column 0 with a colon that is not an assignment. Left
|
|
12
|
-
// out: special targets (`.PHONY`), pattern and variable-spelled ones (`%.o`,
|
|
13
|
-
// `$(BIN)`), and the `_`-prefixed ones a Makefile keeps for itself. The
|
|
14
|
-
// description is the `## comment` on the target's own line - the convention
|
|
15
|
-
// every `make help` recipe on the internet greps for - or, failing that, the
|
|
16
|
-
// comment block sat directly over it. The recipe is the body.
|
|
17
|
-
func readMakefile(file, src string) []catalog.Command {
|
|
18
|
-
lines := strings.Split(src, "\n")
|
|
19
|
-
var out []catalog.Command
|
|
20
|
-
for i := 0; i < len(lines); i++ {
|
|
21
|
-
line := lines[i]
|
|
22
|
-
names, rest, ok := makeTargets(line)
|
|
23
|
-
if !ok {
|
|
24
|
-
continue
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
doc := ""
|
|
28
|
-
if _, help, found := strings.Cut(rest, "##"); found {
|
|
29
|
-
doc = strings.TrimSpace(help)
|
|
30
|
-
} else {
|
|
31
|
-
doc = commentAbove(lines, i, "#")
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
var recipe []string
|
|
35
|
-
for j := i + 1; j < len(lines); j++ {
|
|
36
|
-
next := lines[j]
|
|
37
|
-
if strings.HasPrefix(next, "\t") {
|
|
38
|
-
recipe = append(recipe, strings.TrimSpace(strings.TrimPrefix(next, "\t")))
|
|
39
|
-
|
|
40
|
-
continue
|
|
41
|
-
}
|
|
42
|
-
if strings.TrimSpace(next) == "" || strings.HasPrefix(strings.TrimSpace(next), "#") {
|
|
43
|
-
continue
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
break
|
|
47
|
-
}
|
|
48
|
-
body := strings.Join(recipe, "\n")
|
|
49
|
-
|
|
50
|
-
for _, name := range names {
|
|
51
|
-
if !typeable(name) {
|
|
52
|
-
continue
|
|
53
|
-
}
|
|
54
|
-
out = append(out, catalog.Command{
|
|
55
|
-
Runner: "make",
|
|
56
|
-
Name: name,
|
|
57
|
-
Run: "make " + name,
|
|
58
|
-
Doc: doc,
|
|
59
|
-
Body: body,
|
|
60
|
-
Source: at(file, i+1),
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return out
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// makeTargets reads `a b: deps ## help` into its names and the rest of the
|
|
69
|
-
// line, and refuses anything that is not a rule: assignments, directives,
|
|
70
|
-
// recipes, comments.
|
|
71
|
-
func makeTargets(line string) ([]string, string, bool) {
|
|
72
|
-
if line == "" || line[0] == '\t' || line[0] == '#' || line[0] == ' ' {
|
|
73
|
-
return nil, "", false
|
|
74
|
-
}
|
|
75
|
-
colon := strings.IndexByte(line, ':')
|
|
76
|
-
if colon <= 0 {
|
|
77
|
-
return nil, "", false
|
|
78
|
-
}
|
|
79
|
-
head := line[:colon]
|
|
80
|
-
tail := line[colon+1:]
|
|
81
|
-
// `X := 1`, `X ::= 1`, and `X = 1` (no colon at all) are assignments.
|
|
82
|
-
if strings.HasPrefix(tail, "=") || strings.HasPrefix(tail, ":=") || strings.ContainsAny(head, "=#") {
|
|
83
|
-
return nil, "", false
|
|
84
|
-
}
|
|
85
|
-
tail = strings.TrimPrefix(tail, ":")
|
|
86
|
-
names := strings.Fields(head)
|
|
87
|
-
if len(names) == 0 {
|
|
88
|
-
return nil, "", false
|
|
89
|
-
}
|
|
90
|
-
for _, name := range names {
|
|
91
|
-
switch {
|
|
92
|
-
case strings.HasPrefix(name, "."), strings.ContainsAny(name, "%$()"):
|
|
93
|
-
return nil, "", false
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
switch names[0] {
|
|
97
|
-
case "ifeq", "ifneq", "ifdef", "ifndef", "else", "endif", "include", "-include", "define", "endef", "export", "unexport", "override", "vpath":
|
|
98
|
-
return nil, "", false
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return names, tail, true
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
func typeable(name string) bool {
|
|
105
|
-
return name != "" && !strings.HasPrefix(name, "_")
|
|
106
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
package commands
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"os"
|
|
5
|
-
"path/filepath"
|
|
6
|
-
"regexp"
|
|
7
|
-
"strings"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
// mavenGoals are the goals a person types, by the plugin that declares them.
|
|
13
|
-
// Maven has no task list of its own: the pom declares plugins, and a plugin's
|
|
14
|
-
// goals are either bound to a lifecycle phase - run by `mvn package`, never
|
|
15
|
-
// typed - or offered to be typed, and only the plugin's documentation says
|
|
16
|
-
// which. This is that documentation, for the plugins an estate meets.
|
|
17
|
-
var mavenGoals = map[string][]struct{ goal, doc string }{
|
|
18
|
-
"spring-boot-maven-plugin": {{"spring-boot:run", "Run the application"}},
|
|
19
|
-
"quarkus-maven-plugin": {{"quarkus:dev", "Run the application in dev mode, reloading on change"}},
|
|
20
|
-
"flyway-maven-plugin": {{"flyway:migrate", "Apply pending migrations"}},
|
|
21
|
-
"liquibase-maven-plugin": {{"liquibase:update", "Apply pending changesets"}},
|
|
22
|
-
"exec-maven-plugin": {{"exec:java", "Run the main class"}},
|
|
23
|
-
"jib-maven-plugin": {{"jib:dockerBuild", "Build the container image into the local Docker daemon"}},
|
|
24
|
-
"docker-maven-plugin": {{"docker:build", "Build the container image"}},
|
|
25
|
-
"openapi-generator-maven-plugin": {{"openapi-generator:generate", "Regenerate code from the OpenAPI document"}},
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
var pomArtifact = regexp.MustCompile(`<artifactId>\s*([^<\s]+)\s*</artifactId>`)
|
|
29
|
-
|
|
30
|
-
// readPom lists what a Maven project answers to: the two lifecycle phases
|
|
31
|
-
// every pom accepts, and the goals of the plugins it declares under
|
|
32
|
-
// `<build><plugins>`. The line is typed at the wrapper when the checkout
|
|
33
|
-
// keeps one, because that is the Maven the project was built with.
|
|
34
|
-
func readPom(root, file, src string) []catalog.Command {
|
|
35
|
-
runner, prefix := "mvn", "mvn "
|
|
36
|
-
if _, err := os.Stat(filepath.Join(root, "mvnw")); err == nil {
|
|
37
|
-
runner, prefix = "./mvnw", "./mvnw "
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
lines := strings.Split(src, "\n")
|
|
41
|
-
self := 1
|
|
42
|
-
for i, line := range lines {
|
|
43
|
-
if strings.Contains(line, "<packaging>") {
|
|
44
|
-
self = i + 1
|
|
45
|
-
|
|
46
|
-
break
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
out := []catalog.Command{
|
|
51
|
-
{Runner: runner, Name: "test", Run: prefix + "test", Doc: "Run the tests (lifecycle phase)", Source: at(file, self)},
|
|
52
|
-
{Runner: runner, Name: "package", Run: prefix + "package", Doc: "Build the artifact (lifecycle phase)", Source: at(file, self)},
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Only the build's own plugins, not the ones a dependency's pom names
|
|
56
|
-
// or the ones under <pluginManagement>, which pin a version without
|
|
57
|
-
// running anything.
|
|
58
|
-
build := section(src, "build")
|
|
59
|
-
if build == "" {
|
|
60
|
-
return out
|
|
61
|
-
}
|
|
62
|
-
plugins := section(strings.Replace(build, section(build, "pluginManagement"), "", 1), "plugins")
|
|
63
|
-
offset := strings.Index(src, plugins)
|
|
64
|
-
seen := map[string]bool{}
|
|
65
|
-
for _, m := range pomArtifact.FindAllStringSubmatchIndex(plugins, -1) {
|
|
66
|
-
artifact := plugins[m[2]:m[3]]
|
|
67
|
-
goals, known := mavenGoals[artifact]
|
|
68
|
-
if !known || seen[artifact] {
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
seen[artifact] = true
|
|
72
|
-
line := 1 + strings.Count(src[:offset+m[2]], "\n")
|
|
73
|
-
for _, g := range goals {
|
|
74
|
-
out = append(out, catalog.Command{Runner: runner, Name: g.goal, Run: prefix + g.goal, Doc: g.doc, Source: at(file, line)})
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return out
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// section is the text of the first `<name>...</name>` element, or "".
|
|
82
|
-
func section(src, name string) string {
|
|
83
|
-
open := strings.Index(src, "<"+name+">")
|
|
84
|
-
if open < 0 {
|
|
85
|
-
return ""
|
|
86
|
-
}
|
|
87
|
-
close := strings.Index(src[open:], "</"+name+">")
|
|
88
|
-
if close < 0 {
|
|
89
|
-
return ""
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return src[open : open+close+len(name)+3]
|
|
93
|
-
}
|