@shortlink-org/portolan 0.1.0 → 0.2.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.
Files changed (381) hide show
  1. package/README.md +69 -205
  2. package/cli/init.mjs +285 -0
  3. package/cli/init.test.mjs +158 -0
  4. package/cli/portolan.mjs +37 -73
  5. package/index.html +19 -0
  6. package/package.json +5 -10
  7. package/plugins/README.md +66 -27
  8. package/plugins/portolan-go.wasm +0 -0
  9. package/portolan.json +52 -73
  10. package/public/landing/cats/bug-hunter.webp +0 -0
  11. package/public/landing/cats/compass-nap.webp +0 -0
  12. package/public/landing/cats/diagrammer.webp +0 -0
  13. package/public/landing/cats/docs-reader.webp +0 -0
  14. package/public/landing/cats/laptop.webp +0 -0
  15. package/public/landing/cats/map-inspector.webp +0 -0
  16. package/public/landing/cats/server-break.webp +0 -0
  17. package/public/landing/cats/star-mapper.webp +0 -0
  18. package/public/landing/cats/system-builder.webp +0 -0
  19. package/public/landing/cats/thread-tangle.webp +0 -0
  20. package/public/og.png +0 -0
  21. package/public/readme-header.webp +0 -0
  22. package/schema/portolan.schema.json +180 -0
  23. package/scripts/builtin-plugins.mjs +14 -0
  24. package/scripts/delivery-presets.mjs +353 -0
  25. package/scripts/gen.mjs +37 -4
  26. package/scripts/history.mjs +126 -0
  27. package/scripts/history.test.mjs +110 -0
  28. package/scripts/host-plugins/fetch-bsr.mjs +351 -0
  29. package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
  30. package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
  31. package/scripts/host-plugins/fetch-csr.mjs +386 -0
  32. package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
  33. package/scripts/host-plugins/fetch-git.mjs +379 -0
  34. package/scripts/host-plugins/fetch-git.test.mjs +210 -0
  35. package/scripts/local-api.mjs +22 -4
  36. package/scripts/local-api.test.mjs +84 -2
  37. package/scripts/manifest.mjs +3 -2
  38. package/scripts/package-smoke.mjs +6 -2
  39. package/scripts/plugin-host.mjs +77 -18
  40. package/scripts/plugin-host.test.mjs +50 -1
  41. package/scripts/plugin-wasm-worker.mjs +6 -3
  42. package/scripts/run-builtin.mjs +6 -26
  43. package/scripts/schema.mjs +6 -1
  44. package/src/app/App.tsx +24 -429
  45. package/src/app/Breadcrumbs.test.ts +7 -0
  46. package/src/app/Breadcrumbs.tsx +15 -2
  47. package/src/app/CatalogApp.tsx +436 -0
  48. package/src/app/Sidebar.tsx +0 -1
  49. package/src/chat/ChatPanel.tsx +98 -41
  50. package/src/chat/Composer.tsx +4 -2
  51. package/src/chat/Conversation.tsx +79 -15
  52. package/src/chat/Header.tsx +43 -9
  53. package/src/chat/Starter.tsx +64 -3
  54. package/src/chat/Steps.tsx +1 -1
  55. package/src/chat/flags.test.ts +51 -14
  56. package/src/chat/flags.ts +8 -2
  57. package/src/chat/page-context.test.ts +51 -0
  58. package/src/chat/page-context.ts +128 -0
  59. package/src/chat/prompt.test.ts +49 -1
  60. package/src/chat/prompt.ts +42 -1
  61. package/src/chat/tools.ts +7 -2
  62. package/src/chat/transport.ts +16 -4
  63. package/src/components/ProblemRow.tsx +182 -0
  64. package/src/data.ts +20 -5
  65. package/src/er/ErCanvas.tsx +1 -1
  66. package/src/graph/DependencyGraph.tsx +10 -1
  67. package/src/graph/FocusedEventGraph.tsx +1 -1
  68. package/src/graph/GraphToolbar.tsx +2 -4
  69. package/src/index.css +220 -2
  70. package/src/landing/DraggableReveal.tsx +168 -0
  71. package/src/landing/EstateGraph.tsx +28 -0
  72. package/src/landing/FlowPlayback.tsx +282 -0
  73. package/src/landing/HeroMap.tsx +128 -0
  74. package/src/landing/LandingPage.tsx +757 -0
  75. package/src/landing/ProductFrame.tsx +68 -0
  76. package/src/landing/ProductTour.tsx +371 -0
  77. package/src/landing/catalog.ts +25 -0
  78. package/src/landing/motion.tsx +52 -0
  79. package/src/lib/all-problems.ts +28 -0
  80. package/src/lib/local-api.ts +35 -1
  81. package/src/lib/setup-info.test.ts +8 -0
  82. package/src/lib/setup-info.ts +3 -2
  83. package/src/lib/source-code.ts +2 -1
  84. package/src/likec4/C4View.tsx +5 -0
  85. package/src/likec4/InteractiveView.tsx +5 -1
  86. package/src/map/ContextMapGraph.tsx +47 -20
  87. package/src/pages/ContextMap.tsx +5 -1
  88. package/src/pages/FlowDetail.tsx +24 -7
  89. package/src/pages/Overview.tsx +68 -12
  90. package/src/pages/Problems.tsx +6 -193
  91. package/src/pages/Settings.tsx +133 -108
  92. package/src/pages/settings/DeliverySettings.tsx +229 -0
  93. package/src/pages/settings/PreferencesSettings.tsx +101 -0
  94. package/src/routes.test.ts +20 -0
  95. package/src/routes.ts +13 -1
  96. package/vite.config.ts +12 -8
  97. package/catalog/enum_test.go +0 -46
  98. package/catalog/model.go +0 -932
  99. package/catalog/roundtrip_test.go +0 -185
  100. package/catalog/via_test.go +0 -38
  101. package/go.mod +0 -14
  102. package/go.sum +0 -14
  103. package/internal/commands/cargo.go +0 -154
  104. package/internal/commands/commands.go +0 -143
  105. package/internal/commands/commands_test.go +0 -323
  106. package/internal/commands/gradle.go +0 -77
  107. package/internal/commands/justfile.go +0 -82
  108. package/internal/commands/makefile.go +0 -106
  109. package/internal/commands/maven.go +0 -93
  110. package/internal/commands/packagejson.go +0 -159
  111. package/internal/commands/pyproject.go +0 -169
  112. package/internal/commands/taskfile.go +0 -101
  113. package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
  114. package/internal/commands/testdata/estate/Makefile +0 -33
  115. package/internal/commands/testdata/estate/Taskfile.yml +0 -21
  116. package/internal/commands/testdata/estate/build.gradle.kts +0 -21
  117. package/internal/commands/testdata/estate/justfile +0 -20
  118. package/internal/commands/testdata/estate/package.json +0 -13
  119. package/internal/commands/testdata/estate/pom.xml +0 -29
  120. package/internal/commands/testdata/estate/pyproject.toml +0 -27
  121. package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
  122. package/internal/commands/testdata/golden/commands.json +0 -305
  123. package/internal/gohttp/analyze.go +0 -2500
  124. package/internal/gohttp/endpoints.go +0 -1067
  125. package/internal/gohttp/roots.go +0 -320
  126. package/internal/gohttp/typed.go +0 -143
  127. package/internal/goscan/constants.go +0 -85
  128. package/internal/goscan/goscan_test.go +0 -227
  129. package/internal/goscan/names.go +0 -52
  130. package/internal/goscan/parse_test.go +0 -11
  131. package/internal/goscan/source.go +0 -36
  132. package/internal/goscan/tree.go +0 -155
  133. package/internal/goscan/types.go +0 -99
  134. package/internal/wsdl/ids.go +0 -127
  135. package/internal/wsdl/ids_test.go +0 -21
  136. package/internal/wsdl/model.go +0 -62
  137. package/internal/wsdl/parse.go +0 -920
  138. package/internal/wsdl/parse_test.go +0 -133
  139. package/plugin/describe.go +0 -107
  140. package/plugin/describe_test.go +0 -114
  141. package/plugin/protocol.go +0 -117
  142. package/plugin/schematest/schematest.go +0 -126
  143. package/plugins/extract-adr/describe.go +0 -19
  144. package/plugins/extract-adr/describe_test.go +0 -11
  145. package/plugins/extract-adr/extract.go +0 -153
  146. package/plugins/extract-adr/extract_test.go +0 -350
  147. package/plugins/extract-adr/history.go +0 -99
  148. package/plugins/extract-adr/main.go +0 -65
  149. package/plugins/extract-adr/parse.go +0 -642
  150. package/plugins/extract-adr/parse_test.go +0 -405
  151. package/plugins/extract-asyncapi/describe.go +0 -19
  152. package/plugins/extract-asyncapi/describe_test.go +0 -11
  153. package/plugins/extract-asyncapi/extract.go +0 -315
  154. package/plugins/extract-asyncapi/extract_test.go +0 -197
  155. package/plugins/extract-asyncapi/main.go +0 -48
  156. package/plugins/extract-asyncapi/spec.go +0 -151
  157. package/plugins/extract-commands/describe.go +0 -19
  158. package/plugins/extract-commands/describe_test.go +0 -11
  159. package/plugins/extract-commands/extract.go +0 -79
  160. package/plugins/extract-commands/extract_test.go +0 -92
  161. package/plugins/extract-commands/main.go +0 -55
  162. package/plugins/extract-csr/avro.go +0 -258
  163. package/plugins/extract-csr/describe.go +0 -19
  164. package/plugins/extract-csr/describe_test.go +0 -11
  165. package/plugins/extract-csr/extract.go +0 -338
  166. package/plugins/extract-csr/extract_test.go +0 -374
  167. package/plugins/extract-csr/jsonschema.go +0 -343
  168. package/plugins/extract-csr/lock.go +0 -25
  169. package/plugins/extract-csr/main.go +0 -84
  170. package/plugins/extract-csr/subject.go +0 -102
  171. package/plugins/extract-flows/describe.go +0 -19
  172. package/plugins/extract-flows/describe_test.go +0 -11
  173. package/plugins/extract-flows/extract.go +0 -91
  174. package/plugins/extract-flows/main.go +0 -45
  175. package/plugins/extract-flows/parse.go +0 -593
  176. package/plugins/extract-flows/parse_test.go +0 -204
  177. package/plugins/extract-glossary/describe.go +0 -19
  178. package/plugins/extract-glossary/describe_test.go +0 -11
  179. package/plugins/extract-glossary/extract.go +0 -115
  180. package/plugins/extract-glossary/extract_test.go +0 -220
  181. package/plugins/extract-glossary/main.go +0 -59
  182. package/plugins/extract-glossary/parse.go +0 -214
  183. package/plugins/extract-glossary/parse_test.go +0 -203
  184. package/plugins/extract-go/aggregate.go +0 -214
  185. package/plugins/extract-go/client.go +0 -409
  186. package/plugins/extract-go/client_test.go +0 -266
  187. package/plugins/extract-go/describe.go +0 -19
  188. package/plugins/extract-go/describe_test.go +0 -11
  189. package/plugins/extract-go/enum.go +0 -195
  190. package/plugins/extract-go/enum_test.go +0 -82
  191. package/plugins/extract-go/event.go +0 -99
  192. package/plugins/extract-go/extract.go +0 -191
  193. package/plugins/extract-go/extract_test.go +0 -261
  194. package/plugins/extract-go/flow.go +0 -1441
  195. package/plugins/extract-go/flow_test.go +0 -609
  196. package/plugins/extract-go/httpclient.go +0 -174
  197. package/plugins/extract-go/httpclient_test.go +0 -296
  198. package/plugins/extract-go/ids.go +0 -92
  199. package/plugins/extract-go/layout.go +0 -225
  200. package/plugins/extract-go/layout_test.go +0 -125
  201. package/plugins/extract-go/lifecycle.go +0 -324
  202. package/plugins/extract-go/lifecycle_test.go +0 -108
  203. package/plugins/extract-go/main.go +0 -79
  204. package/plugins/extract-go/operation.go +0 -157
  205. package/plugins/extract-go/source.go +0 -316
  206. package/plugins/extract-go/transport.go +0 -321
  207. package/plugins/extract-go/transport_test.go +0 -145
  208. package/plugins/extract-go/wiring.go +0 -434
  209. package/plugins/extract-go-nats/describe.go +0 -19
  210. package/plugins/extract-go-nats/describe_test.go +0 -11
  211. package/plugins/extract-go-nats/extract.go +0 -177
  212. package/plugins/extract-go-nats/extract_test.go +0 -389
  213. package/plugins/extract-go-nats/index.go +0 -379
  214. package/plugins/extract-go-nats/main.go +0 -42
  215. package/plugins/extract-go-nats/resolve.go +0 -161
  216. package/plugins/extract-go-nats/sites.go +0 -224
  217. package/plugins/extract-graphql/describe.go +0 -19
  218. package/plugins/extract-graphql/describe_test.go +0 -11
  219. package/plugins/extract-graphql/extract.go +0 -433
  220. package/plugins/extract-graphql/extract_test.go +0 -256
  221. package/plugins/extract-graphql/ids.go +0 -49
  222. package/plugins/extract-graphql/lex.go +0 -237
  223. package/plugins/extract-graphql/main.go +0 -51
  224. package/plugins/extract-graphql/parse.go +0 -621
  225. package/plugins/extract-graphql/parse_test.go +0 -122
  226. package/plugins/extract-http-clients/describe.go +0 -19
  227. package/plugins/extract-http-clients/describe_test.go +0 -11
  228. package/plugins/extract-http-clients/extract.go +0 -705
  229. package/plugins/extract-http-clients/extract_test.go +0 -1263
  230. package/plugins/extract-http-clients/main.go +0 -42
  231. package/plugins/extract-openapi/describe.go +0 -19
  232. package/plugins/extract-openapi/describe_test.go +0 -11
  233. package/plugins/extract-openapi/discover.go +0 -243
  234. package/plugins/extract-openapi/extract.go +0 -526
  235. package/plugins/extract-openapi/extract_test.go +0 -545
  236. package/plugins/extract-openapi/main.go +0 -75
  237. package/plugins/extract-openapi/spec.go +0 -350
  238. package/plugins/extract-project/describe.go +0 -19
  239. package/plugins/extract-project/describe_test.go +0 -11
  240. package/plugins/extract-project/extract.go +0 -221
  241. package/plugins/extract-project/extract_test.go +0 -109
  242. package/plugins/extract-project/main.go +0 -41
  243. package/plugins/extract-proto/ast.go +0 -125
  244. package/plugins/extract-proto/consumes.go +0 -77
  245. package/plugins/extract-proto/describe.go +0 -19
  246. package/plugins/extract-proto/extract.go +0 -293
  247. package/plugins/extract-proto/extract_test.go +0 -459
  248. package/plugins/extract-proto/ids.go +0 -89
  249. package/plugins/extract-proto/ids_test.go +0 -57
  250. package/plugins/extract-proto/lex.go +0 -285
  251. package/plugins/extract-proto/main.go +0 -100
  252. package/plugins/extract-proto/module.go +0 -120
  253. package/plugins/extract-proto/parse.go +0 -720
  254. package/plugins/extract-proto/parse_test.go +0 -307
  255. package/plugins/extract-proto/provides.go +0 -236
  256. package/plugins/extract-proto/resolve.go +0 -222
  257. package/plugins/extract-proto/resolve_test.go +0 -119
  258. package/plugins/extract-redis/describe.go +0 -19
  259. package/plugins/extract-redis/describe_test.go +0 -11
  260. package/plugins/extract-redis/extract.go +0 -183
  261. package/plugins/extract-redis/extract_test.go +0 -168
  262. package/plugins/extract-redis/keyspaces.go +0 -469
  263. package/plugins/extract-redis/main.go +0 -44
  264. package/plugins/extract-river/describe.go +0 -19
  265. package/plugins/extract-river/describe_test.go +0 -11
  266. package/plugins/extract-river/extract.go +0 -507
  267. package/plugins/extract-river/extract_test.go +0 -132
  268. package/plugins/extract-river/main.go +0 -42
  269. package/plugins/extract-sql/ddl.go +0 -893
  270. package/plugins/extract-sql/ddl_test.go +0 -401
  271. package/plugins/extract-sql/describe.go +0 -19
  272. package/plugins/extract-sql/describe_test.go +0 -11
  273. package/plugins/extract-sql/layout.go +0 -221
  274. package/plugins/extract-sql/layout_test.go +0 -55
  275. package/plugins/extract-sql/lineage.go +0 -107
  276. package/plugins/extract-sql/main.go +0 -141
  277. package/plugins/extract-sql/maps.go +0 -564
  278. package/plugins/extract-sql/maps_java.go +0 -117
  279. package/plugins/extract-sql/maps_rust.go +0 -333
  280. package/plugins/extract-sql/maps_rust_test.go +0 -70
  281. package/plugins/extract-sql/maps_test.go +0 -204
  282. package/plugins/extract-sql/maps_ts.go +0 -398
  283. package/plugins/extract-sql/maps_ts_test.go +0 -136
  284. package/plugins/extract-sql/projection.go +0 -70
  285. package/plugins/extract-sql/projection_test.go +0 -50
  286. package/plugins/extract-sql/store.go +0 -420
  287. package/plugins/extract-sql/store_test.go +0 -233
  288. package/plugins/extract-sql/view.go +0 -295
  289. package/plugins/extract-watermill/describe.go +0 -19
  290. package/plugins/extract-watermill/describe_test.go +0 -11
  291. package/plugins/extract-watermill/extract.go +0 -1228
  292. package/plugins/extract-watermill/extract_test.go +0 -234
  293. package/plugins/extract-watermill/main.go +0 -41
  294. package/plugins/extract-wsdl/describe.go +0 -19
  295. package/plugins/extract-wsdl/describe_test.go +0 -11
  296. package/plugins/extract-wsdl/extract.go +0 -148
  297. package/plugins/extract-wsdl/extract_test.go +0 -45
  298. package/plugins/extract-wsdl/main.go +0 -53
  299. package/plugins/fetch-bsr/auth.go +0 -114
  300. package/plugins/fetch-bsr/bsr.go +0 -240
  301. package/plugins/fetch-bsr/cache.go +0 -66
  302. package/plugins/fetch-bsr/describe.go +0 -19
  303. package/plugins/fetch-bsr/fetch.go +0 -192
  304. package/plugins/fetch-bsr/fetch_test.go +0 -465
  305. package/plugins/fetch-bsr/lock.go +0 -71
  306. package/plugins/fetch-bsr/main.go +0 -117
  307. package/plugins/fetch-csr/auth.go +0 -95
  308. package/plugins/fetch-csr/cache.go +0 -69
  309. package/plugins/fetch-csr/describe.go +0 -19
  310. package/plugins/fetch-csr/describe_test.go +0 -11
  311. package/plugins/fetch-csr/fetch.go +0 -254
  312. package/plugins/fetch-csr/fetch_test.go +0 -494
  313. package/plugins/fetch-csr/lock.go +0 -95
  314. package/plugins/fetch-csr/main.go +0 -122
  315. package/plugins/fetch-csr/registry.go +0 -209
  316. package/plugins/fetch-git/cache.go +0 -65
  317. package/plugins/fetch-git/describe.go +0 -19
  318. package/plugins/fetch-git/describe_test.go +0 -11
  319. package/plugins/fetch-git/fetch.go +0 -181
  320. package/plugins/fetch-git/fetch_test.go +0 -332
  321. package/plugins/fetch-git/git.go +0 -169
  322. package/plugins/fetch-git/lock.go +0 -72
  323. package/plugins/fetch-git/main.go +0 -127
  324. package/plugins/fetch-git/offline.go +0 -39
  325. package/plugins/fetch-git/pin.go +0 -90
  326. package/plugins/fetch-git/pin_test.go +0 -74
  327. package/plugins/gen-backstage/describe.go +0 -17
  328. package/plugins/gen-backstage/main.go +0 -22
  329. package/plugins/gen-backstage/plugin.go +0 -473
  330. package/plugins/gen-backstage/plugin_test.go +0 -145
  331. package/plugins/gen-backstage.wasm +0 -0
  332. package/plugins/gen-markdown/adr.go +0 -162
  333. package/plugins/gen-markdown/aggregate.go +0 -362
  334. package/plugins/gen-markdown/canonical.go +0 -93
  335. package/plugins/gen-markdown/context.go +0 -90
  336. package/plugins/gen-markdown/coverage_test.go +0 -89
  337. package/plugins/gen-markdown/describe.go +0 -19
  338. package/plugins/gen-markdown/describe_test.go +0 -11
  339. package/plugins/gen-markdown/external.go +0 -71
  340. package/plugins/gen-markdown/flow.go +0 -277
  341. package/plugins/gen-markdown/glossary.go +0 -76
  342. package/plugins/gen-markdown/glossary_test.go +0 -148
  343. package/plugins/gen-markdown/llms.go +0 -302
  344. package/plugins/gen-markdown/main.go +0 -22
  345. package/plugins/gen-markdown/markdown.go +0 -254
  346. package/plugins/gen-markdown/markdown_test.go +0 -100
  347. package/plugins/gen-markdown/module.go +0 -107
  348. package/plugins/gen-markdown/plugin.go +0 -39
  349. package/plugins/gen-markdown/quality_test.go +0 -176
  350. package/plugins/gen-markdown/redis_test.go +0 -38
  351. package/plugins/gen-markdown/render.go +0 -344
  352. package/plugins/gen-markdown/render_test.go +0 -186
  353. package/plugins/gen-markdown/service.go +0 -456
  354. package/plugins/gen-markdown/source.go +0 -177
  355. package/plugins/gen-markdown/store.go +0 -201
  356. package/plugins/gen-markdown.wasm +0 -0
  357. package/plugins/gen-mermaid/describe.go +0 -17
  358. package/plugins/gen-mermaid/main.go +0 -22
  359. package/plugins/gen-mermaid/plugin.go +0 -104
  360. package/plugins/gen-mermaid/plugin_test.go +0 -33
  361. package/plugins/gen-mermaid.wasm +0 -0
  362. package/plugins/openapi/ids.go +0 -261
  363. package/plugins/openapi/ids_test.go +0 -98
  364. package/plugins/verify-codeowners/describe.go +0 -19
  365. package/plugins/verify-codeowners/describe_test.go +0 -11
  366. package/plugins/verify-codeowners/main.go +0 -75
  367. package/plugins/verify-codeowners/match.go +0 -85
  368. package/plugins/verify-codeowners/match_test.go +0 -47
  369. package/plugins/verify-codeowners/owners.go +0 -164
  370. package/plugins/verify-codeowners/owners_test.go +0 -225
  371. package/plugins/verify-codeowners/parse.go +0 -90
  372. package/plugins/verify-codeowners/parse_test.go +0 -62
  373. package/plugins/verify-otel/describe.go +0 -19
  374. package/plugins/verify-otel/describe_test.go +0 -11
  375. package/plugins/verify-otel/main.go +0 -53
  376. package/plugins/verify-otel/match.go +0 -336
  377. package/plugins/verify-otel/otlp.go +0 -200
  378. package/plugins/verify-otel/verify.go +0 -734
  379. package/plugins/verify-otel/verify_test.go +0 -460
  380. /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
  381. /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
- }