From 9f59ae025b844ec3c9c2b4b6323e8047cc64fa68 Mon Sep 17 00:00:00 2001 From: softwaredevelop <61334390+softwaredevelop@users.noreply.github.com> Date: Sun, 20 Sep 2026 14:24:16 +0200 Subject: [PATCH] refactor: shellcheck --- shellcheck/main.go | 80 ++++++++++++----- shellcheck/test/go.mod | 51 +++++++++-- shellcheck/test/go.sum | 89 ++++++++++++++++++ shellcheck/test/main.go | 90 +++++++++++++------ shellcheck/test/testdata/bash/test.bash | 3 + .../test/testdata/config/custom.shellcheckrc | 2 + shellcheck/test/testdata/config/test.sh | 3 + 7 files changed, 267 insertions(+), 51 deletions(-) create mode 100644 shellcheck/test/testdata/bash/test.bash create mode 100644 shellcheck/test/testdata/config/custom.shellcheckrc create mode 100644 shellcheck/test/testdata/config/test.sh diff --git a/shellcheck/main.go b/shellcheck/main.go index 73fa218..0b8f1ec 100644 --- a/shellcheck/main.go +++ b/shellcheck/main.go @@ -1,16 +1,7 @@ -// A generated module for Shellcheck functions +// A Dagger module for linting shell scripts using ShellCheck. // -// This module has been generated via dagger init and serves as a reference to -// basic module structure as you get started with Dagger. -// -// Two functions have been pre-created. You can modify, delete, or add to them, -// as needed. They demonstrate usage of arguments and return types using simple -// echo and grep commands. The functions can be called from the dagger CLI or -// from one of the SDKs. -// -// The first line in this comment block is a short description line and the -// rest is a long description with more detail on the module's purpose or usage, -// if appropriate. All modules should have a short description. +// ShellCheck is a static analysis tool for shell scripts (sh, bash, dash, ksh) +// that finds bugs, syntax issues, and style warnings. package main import ( @@ -21,7 +12,7 @@ const ( defaultImageRepository = "koalaman/shellcheck-alpine:latest" ) -// Shellcheck is a module for checking shell scripts. +// Shellcheck provides functions for checking shell scripts. type Shellcheck struct { // +private Image string @@ -29,7 +20,7 @@ type Shellcheck struct { Ctr *dagger.Container } -// New creates a new instance of the Shellcheck struct +// New creates a new instance of the Shellcheck struct. func New( // Custom image reference in "repository:tag" format to use as a base container. // +optional @@ -40,7 +31,7 @@ func New( } } -// Container returns the underlying Dagger container +// container returns the underlying Dagger container, lazily initialized. func (m *Shellcheck) container() *dagger.Container { if m.Ctr != nil { return m.Ctr @@ -55,13 +46,60 @@ func (m *Shellcheck) container() *dagger.Container { return m.Ctr } -// Check runs the shellcheck command. +// Check runs shellcheck on all .sh and .bash files in the source directory. +// If an optional config file is provided, it is placed in the working directory as .shellcheckrc. func (m *Shellcheck) Check( - // source is an optional argument that specifies a directory. + // Source directory containing shell scripts. source *dagger.Directory, + // Minimum severity of errors to report (error, warning, info, style). + // +optional + severity string, + // Optional shellcheck configuration file (.shellcheckrc). + // +optional + config *dagger.File, ) *dagger.Container { - return m.container(). - WithMountedDirectory("/tmp", source). - WithWorkdir("/tmp"). - WithExec([]string{"sh", "-c", "find . -type f -name '*.sh' -print0 | xargs -0 shellcheck"}) + ctr := m.container(). + WithMountedDirectory("/work", source). + WithWorkdir("/work") + + if config != nil { + ctr = ctr.WithFile("/work/.shellcheckrc", config). + WithFile("/root/.shellcheckrc", config) + } + + severityArg := "" + if severity != "" { + severityArg = "--severity=" + severity + " " + } + + cmd := "find . -type f \\( -name '*.sh' -o -name '*.bash' \\) -print0 | xargs -0 -r shellcheck " + severityArg + + return ctr.WithExec([]string{"sh", "-c", cmd}) +} + +// CheckWithConfig runs shellcheck explicitly using the provided configuration file via --rcfile. +// The configuration file is mandatory for this function. +func (m *Shellcheck) CheckWithConfig( + // Source directory containing shell scripts. + source *dagger.Directory, + // Mandatory configuration file for shellcheck. + file *dagger.File, + // Minimum severity of errors to report (error, warning, info, style). + // +optional + severity string, +) *dagger.Container { + ctr := m.container(). + WithMountedDirectory("/work", source). + WithWorkdir("/work"). + WithFile("/etc/shellcheckrc", file) + + severityArg := "" + if severity != "" { + severityArg = "--severity=" + severity + " " + } + + // Explicitly pass --rcfile to force using the provided configuration file + cmd := "find . -type f \\( -name '*.sh' -o -name '*.bash' \\) -print0 | xargs -0 -r shellcheck --rcfile=/etc/shellcheckrc " + severityArg + + return ctr.WithExec([]string{"sh", "-c", cmd}) } diff --git a/shellcheck/test/go.mod b/shellcheck/test/go.mod index 2ccdd72..2106f19 100644 --- a/shellcheck/test/go.mod +++ b/shellcheck/test/go.mod @@ -2,11 +2,52 @@ module dagger/shellcheck/test go 1.26.1 -require github.com/sourcegraph/conc v0.3.0 - -require github.com/stretchr/testify v1.11.1 // indirect - -require go.uber.org/multierr v1.11.0 // indirect +require ( + github.com/dagger/otel-go v1.43.0 + github.com/sourcegraph/conc v0.3.0 + go.opentelemetry.io/otel/sdk v1.43.0 +) + +require ( + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 // indirect + go.opentelemetry.io/otel/log v0.17.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.17.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/text v0.34.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) + +require ( + github.com/99designs/gqlgen v0.17.89 // indirect + github.com/Khan/genqlient v0.8.1 + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/dagger/querybuilder v0.0.0-20260402040506-574a5e81cb59 + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/sosodev/duration v1.4.0 // indirect + github.com/vektah/gqlparser/v2 v2.5.32 + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.43.0 + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect +) replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 diff --git a/shellcheck/test/go.sum b/shellcheck/test/go.sum index 9e94250..a4d714f 100644 --- a/shellcheck/test/go.sum +++ b/shellcheck/test/go.sum @@ -1,12 +1,101 @@ +github.com/99designs/gqlgen v0.17.89 h1:KzEcxPiMgQoMw3m/E85atUEHyZyt0PbAflMia5Kw8z8= +github.com/99designs/gqlgen v0.17.89/go.mod h1:GFqruTVGB7ZTdrf1uzOagpXbY7DrEt1pIxnTdhIbWvQ= +github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= +github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= +github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= +github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/dagger/otel-go v1.43.0 h1:AYCnAamWmxtSxigWPTgC+8EWqiWPcDZEegh8y05gdJ8= +github.com/dagger/otel-go v1.43.0/go.mod h1:83CTuXi70zcx1kaym5buqmb7RNzg1E9dEiQSFyLbLdU= +github.com/dagger/querybuilder v0.0.0-20260402040506-574a5e81cb59 h1:g6vfdGRyz6fAjfHz5FyYPZgHy8qcQ31fHrBl1iCOzxw= +github.com/dagger/querybuilder v0.0.0-20260402040506-574a5e81cb59/go.mod h1:jsdUJeYzcbyK1j/EqMGPrQgNYxl/Zfg06vvM9C/xXxs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE= +github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc= +github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 h1:ZVg+kCXxd9LtAaQNKBxAvJ5NpMf7LpvEr4MIZqb0TMQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0/go.mod h1:hh0tMeZ75CCXrHd9OXRYxTlCAdxcXioWHFIpYw2rZu8= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0 h1:djrxvDxAe44mJUrKataUbOhCKhR3F8QCyWucO16hTQs= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0/go.mod h1:dt3nxpQEiSoKvfTVxp3TUg5fHPLhKtbcnN3Z1I1ePD0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.41.0 h1:VO3BL6OZXRQ1yQc8W6EVfJzINeJ35BkiHx4MYfoQf44= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.41.0/go.mod h1:qRDnJ2nv3CQXMK2HUd9K9VtvedsPAce3S+/4LZHjX/s= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.41.0 h1:MMrOAN8H1FrvDyq9UJ4lu5/+ss49Qgfgb7Zpm0m8ABo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.41.0/go.mod h1:Na+2NNASJtF+uT4NxDe0G+NQb+bUgdPDfwxY/6JmS/c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9ZhgqiDqh6BioM7DVHHzEGVq76Db5897WLGZ5Go= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY= +go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4= +go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI= +go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4= +go.opentelemetry.io/otel/sdk/log/logtest v0.16.0 h1:/XVkpZ41rVRTP4DfMgYv1nEtNmf65XPPyAdqV90TMy4= +go.opentelemetry.io/otel/sdk/log/logtest v0.16.0/go.mod h1:iOOPgQr5MY9oac/F5W86mXdeyWZGleIx3uXO98X2R6Y= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4= +google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/shellcheck/test/main.go b/shellcheck/test/main.go index 8e1834e..9be1838 100644 --- a/shellcheck/test/main.go +++ b/shellcheck/test/main.go @@ -1,49 +1,89 @@ -// A generated module for Shellchecktest functions -// -// This module has been generated via dagger init and serves as a reference to -// basic module structure as you get started with Dagger. -// -// Two functions have been pre-created. You can modify, delete, or add to them, -// as needed. They demonstrate usage of arguments and return types using simple -// echo and grep commands. The functions can be called from the dagger CLI or -// from one of the SDKs. -// -// The first line in this comment block is a short description line and the -// rest is a long description with more detail on the module's purpose or usage, -// if appropriate. All modules should have a short description. +// Package main provides test suites for the Shellcheck Dagger module. package main import ( "context" - "regexp" + "dagger/shellcheck/test/internal/dagger" + "errors" + "strings" "github.com/sourcegraph/conc/pool" ) -// Shellchecktest is a module for testing shell scripts. +// Shellchecktest provides test functions for the Shellcheck module. type Shellchecktest struct{} -// All runs all tests. +// All runs all tests concurrently. func (m *Shellchecktest) All(ctx context.Context) error { p := pool.New().WithErrors().WithContext(ctx) - p.Go(m.CheckDirectory) + p.Go(m.CheckDirectoryFailsOnInvalid) + p.Go(m.CheckBashFiles) + p.Go(m.CheckWithOptionalConfigPasses) + p.Go(m.CheckWithConfigPasses) return p.Wait() } -// CheckDirectory runs a test on a directory. -func (m *Shellchecktest) CheckDirectory(ctx context.Context) error { - +// CheckDirectoryFailsOnInvalid tests that shellcheck detects errors in standard .sh scripts. +func (m *Shellchecktest) CheckDirectoryFailsOnInvalid(ctx context.Context) error { dir := dag.CurrentModule().Source().Directory("./testdata") + _, err := dag.Shellcheck().Check(dir).Stderr(ctx) + if err == nil { + return errors.New("expected shellcheck to fail on invalid shell script, but it succeeded") + } - if err != nil { - re := regexp.MustCompile("exit code: 123") - if re.MatchString(err.Error()) { - return nil - } + if strings.Contains(err.Error(), "exit code: 123") || strings.Contains(err.Error(), "exit code: 1") { + return nil + } + + return err +} + +// CheckBashFiles tests that shellcheck properly discovers and checks .bash files. +func (m *Shellchecktest) CheckBashFiles(ctx context.Context) error { + dir := dag.CurrentModule().Source().Directory("./testdata/bash") + + _, err := dag.Shellcheck().Check(dir).Stderr(ctx) + if err == nil { + return errors.New("expected shellcheck to detect errors in .bash file, but it succeeded or skipped it") + } + + if strings.Contains(err.Error(), "exit code: 123") || strings.Contains(err.Error(), "exit code: 1") { + return nil } return err } + +// CheckWithOptionalConfigPasses tests that passing an optional config to Check disables warnings and passes. +func (m *Shellchecktest) CheckWithOptionalConfigPasses(ctx context.Context) error { + dir := dag.CurrentModule().Source().Directory("./testdata/config") + configFile := dag.CurrentModule().Source().File("./testdata/config/custom.shellcheckrc") + + _, err := dag.Shellcheck().Check(dir, dagger.ShellcheckCheckOpts{ + Config: configFile, + }).Stderr(ctx) + + if err != nil { + return errors.New("expected shellcheck to pass with optional config, but it failed: " + err.Error()) + } + + return nil +} + +// CheckWithConfigPasses tests the dedicated CheckWithConfig function with --rcfile. +func (m *Shellchecktest) CheckWithConfigPasses(ctx context.Context) error { + dir := dag.CurrentModule().Source().Directory("./testdata/config") + configFile := dag.CurrentModule().Source().File("./testdata/config/custom.shellcheckrc") + + // Call CheckWithConfig where configFile is mandatory + _, err := dag.Shellcheck().CheckWithConfig(dir, configFile).Stderr(ctx) + + if err != nil { + return errors.New("expected CheckWithConfig (--rcfile) to pass, but it failed: " + err.Error()) + } + + return nil +} diff --git a/shellcheck/test/testdata/bash/test.bash b/shellcheck/test/testdata/bash/test.bash new file mode 100644 index 0000000..5e1bdbb --- /dev/null +++ b/shellcheck/test/testdata/bash/test.bash @@ -0,0 +1,3 @@ +#!/bin/bash +# Intentionally unquoted variable to trigger SC2086 +echo $1 diff --git a/shellcheck/test/testdata/config/custom.shellcheckrc b/shellcheck/test/testdata/config/custom.shellcheckrc new file mode 100644 index 0000000..9e0573a --- /dev/null +++ b/shellcheck/test/testdata/config/custom.shellcheckrc @@ -0,0 +1,2 @@ +# Disable SC2086 (Double quote to prevent globbing and word splitting) +disable=SC2086 diff --git a/shellcheck/test/testdata/config/test.sh b/shellcheck/test/testdata/config/test.sh new file mode 100644 index 0000000..dc5cbb8 --- /dev/null +++ b/shellcheck/test/testdata/config/test.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# Intentionally unquoted variable to test .shellcheckrc disabling +echo $1