feat: manual for individual modules
Refactor CLI to group module related commands together. Extend HCL writing support to support slices of complex types including slices of blocks with labels.
This commit is contained in:
parent
0f91cf6c73
commit
73ef9e5135
|
@ -1,9 +1,9 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="__complete" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="buildr" />
|
||||
<working_directory value="$PROJECT_DIR$/buildr" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<go_parameters value="--race" />
|
||||
<parameters value="__complete new task " />
|
||||
<parameters value="__complete modules man" />
|
||||
<EXTENSION ID="net.ashald.envfile">
|
||||
<option name="IS_ENABLED" value="false" />
|
||||
<option name="IS_SUBST" value="false" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="new plugin" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="buildr" />
|
||||
<working_directory value="$PROJECT_DIR$/buildr" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<parameters value="new task hello_world asdf" />
|
||||
<EXTENSION ID="net.ashald.envfile">
|
||||
<option name="IS_ENABLED" value="false" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="task go_fmt" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="buildr" />
|
||||
<working_directory value="$PROJECT_DIR$/buildr" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<parameters value="--pprof.cpu-profile --pprof.out-file cpu.profile task go_fmt" />
|
||||
<EXTENSION ID="net.ashald.envfile">
|
||||
<option name="IS_ENABLED" value="false" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="task wasi_hello_world" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="buildr" />
|
||||
<working_directory value="$PROJECT_DIR$/buildr" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<parameters value="--pprof.cpu-profile --pprof.out-file cpu.profile task wasi_hello_world" />
|
||||
<EXTENSION ID="net.ashald.envfile">
|
||||
<option name="IS_ENABLED" value="false" />
|
||||
|
|
16
assets/manual/single-module-man.tmpl.md
Normal file
16
assets/manual/single-module-man.tmpl.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# {{ .Name }}
|
||||
|
||||
{{ .Description }}
|
||||
|
||||
{{- if .Examples }}
|
||||
## Examples
|
||||
{{ range .Examples }}
|
||||
### {{ .Name }}
|
||||
|
||||
{{ .Description }}
|
||||
|
||||
```hcl
|
||||
{{ to_hcl .Spec }}
|
||||
```
|
||||
{{- end }}
|
||||
{{- end }}
|
8
go.mod
8
go.mod
|
@ -48,6 +48,9 @@ require (
|
|||
cloud.google.com/go/iam v1.1.0 // indirect
|
||||
cloud.google.com/go/storage v1.30.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
|
@ -86,6 +89,7 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
|
@ -98,9 +102,11 @@ require (
|
|||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.24 // indirect
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
|
@ -116,8 +122,10 @@ require (
|
|||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/sergi/go-diff v1.3.1 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.1.1 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
|
|
20
go.sum
20
go.sum
|
@ -432,6 +432,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOC
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
|
@ -696,6 +702,7 @@ github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA
|
|||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
@ -742,9 +749,12 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
|
|||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
|
||||
github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
|
||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
|
@ -821,6 +831,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr
|
|||
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
|
||||
github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw=
|
||||
github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
|
@ -829,6 +841,8 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
|
|||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mmcloughlin/avo v0.5.0 h1:nAco9/aI9Lg2kiuROBY6BhCI/z0t5jEvJfjWbL8qXLU=
|
||||
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
||||
|
@ -898,6 +912,8 @@ github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
|||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
|
@ -907,6 +923,8 @@ github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIn
|
|||
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
|
@ -990,6 +1008,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
|
@ -1485,6 +1504,7 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
|||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -51,6 +51,14 @@ type AppConfigAccessor interface {
|
|||
AppConfig() AppConfig
|
||||
}
|
||||
|
||||
type TypeRegistryAccessor interface {
|
||||
TypeRegistry() *modules.TypeRegistry
|
||||
}
|
||||
|
||||
type ModuleRepositoryAccessor interface {
|
||||
Repository() *modules.Repository
|
||||
}
|
||||
|
||||
type VaultCommander interface {
|
||||
Init(cfg VaultInitConfig) error
|
||||
List(writer io.Writer) error
|
||||
|
@ -80,6 +88,7 @@ type KnownModulesArgProvider interface {
|
|||
}
|
||||
|
||||
type ManCommander interface {
|
||||
DisplayModuleManual(pager Pager, cat modules.Category, name string) error
|
||||
DisplayModulesManual(pager Pager) error
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,12 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/slices"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/config"
|
||||
"code.icb4dc0.de/buildr/buildr/internal/plugins"
|
||||
|
||||
|
@ -38,7 +41,6 @@ import (
|
|||
hcl2 "github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
|
@ -82,60 +84,20 @@ func NewApp() *App {
|
|||
}
|
||||
|
||||
app.rootCmd.AddCommand(
|
||||
ModuleCommand(
|
||||
modules.CategoryTool,
|
||||
app,
|
||||
app.TasksArgsProviderFor(modules.CategoryTool),
|
||||
WithShort("Run a tool by its name"),
|
||||
),
|
||||
ModuleCommand(
|
||||
modules.CategoryTask,
|
||||
app,
|
||||
app.TasksArgsProviderFor(modules.CategoryTask),
|
||||
WithShort("Run a task by its name"),
|
||||
),
|
||||
ModuleCommand(
|
||||
modules.CategoryBuild,
|
||||
app,
|
||||
app.TasksArgsProviderFor(modules.CategoryBuild),
|
||||
WithShort("Run a build by its name"),
|
||||
),
|
||||
ModuleCommand(
|
||||
modules.CategoryPackage,
|
||||
app,
|
||||
app.TasksArgsProviderFor(modules.CategoryPackage),
|
||||
WithShort("Create a package by its name"),
|
||||
),
|
||||
NewCmd(
|
||||
BootstrapModuleCmd(
|
||||
modules.CategoryTool,
|
||||
slices.Map(modules.Categories(), func(c modules.Category) *cobra.Command {
|
||||
return ExecuteModuleCommand(
|
||||
c,
|
||||
app,
|
||||
app.ModulesArgsProviderFor(modules.CategoryTool),
|
||||
WithShort("Bootstrap tool module"),
|
||||
),
|
||||
BootstrapModuleCmd(
|
||||
modules.CategoryTask,
|
||||
app,
|
||||
app.ModulesArgsProviderFor(modules.CategoryTask),
|
||||
WithShort("Bootstrap task module"),
|
||||
),
|
||||
BootstrapModuleCmd(
|
||||
modules.CategoryBuild,
|
||||
app,
|
||||
app.ModulesArgsProviderFor(modules.CategoryBuild),
|
||||
WithShort("Bootstrap build module"),
|
||||
),
|
||||
BootstrapModuleCmd(
|
||||
modules.CategoryPackage,
|
||||
app,
|
||||
app.ModulesArgsProviderFor(modules.CategoryPackage),
|
||||
WithShort("Bootstrap package module"),
|
||||
),
|
||||
),
|
||||
TasksArgsProviderFor(app, app, c),
|
||||
WithShort(fmt.Sprintf("Run a %s by its name", strings.ToLower(c.String()))),
|
||||
)
|
||||
})...,
|
||||
)
|
||||
app.rootCmd.AddCommand(
|
||||
VaultCommand(NewVaultApp(app, app, app)),
|
||||
ServerCommand(NewServerApp(app, app)),
|
||||
EnvCommand(NewEnvApp(app, app)),
|
||||
NewManCmd(manApp),
|
||||
ModulesCommand(app, app, manApp, app),
|
||||
VersionCommand(),
|
||||
)
|
||||
|
||||
|
@ -259,6 +221,10 @@ func (a *App) BuildrConfig() config.Buildr {
|
|||
return *a.buildrCfg
|
||||
}
|
||||
|
||||
func (a *App) Repository() *modules.Repository {
|
||||
return a.repo
|
||||
}
|
||||
|
||||
func (a *App) BootstrapModule(cat modules.Category, typeName, moduleName string) error {
|
||||
if err := a.InitAt(InitLevelBuildRConfig); err != nil {
|
||||
return err
|
||||
|
@ -305,56 +271,6 @@ func (a *App) RunModule(ctx context.Context, cat modules.Category, name string)
|
|||
})
|
||||
}
|
||||
|
||||
func (a *App) ModulesArgsProviderFor(cat modules.Category) KnownModulesArgProvider {
|
||||
return KnownModulesArgProviderFunc(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if err := a.basicParseConfig(cmd.Context()); err != nil {
|
||||
slog.Warn("Failed to parse config", slog.String("err", err.Error()))
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
knownModules := a.TypeRegistry().Inventory()[cat]
|
||||
filtered := make([]string, 0, len(knownModules))
|
||||
toComplete = strings.ToLower(toComplete)
|
||||
|
||||
for i := range knownModules {
|
||||
if strings.HasPrefix(strings.ToLower(knownModules[i]), toComplete) {
|
||||
filtered = append(filtered, knownModules[i])
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
}
|
||||
|
||||
func (a *App) TasksArgsProviderFor(cat modules.Category) KnownTasksArgProvider {
|
||||
return KnownTasksArgProviderFunc(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if err := a.basicParseConfig(cmd.Context()); err != nil {
|
||||
slog.Warn("Failed to parse config", slog.String("err", err.Error()))
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
tasks := maps.Keys(a.repo.ModulesByCategory(cat))
|
||||
|
||||
filtered := make([]string, 0, len(tasks))
|
||||
|
||||
for i := range tasks {
|
||||
if strings.HasPrefix(tasks[i], toComplete) {
|
||||
filtered = append(filtered, tasks[i])
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
}
|
||||
|
||||
// basicParseConfig parses the config files without Vault and API clients.
|
||||
// It's primary purpose is to be able to list completions
|
||||
func (a *App) basicParseConfig(ctx context.Context) (err error) {
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"text/template"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/tmpl"
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"golang.org/x/exp/slog"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/services"
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
|
@ -21,14 +25,18 @@ type Pager interface {
|
|||
}
|
||||
|
||||
func NewManApp(templatesFs fs.FS, svcAcc ManAppServiceAccess) (*ManApp, error) {
|
||||
tmpl, err := template.New("man").
|
||||
parsedTemplates, err := template.New("man").
|
||||
Funcs(sprig.FuncMap()).
|
||||
Funcs(template.FuncMap{
|
||||
"to_hcl": tmpl.WriteToHcl,
|
||||
}).
|
||||
ParseFS(templatesFs, "manual/*.tmpl.md")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ManApp{
|
||||
templates: tmpl,
|
||||
templates: parsedTemplates,
|
||||
serviceAccess: svcAcc,
|
||||
}, nil
|
||||
}
|
||||
|
@ -38,6 +46,31 @@ type ManApp struct {
|
|||
serviceAccess ManAppServiceAccess
|
||||
}
|
||||
|
||||
func (m ManApp) DisplayModuleManual(pager Pager, cat modules.Category, name string) error {
|
||||
mod, err := m.serviceAccess.TypeRegistry().Create(cat, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var help modules.Help
|
||||
if h, ok := mod.Unwrap().(modules.Helper); !ok {
|
||||
slog.Info(
|
||||
"Module has no help",
|
||||
slog.String("category", cat.String()),
|
||||
slog.String("name", name),
|
||||
)
|
||||
return nil
|
||||
} else {
|
||||
help = h.Help()
|
||||
}
|
||||
|
||||
if err := m.templates.ExecuteTemplate(pager, "single-module-man.tmpl.md", help); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pager.Display()
|
||||
}
|
||||
|
||||
func (m ManApp) DisplayModulesManual(pager Pager) error {
|
||||
templateData := struct {
|
||||
Modules map[modules.Category][]string
|
||||
|
|
64
internal/cmd/args.go
Normal file
64
internal/cmd/args.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
func ModulesArgsProviderFor(
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
cat modules.Category,
|
||||
) KnownModulesArgProvider {
|
||||
return KnownModulesArgProviderFunc(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if err := initializer.InitAt(InitLevelBasic); err != nil {
|
||||
slog.Warn("Failed to parse config", slog.String("err", err.Error()))
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
knownModules := registryAcc.TypeRegistry().Inventory()[cat]
|
||||
filtered := make([]string, 0, len(knownModules))
|
||||
toComplete = strings.ToLower(toComplete)
|
||||
|
||||
for i := range knownModules {
|
||||
if strings.HasPrefix(strings.ToLower(knownModules[i]), toComplete) {
|
||||
filtered = append(filtered, knownModules[i])
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
}
|
||||
|
||||
func TasksArgsProviderFor(initializer LevelInitializer, repoAcc ModuleRepositoryAccessor, cat modules.Category) KnownTasksArgProvider {
|
||||
return KnownTasksArgProviderFunc(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if err := initializer.InitAt(InitLevelBasic); err != nil {
|
||||
slog.Warn("Failed to parse config", slog.String("err", err.Error()))
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
tasks := maps.Keys(repoAcc.Repository().ModulesByCategory(cat))
|
||||
|
||||
filtered := make([]string, 0, len(tasks))
|
||||
|
||||
for i := range tasks {
|
||||
if strings.HasPrefix(tasks[i], toComplete) {
|
||||
filtered = append(filtered, tasks[i])
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
}
|
|
@ -56,16 +56,17 @@ func EnsureLatestBinary(binariesDir string) (err error) {
|
|||
}
|
||||
|
||||
if info, err = buildinfo.ReadFile(expectedBuildrBinPath); err != nil {
|
||||
return err
|
||||
shouldCopy = true
|
||||
} else {
|
||||
existingBinaryVCSVersion = settingsToMap(info.Settings)[buildSettingsVCSRevision]
|
||||
shouldCopy = existingBinaryVCSVersion != thisVCSVersion
|
||||
}
|
||||
|
||||
existingBinaryVCSVersion = settingsToMap(info.Settings)[buildSettingsVCSRevision]
|
||||
|
||||
if existingBinaryVCSVersion == thisVCSVersion {
|
||||
return nil
|
||||
if shouldCopy {
|
||||
return copyCurrentBinaryToBinariesDir(expectedBuildrBinPath)
|
||||
}
|
||||
|
||||
return copyCurrentBinaryToBinariesDir(expectedBuildrBinPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyCurrentBinaryToBinariesDir(expectedBuildrBinPath string) (err error) {
|
||||
|
|
|
@ -13,7 +13,7 @@ func WithShort(short string) ModuleCommandOption {
|
|||
}
|
||||
}
|
||||
|
||||
func ModuleCommand(
|
||||
func ExecuteModuleCommand(
|
||||
category modules.Category,
|
||||
cmder ModuleCommander,
|
||||
argsProvider KnownTasksArgProvider,
|
|
@ -5,6 +5,11 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/slices"
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -45,12 +50,27 @@ const (
|
|||
PagerColorNever PagerColor = "never"
|
||||
)
|
||||
|
||||
type manConfig struct {
|
||||
func manCfgFromFlags(flags *pflag.FlagSet) (cfg *ManConfig, err error) {
|
||||
cfg = new(ManConfig)
|
||||
if cfg.PagerCommand, err = flags.GetString("pager.command"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pagerColor, err := flags.GetString("pager.color"); err != nil {
|
||||
return nil, err
|
||||
} else if err = cfg.ColorOutput.Set(pagerColor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
type ManConfig struct {
|
||||
ColorOutput PagerColor
|
||||
PagerCommand string
|
||||
}
|
||||
|
||||
func (m *manConfig) Flags() *flag.FlagSet {
|
||||
func (m *ManConfig) Flags() *flag.FlagSet {
|
||||
fs := flag.NewFlagSet("man", flag.ExitOnError)
|
||||
|
||||
fs.StringVar(&m.PagerCommand, "pager.command", config.StringEnvOr("BUILDR_PAGER", ""), "pager command - e.g. bat -l md")
|
||||
|
@ -59,31 +79,52 @@ func (m *manConfig) Flags() *flag.FlagSet {
|
|||
return fs
|
||||
}
|
||||
|
||||
func (m *manConfig) Pager(ctx context.Context) (Pager, error) {
|
||||
func (m *ManConfig) Pager(ctx context.Context, title string) (Pager, error) {
|
||||
switch m.PagerCommand {
|
||||
case "":
|
||||
return NewTUIPager()
|
||||
return NewTUIPager(title)
|
||||
default:
|
||||
return NewCmdPager(ctx, m.PagerCommand, m.ColorOutput == PagerColorAlways)
|
||||
}
|
||||
}
|
||||
|
||||
func NewManCmd(cmder ManCommander) *cobra.Command {
|
||||
var manCfg manConfig
|
||||
func ModuleManCommand(
|
||||
category modules.Category,
|
||||
cmder ManCommander,
|
||||
argsProvider KnownModulesArgProvider,
|
||||
manCfg *ManConfig,
|
||||
) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: category.String(),
|
||||
Short: fmt.Sprintf("Show manual pages for %s modules", category.String()),
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: argsProvider.ValidModulesArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
p, err := manCfg.Pager(cmd.Context(), fmt.Sprintf("Manual - %s/%s", category.String(), args[0]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmder.DisplayModuleManual(p, category, args[0])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ManCmd(
|
||||
cmder ManCommander,
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
) *cobra.Command {
|
||||
var manCfg ManConfig
|
||||
manCmd := &cobra.Command{
|
||||
Use: "man",
|
||||
Short: "Show manual pages",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
}
|
||||
|
||||
modulesOverviewCmd := &cobra.Command{
|
||||
Use: "modules",
|
||||
Short: "Basic overview over all modules",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
p, err := manCfg.Pager(cmd.Context())
|
||||
p, err := manCfg.Pager(cmd.Context(), "Manual - modules")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -91,8 +132,16 @@ func NewManCmd(cmder ManCommander) *cobra.Command {
|
|||
},
|
||||
}
|
||||
|
||||
manCmd.AddCommand(slices.Map(modules.Categories(), func(c modules.Category) *cobra.Command {
|
||||
return ModuleManCommand(
|
||||
c,
|
||||
cmder,
|
||||
ModulesArgsProviderFor(initializer, registryAcc, c),
|
||||
&manCfg,
|
||||
)
|
||||
})...)
|
||||
|
||||
manCmd.PersistentFlags().AddGoFlagSet(manCfg.Flags())
|
||||
manCmd.AddCommand(modulesOverviewCmd)
|
||||
|
||||
return manCmd
|
||||
}
|
||||
|
|
39
internal/cmd/modules.go
Normal file
39
internal/cmd/modules.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/slices"
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func ModulesCommand(
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
manCmder ManCommander,
|
||||
moduleCmder BootstrapModuleCommander,
|
||||
) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "modules",
|
||||
Short: "Interact with modules",
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
ManCmd(manCmder, initializer, registryAcc),
|
||||
NewCmd(
|
||||
slices.Map(modules.Categories(), func(c modules.Category) *cobra.Command {
|
||||
return BootstrapModuleCmd(
|
||||
c,
|
||||
moduleCmder,
|
||||
ModulesArgsProviderFor(initializer, registryAcc, c),
|
||||
WithShort(fmt.Sprintf("Bootstrap %s module", strings.ToLower(c.String()))),
|
||||
)
|
||||
})...,
|
||||
),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
|
@ -24,10 +24,10 @@ func BootstrapModuleCmd(
|
|||
) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: category.String(),
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
ValidArgsFunction: argsProvider.ValidModulesArgs,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
typeName = args[0]
|
||||
|
|
|
@ -20,7 +20,7 @@ var (
|
|||
_ Pager = (*CommandPager)(nil)
|
||||
)
|
||||
|
||||
func NewTUIPager() (*TUIPager, error) {
|
||||
func NewTUIPager(title string) (*TUIPager, error) {
|
||||
renderer, err := glamour.NewTermRenderer(
|
||||
glamour.WithStyles(glamour.DraculaStyleConfig),
|
||||
glamour.WithEmoji(),
|
||||
|
@ -31,11 +31,13 @@ func NewTUIPager() (*TUIPager, error) {
|
|||
}
|
||||
|
||||
return &TUIPager{
|
||||
title: title,
|
||||
renderer: renderer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type TUIPager struct {
|
||||
title string
|
||||
renderer *glamour.TermRenderer
|
||||
}
|
||||
|
||||
|
@ -52,9 +54,8 @@ func (t *TUIPager) Display() error {
|
|||
_, _ = io.Copy(builder, t.renderer)
|
||||
|
||||
tuiProgram := tea.NewProgram(
|
||||
tui.NewPager("Manual - modules", builder.String()),
|
||||
tui.NewPager(t.title, builder.String()),
|
||||
tea.WithAltScreen(),
|
||||
tea.WithMouseCellMotion(),
|
||||
)
|
||||
|
||||
_, err := tuiProgram.Run()
|
||||
|
|
|
@ -49,8 +49,10 @@ func (c *containerTask) doExecute(ctx context.Context, spec execution.Spec) (err
|
|||
return err
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(c.moduleWithMeta.OutDir(), 0o755); err != nil {
|
||||
return err
|
||||
if outDir := c.moduleWithMeta.OutDir(); outDir != "" {
|
||||
if err = os.MkdirAll(c.moduleWithMeta.OutDir(), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger := slog.
|
||||
|
|
|
@ -39,6 +39,26 @@ type TaskFactory struct {
|
|||
providers []TaskProvider
|
||||
}
|
||||
|
||||
func (f *TaskFactory) TaskForModuleWithoutDependencies(module modules.ModuleWithMeta) (Task, error) {
|
||||
var t Task
|
||||
|
||||
for i := range f.providers {
|
||||
if p := f.providers[i]; p.CanProvide(module) {
|
||||
if task, err := p.Create(module); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
t = task
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil, fmt.Errorf("no provider available to handle module %s", module.Name())
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (f *TaskFactory) TaskForModule(id string, repo *modules.Repository) (Task, error) {
|
||||
if t, ok := f.knownTasks[id]; ok {
|
||||
return t, nil
|
||||
|
|
|
@ -45,8 +45,10 @@ func (t *localTask) doExecute(ctx context.Context, spec execution.Spec) (err err
|
|||
}
|
||||
}()
|
||||
|
||||
if err = os.MkdirAll(t.module.OutDir(), 0o755); err != nil {
|
||||
return err
|
||||
if outDir := t.module.OutDir(); outDir != "" {
|
||||
if err = os.MkdirAll(t.module.OutDir(), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if mappings := t.module.InputMappings(); len(mappings) > 0 || t.module.Category() == modules.CategoryTool {
|
||||
|
|
|
@ -6,10 +6,9 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func marshalInto(val reflect.Value, block *hclwrite.Block) error {
|
||||
func (w *Writer[T]) marshalInto(val reflect.Value, block *hclwrite.Block) error {
|
||||
if val.CanInterface() {
|
||||
if marshaler, ok := val.Interface().(Marshaler); ok {
|
||||
return marshaler.MarshalHCL(block)
|
||||
|
@ -17,15 +16,15 @@ func marshalInto(val reflect.Value, block *hclwrite.Block) error {
|
|||
}
|
||||
switch val.Kind() {
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
return marshalInto(val.Elem(), block)
|
||||
return w.marshalInto(val.Elem(), block)
|
||||
case reflect.Struct:
|
||||
return marshalStructInto(val, block)
|
||||
return w.marshalStructInto(val, block)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalStructInto(val reflect.Value, block *hclwrite.Block) error {
|
||||
func (w *Writer[T]) marshalStructInto(val reflect.Value, block *hclwrite.Block) error {
|
||||
if val.CanInterface() {
|
||||
if marshaler, ok := val.Interface().(Marshaler); ok {
|
||||
return marshaler.MarshalHCL(block)
|
||||
|
@ -34,7 +33,7 @@ func marshalStructInto(val reflect.Value, block *hclwrite.Block) error {
|
|||
valueType := val.Type()
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
fieldType := valueType.Field(i)
|
||||
if err := marshalField(fieldType, val.Field(i), block); err != nil {
|
||||
if err := w.marshalField(fieldType, val.Field(i), block); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -42,11 +41,15 @@ func marshalStructInto(val reflect.Value, block *hclwrite.Block) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func marshalField(structField reflect.StructField, fieldVal reflect.Value, block *hclwrite.Block) error {
|
||||
func (w *Writer[T]) marshalField(structField reflect.StructField, fieldVal reflect.Value, block *hclwrite.Block) error {
|
||||
if !structField.IsExported() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if w.SkipZeroValues && fieldVal.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
marshalCfg, err := marshalConfigOf(structField)
|
||||
if err != nil && !errors.Is(err, errNoHclTag) {
|
||||
return err
|
||||
|
@ -56,6 +59,32 @@ func marshalField(structField reflect.StructField, fieldVal reflect.Value, block
|
|||
return nil
|
||||
}
|
||||
|
||||
handleStruct := func(val reflect.Value, t elementType, name string) error {
|
||||
var (
|
||||
targetBlock *hclwrite.Block
|
||||
needsAppending bool
|
||||
)
|
||||
|
||||
switch t {
|
||||
case elementTypeBlock:
|
||||
targetBlock = hclwrite.NewBlock(name, discoverLabels(val.Type()))
|
||||
needsAppending = true
|
||||
case elementTypeRemain:
|
||||
targetBlock = block
|
||||
default:
|
||||
return fmt.Errorf("undefined block creation behavior for type %s", marshalCfg.Type)
|
||||
}
|
||||
|
||||
if err := w.marshalStructInto(val, targetBlock); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if needsAppending {
|
||||
block.Body().AppendBlock(targetBlock)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.Map:
|
||||
if isPrimitiveType(fieldVal.Type().Elem()) {
|
||||
|
@ -66,49 +95,39 @@ func marshalField(structField reflect.StructField, fieldVal reflect.Value, block
|
|||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
if isPrimitiveType(fieldVal.Type().Elem()) {
|
||||
if isPrimitiveType(fieldVal.Type().Elem()) || marshalCfg.Type == elementTypeAttribute {
|
||||
if v, err := mapToCtyVal(fieldVal, mappingCfg{}); err != nil {
|
||||
return err
|
||||
} else {
|
||||
block.Body().SetAttributeValue(marshalCfg.Name, v)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < fieldVal.Len(); i++ {
|
||||
if err := handleStruct(fieldVal.Index(i), elementTypeBlock, marshalCfg.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
if fieldVal.IsNil() {
|
||||
fieldVal = reflect.New(fieldVal.Type().Elem())
|
||||
}
|
||||
|
||||
return marshalField(structField, fieldVal.Elem(), block)
|
||||
return w.marshalField(structField, fieldVal.Elem(), block)
|
||||
case reflect.Struct:
|
||||
if marshalCfg.Type != elementTypeBlock && marshalCfg.Type != elementTypeRemain {
|
||||
return fmt.Errorf("field %s is a struct but not marked as block", structField.Name)
|
||||
}
|
||||
|
||||
var (
|
||||
targetBlock *hclwrite.Block
|
||||
needsAppending bool
|
||||
)
|
||||
|
||||
switch marshalCfg.Type {
|
||||
case elementTypeBlock:
|
||||
targetBlock = hclwrite.NewBlock(marshalCfg.Name, discoverLabels(fieldVal.Type()))
|
||||
needsAppending = true
|
||||
case elementTypeRemain:
|
||||
targetBlock = block
|
||||
default:
|
||||
return fmt.Errorf("undefined block creation behavior for type %s", marshalCfg.Type)
|
||||
}
|
||||
|
||||
if err := marshalStructInto(fieldVal, targetBlock); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if needsAppending {
|
||||
block.Body().AppendBlock(targetBlock)
|
||||
}
|
||||
return handleStruct(fieldVal, marshalCfg.Type, marshalCfg.Name)
|
||||
default:
|
||||
if val, err := mapToCtyVal(fieldVal, mappingCfg{}); err == nil {
|
||||
block.Body().SetAttributeValue(marshalCfg.Name, val)
|
||||
switch marshalCfg.Type {
|
||||
case elementTypeAttribute:
|
||||
block.Body().SetAttributeValue(marshalCfg.Name, val)
|
||||
case elementTypeLabel:
|
||||
block.SetLabels(append(block.Labels(), val.AsString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,26 +179,3 @@ func isPrimitiveType(t reflect.Type) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func fieldCtyValue(val reflect.Value) (cty.Value, bool) {
|
||||
switch val.Kind() {
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return cty.NumberUIntVal(val.Uint()), true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return cty.NumberIntVal(val.Int()), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return cty.NumberFloatVal(val.Float()), true
|
||||
case reflect.String:
|
||||
return cty.StringVal(val.String()), true
|
||||
case reflect.Bool:
|
||||
return cty.BoolVal(val.Bool()), true
|
||||
case reflect.Pointer:
|
||||
if val.IsNil() {
|
||||
val = reflect.New(val.Type().Elem()).Elem()
|
||||
}
|
||||
|
||||
return fieldCtyValue(val)
|
||||
default:
|
||||
return cty.Value{}, false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,30 @@ import (
|
|||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
)
|
||||
|
||||
func NewWriter[T modules.ModuleWithMeta](w io.Writer) *Writer[T] {
|
||||
return &Writer[T]{writer: w}
|
||||
type writerCfg struct {
|
||||
SkipZeroValues bool
|
||||
}
|
||||
|
||||
type WriterOption func(*writerCfg)
|
||||
|
||||
func WithSkipZeroValues(skip bool) WriterOption {
|
||||
return func(cfg *writerCfg) {
|
||||
cfg.SkipZeroValues = skip
|
||||
}
|
||||
}
|
||||
|
||||
func NewWriter[T modules.ModuleWithMeta](w io.Writer, opts ...WriterOption) *Writer[T] {
|
||||
writer := &Writer[T]{writer: w}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&writer.writerCfg)
|
||||
}
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
type Writer[T modules.ModuleWithMeta] struct {
|
||||
writerCfg
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
|
@ -20,7 +39,7 @@ func (w *Writer[T]) Write(in T) error {
|
|||
f := hclwrite.NewEmptyFile()
|
||||
|
||||
block := hclwrite.NewBlock(in.Category().String(), []string{in.Type(), in.Name()})
|
||||
if err := marshalInto(reflect.ValueOf(in), block); err != nil {
|
||||
if err := w.marshalInto(reflect.ValueOf(in), block); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
9
internal/slices/map.go
Normal file
9
internal/slices/map.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package slices
|
||||
|
||||
func Map[TIn any, TOut any](slice []TIn, f func(TIn) TOut) []TOut {
|
||||
out := make([]TOut, len(slice))
|
||||
for i, v := range slice {
|
||||
out[i] = f(v)
|
||||
}
|
||||
return out
|
||||
}
|
19
internal/tmpl/hcl_helpers.go
Normal file
19
internal/tmpl/hcl_helpers.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package tmpl
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/hcl"
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
)
|
||||
|
||||
func WriteToHcl(mod modules.ModuleWithMeta) (string, error) {
|
||||
builder := new(strings.Builder)
|
||||
writer := hcl.NewWriter[modules.ModuleWithMeta](builder, hcl.WithSkipZeroValues(true))
|
||||
|
||||
if err := writer.Write(mod); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
|
@ -47,6 +47,10 @@ type Initializer interface {
|
|||
Init(hclCtx *hcl.EvalContext) (Module, error)
|
||||
}
|
||||
|
||||
type Helper interface {
|
||||
Help() Help
|
||||
}
|
||||
|
||||
type Module interface {
|
||||
Execute(ctx ExecutionContext) error
|
||||
Category() Category
|
||||
|
|
|
@ -12,7 +12,10 @@ import (
|
|||
|
||||
const defaultArgsLength = 6
|
||||
|
||||
var _ modules.Module = (*GoBuild)(nil)
|
||||
var (
|
||||
_ modules.Module = (*GoBuild)(nil)
|
||||
_ modules.Helper = (*GoBuild)(nil)
|
||||
)
|
||||
|
||||
type GoBuild struct {
|
||||
Binary string `hcl:"binary"`
|
||||
|
|
106
modules/build/golang/go_build_help.go
Normal file
106
modules/build/golang/go_build_help.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package golang
|
||||
|
||||
import "code.icb4dc0.de/buildr/buildr/modules"
|
||||
|
||||
func (g GoBuild) Help() modules.Help {
|
||||
return modules.Help{
|
||||
Name: "Go build - build Go binaries",
|
||||
Description: `This module helps to build Go binaries.
|
||||
It abstracts common build parameters like GOOS and GOARCH.
|
||||
Less common parameters can be specified e.g. with ` + "`flags`" + ` or ` + "`ldflags`" + `.
|
||||
Builds - as every other task - are executed in parallel.`,
|
||||
Examples: []modules.Example{
|
||||
{
|
||||
Name: "Simple build",
|
||||
Description: "Simplest possible example of a build",
|
||||
Spec: &modules.Metadata[GoBuild]{
|
||||
ModuleName: "buildr_linux_amd64",
|
||||
Module: GoBuild{
|
||||
Binary: "buildr",
|
||||
Main: ".",
|
||||
GoOS: "linux",
|
||||
GoArch: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Build with flags",
|
||||
Description: `Specify normal build flags as well as ldflags.`,
|
||||
Spec: &modules.Metadata[GoBuild]{
|
||||
ModuleName: "buildr_linux_amd64",
|
||||
Module: GoBuild{
|
||||
Binary: "buildr",
|
||||
Main: ".",
|
||||
GoOS: "linux",
|
||||
GoArch: "amd64",
|
||||
Flags: []string{
|
||||
"-v",
|
||||
"-trimpath",
|
||||
"-a",
|
||||
"-installsuffix=cgo",
|
||||
},
|
||||
LdFlags: []string{
|
||||
"-w -s",
|
||||
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=main'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Build with environment variables",
|
||||
Description: `Specify additional environment variables.`,
|
||||
Spec: &modules.Metadata[GoBuild]{
|
||||
ModuleName: "buildr_linux_amd64",
|
||||
Module: GoBuild{
|
||||
Binary: "buildr",
|
||||
Main: ".",
|
||||
GoOS: "linux",
|
||||
GoArch: "amd64",
|
||||
Env: map[string]string{
|
||||
"CGO_ENABLED": "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Containerize build process",
|
||||
Description: `It is also possible to run the build in a container.
|
||||
Although it is not very effective because the build process always has to download all dependencies if they are not cached.`,
|
||||
Spec: &modules.Metadata[GoBuild]{
|
||||
ModuleName: "buildr_linux_amd64",
|
||||
Container: &modules.ContainerSpec{
|
||||
Image: "golang:alpine",
|
||||
},
|
||||
Module: GoBuild{
|
||||
Binary: "buildr",
|
||||
Main: ".",
|
||||
GoOS: "linux",
|
||||
GoArch: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Containerize build process - with module cache volume",
|
||||
Description: `Specify additional environment variables.`,
|
||||
Spec: &modules.Metadata[GoBuild]{
|
||||
ModuleName: "buildr_linux_amd64",
|
||||
Container: &modules.ContainerSpec{
|
||||
Image: "golang:alpine",
|
||||
VolumeMounts: []modules.ContainerVolumeMount{
|
||||
{
|
||||
Target: "/go/pkg/mod",
|
||||
Name: "modcache",
|
||||
},
|
||||
},
|
||||
},
|
||||
Module: GoBuild{
|
||||
Binary: "buildr",
|
||||
Main: ".",
|
||||
GoOS: "linux",
|
||||
GoArch: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
13
modules/help.go
Normal file
13
modules/help.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package modules
|
||||
|
||||
type Example struct {
|
||||
Name string
|
||||
Description string
|
||||
Spec ModuleWithMeta
|
||||
}
|
||||
|
||||
type Help struct {
|
||||
Name string
|
||||
Description string
|
||||
Examples []Example
|
||||
}
|
40
modules/state/in_memory.go
Normal file
40
modules/state/in_memory.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ Store = (*InMemoryStore)(nil)
|
||||
|
||||
type InMemoryStore struct {
|
||||
lock sync.RWMutex
|
||||
data map[string][]byte
|
||||
}
|
||||
|
||||
func (i *InMemoryStore) Set(_ context.Context, key Key, state []byte, opts ...EntryOption) error {
|
||||
i.lock.Lock()
|
||||
defer i.lock.Unlock()
|
||||
|
||||
if i.data == nil {
|
||||
i.data = make(map[string][]byte)
|
||||
}
|
||||
|
||||
i.data[string(key.Bytes())] = state
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *InMemoryStore) Get(ctx context.Context, key Key) (state []byte, meta Metadata, err error) {
|
||||
i.lock.RLock()
|
||||
defer i.lock.RUnlock()
|
||||
|
||||
if i.data == nil {
|
||||
return nil, Metadata{}, nil
|
||||
}
|
||||
|
||||
if d, ok := i.data[string(key.Bytes())]; ok {
|
||||
return d, Metadata{}, nil
|
||||
} else {
|
||||
return nil, Metadata{}, nil
|
||||
}
|
||||
}
|
|
@ -15,7 +15,10 @@ import (
|
|||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
)
|
||||
|
||||
var _ modules.Module = (*ScriptTask)(nil)
|
||||
var (
|
||||
_ modules.Module = (*ScriptTask)(nil)
|
||||
_ modules.Helper = (*ScriptTask)(nil)
|
||||
)
|
||||
|
||||
type ScriptTask struct {
|
||||
Shell string `hcl:"shell,optional"`
|
||||
|
|
96
modules/task/script_task_help.go
Normal file
96
modules/task/script_task_help.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
)
|
||||
|
||||
func (s ScriptTask) Help() modules.Help {
|
||||
return modules.Help{
|
||||
Name: "Script - run single commands or complete scripts",
|
||||
Description: `This module helps to run arbitrary commands or complete scripts.
|
||||
It's possible to either inline all commands or specify an external script file.
|
||||
Furthermore, it's possible to specify which shell should be used to execute the commands.
|
||||
Right now this will only work with Linux/MacOS shells.`,
|
||||
Examples: []modules.Example{
|
||||
{
|
||||
Name: "Hello world",
|
||||
Description: `Simples possible script.`,
|
||||
Spec: &modules.Metadata[ScriptTask]{
|
||||
ModuleName: "hello_world",
|
||||
Module: ScriptTask{
|
||||
Inline: []string{`echo 'Hello world!'`},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Multiline inline script",
|
||||
Description: `Specify multiple array entries to run a script.
|
||||
All lines of the inline script will be joined to a single string so it's possible to use some context like changing the current working directory.`,
|
||||
Spec: &modules.Metadata[ScriptTask]{
|
||||
ModuleName: "multi_inline_script",
|
||||
Module: ScriptTask{
|
||||
Inline: []string{
|
||||
`mkdir test`,
|
||||
`echo 'Hello world' > test/sample.txt`,
|
||||
`cd test`,
|
||||
`cat sample.txt`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "External script file",
|
||||
Description: `Call a script from a file.`,
|
||||
Spec: &modules.Metadata[ScriptTask]{
|
||||
ModuleName: "external_script_file",
|
||||
Module: ScriptTask{
|
||||
Script: "testdata/sample.sh",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Manipulate environment variables",
|
||||
Description: `Set environment variables and use it in a script`,
|
||||
Spec: &modules.Metadata[ScriptTask]{
|
||||
ModuleName: "env_script",
|
||||
Module: ScriptTask{
|
||||
Inline: []string{
|
||||
`echo "$GREETING world"`,
|
||||
},
|
||||
Env: map[string]string{
|
||||
"GREETING": "Hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Change working directory",
|
||||
Description: `Set the working directory to another directory.`,
|
||||
Spec: &modules.Metadata[ScriptTask]{
|
||||
ModuleName: "script_with_working_dir",
|
||||
Module: ScriptTask{
|
||||
Inline: []string{
|
||||
`cat sample.txt`,
|
||||
},
|
||||
WorkingDir: "testdata",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Container script",
|
||||
Description: `Run a script inside a container.`,
|
||||
Spec: &modules.Metadata[ScriptTask]{
|
||||
ModuleName: "container_script",
|
||||
Container: &modules.ContainerSpec{
|
||||
Image: "busybox",
|
||||
},
|
||||
Module: ScriptTask{
|
||||
Inline: []string{
|
||||
`echo 'Hello, world!'`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
43
modules/task/script_task_test.go
Normal file
43
modules/task/script_task_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package task_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
"code.icb4dc0.de/buildr/buildr/modules/state"
|
||||
"code.icb4dc0.de/buildr/buildr/modules/task"
|
||||
)
|
||||
|
||||
func TestExamples(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := new(task.ScriptTask)
|
||||
for _, e := range s.Help().Examples {
|
||||
e := e
|
||||
t.Run(e.Name, func(t *testing.T) {
|
||||
var memStateStore state.InMemoryStore
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
execCtx := modules.TestExecutionContext{
|
||||
Context: ctx,
|
||||
TB: t,
|
||||
TestWorkingDir: t.TempDir(),
|
||||
OutputDirectory: t.TempDir(),
|
||||
StateStore: &memStateStore,
|
||||
}
|
||||
|
||||
if e.Spec.Unwrap().(task.ScriptTask).WorkingDir != "" {
|
||||
execCtx.TestWorkingDir = cwd
|
||||
}
|
||||
|
||||
if err := e.Spec.Execute(execCtx); err != nil {
|
||||
t.Errorf("failed to execute example: %s: %v", e.Name, err)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
3
modules/task/testdata/sample.sh
vendored
Normal file
3
modules/task/testdata/sample.sh
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Hello, world!"
|
1
modules/task/testdata/sample.txt
vendored
Normal file
1
modules/task/testdata/sample.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
67
modules/test_ctx.go
Normal file
67
modules/test_ctx.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/modules/state"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
var _ ExecutionContext = (*TestExecutionContext)(nil)
|
||||
|
||||
type TestExecutionContext struct {
|
||||
context.Context
|
||||
TB testing.TB
|
||||
TestWorkingDir, OutputDirectory string
|
||||
StateStore state.Store
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) Name() string {
|
||||
return t.TB.Name()
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) WorkingDir() string {
|
||||
return t.TestWorkingDir
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) OutDir() string {
|
||||
return t.OutputDirectory
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) BinariesDir() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) StdOut() io.Writer {
|
||||
return testWriter{TB: t.TB}
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) StdErr() io.Writer {
|
||||
return testWriter{TB: t.TB}
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) Logger() *slog.Logger {
|
||||
return slog.New(slog.NewTextHandler(testWriter{TB: t.TB}, nil))
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) GetState(ctx context.Context, key string) ([]byte, state.Metadata, error) {
|
||||
return t.StateStore.Get(ctx, state.KeyOfStrings(key))
|
||||
}
|
||||
|
||||
func (t TestExecutionContext) SetState(ctx context.Context, key string, value []byte) error {
|
||||
return t.StateStore.Set(ctx, state.KeyOfStrings(key), value)
|
||||
}
|
||||
|
||||
var _ io.Writer = (*testWriter)(nil)
|
||||
|
||||
type testWriter struct {
|
||||
testing.TB
|
||||
}
|
||||
|
||||
func (t testWriter) Write(p []byte) (n int, err error) {
|
||||
t.Log(string(p))
|
||||
return len(p), nil
|
||||
}
|
|
@ -18,3 +18,12 @@ const (
|
|||
CategoryBuild Category = "build"
|
||||
CategoryPackage Category = "package"
|
||||
)
|
||||
|
||||
func Categories() []Category {
|
||||
return []Category{
|
||||
CategoryTool,
|
||||
CategoryTask,
|
||||
CategoryBuild,
|
||||
CategoryPackage,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue