diff --git a/.dagger/.gitattributes b/.dagger/.gitattributes new file mode 100644 index 0000000..3a45493 --- /dev/null +++ b/.dagger/.gitattributes @@ -0,0 +1,4 @@ +/dagger.gen.go linguist-generated +/internal/dagger/** linguist-generated +/internal/querybuilder/** linguist-generated +/internal/telemetry/** linguist-generated diff --git a/.dagger/.gitignore b/.dagger/.gitignore new file mode 100644 index 0000000..773338b --- /dev/null +++ b/.dagger/.gitignore @@ -0,0 +1,5 @@ +/dagger.gen.go +/internal/dagger +/internal/querybuilder +/internal/telemetry +/.env diff --git a/.dagger/go.mod b/.dagger/go.mod new file mode 100644 index 0000000..27924a8 --- /dev/null +++ b/.dagger/go.mod @@ -0,0 +1,50 @@ +module dagger/macroservices-invoice-renderer + +go 1.25.5 + +require ( + github.com/99designs/gqlgen v0.17.81 + github.com/Khan/genqlient v0.8.1 + github.com/vektah/gqlparser/v2 v2.5.30 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 + go.opentelemetry.io/otel/log v0.14.0 + go.opentelemetry.io/otel/metric v1.38.0 + go.opentelemetry.io/otel/sdk v1.38.0 + go.opentelemetry.io/otel/sdk/log v0.14.0 + go.opentelemetry.io/otel/sdk/metric v1.38.0 + go.opentelemetry.io/otel/trace v1.38.0 + go.opentelemetry.io/proto/otlp v1.8.0 + golang.org/x/sync v0.17.0 + google.golang.org/grpc v1.76.0 +) + +require ( + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + 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/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/sosodev/duration v1.3.1 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/protobuf v1.36.9 // indirect +) + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 + +replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.14.0 + +replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.14.0 diff --git a/.dagger/go.sum b/.dagger/go.sum new file mode 100644 index 0000000..03dea85 --- /dev/null +++ b/.dagger/go.sum @@ -0,0 +1,89 @@ +github.com/99designs/gqlgen v0.17.81 h1:kCkN/xVyRb5rEQpuwOHRTYq83i0IuTQg9vdIiwEerTs= +github.com/99designs/gqlgen v0.17.81/go.mod h1:vgNcZlLwemsUhYim4dC1pvFP5FX0pr2Y+uYUoHFb1ig= +github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= +github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= +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/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.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +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.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +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.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE= +github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 h1:OMqPldHt79PqWKOMYIAQs3CxAi7RLgPxwfFSwr4ZxtM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0/go.mod h1:1biG4qiqTxKiUCtoWDPpL3fB3KxVwCiGw81j3nKMuHE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= +go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= +go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +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-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +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/.dagger/main.go b/.dagger/main.go new file mode 100644 index 0000000..b55492d --- /dev/null +++ b/.dagger/main.go @@ -0,0 +1,100 @@ +package main + +import ( + "context" + + "golang.org/x/sync/errgroup" + + "dagger/macroservices-invoice-renderer/internal/dagger" +) + +const ( + nodeJSVersion = "16" + repoName = "macroservices" +) + +type MacroservicesInvoiceRenderer struct { + // Source code directory + Source *dagger.Directory + // +private + NodeVersion string + // +private + InfisicalClientSecret *dagger.Secret +} + +func New( + // Source code directory + // +defaultPath="." + source *dagger.Directory, + // Infisical client secret + infisicalClientSecret *dagger.Secret, +) *MacroservicesInvoiceRenderer { + return &MacroservicesInvoiceRenderer{ + Source: source, + NodeVersion: nodeJSVersion, + InfisicalClientSecret: infisicalClientSecret, + } +} + +// CI runs the complete CI pipeline (lint checks) +func (m *MacroservicesInvoiceRenderer) CI(ctx context.Context) error { + g, ctx := errgroup.WithContext(ctx) + + g.Go(func() error { + backend := dag.NodeCi(m.Source.Directory("backend"), dagger.NodeCiOpts{ + NodeVersion: m.NodeVersion, + }) + + _, err := backend. + Install(). + WithTest(). + WithLint(). + WithExec("check-prettier"). + Stdout(ctx) + + return err + }) + + g.Go(func() error { + frontend := dag.NodeCi(m.Source.Directory("frontend"), dagger.NodeCiOpts{ + NodeVersion: m.NodeVersion, + }) + + _, err := frontend. + Install(). + WithLint(). + WithExec("check-prettier"). + Build(dagger.NodeCiBuildOpts{ + UseNextCache: true, + }). + Stdout(ctx) + return err + }) + + return g.Wait() +} + +// BuildAndPush builds and pushes the Docker image to the container registry +func (m *MacroservicesInvoiceRenderer) BuildAndPush( + ctx context.Context, +) error { + g, ctx := errgroup.WithContext(ctx) + + g.Go(func() error { + _, err := dag.Docker(m.Source.Directory("backend"), m.InfisicalClientSecret, repoName+"-backend", dagger.DockerOpts{ + Environment: "prod", + }).Build().Publish(ctx) + + return err + }) + + g.Go(func() error { + _, err := dag.Docker(m.Source.Directory("frontend"), m.InfisicalClientSecret, repoName+"-frontend", dagger.DockerOpts{ + Environment: "prod", + }).Build().Publish(ctx) + + return err + }) + + return g.Wait() +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 569981e..6690bff 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,126 +1,9 @@ -name: Node.js CI +name: CI -on: ["pull_request"] +on: + pull_request: jobs: - test-coverage-backend: - runs-on: ubuntu-latest - permissions: - checks: write - pull-requests: write - contents: write - - steps: - - uses: actions/checkout@v3 - - uses: ArtiomTr/jest-coverage-report-action@v2 - with: - working-directory: backend - - lint-backend: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./backend - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci --include=dev - - run: npm run lint - - prettier-backend: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./backend - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci --include=dev - - run: npm run check-prettier - - # test-coverage-next: - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - # - uses: ArtiomTr/jest-coverage-report-action@v2 - # with: - # working-directory: frontend - - lint-next: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./frontend - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci --include=dev - - run: npm run lint - - prettier-next: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./frontend - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci --include=dev - - run: npm run check-prettier - - dryrun-build-next: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./frontend - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci --include=dev - - run: npm run build + checks: + uses: mocbotau/infra-workflows/.github/workflows/generic-ci.yaml@main + secrets: inherit diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 66650e7..71a43ad 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,101 +1,14 @@ -name: Build, push, and deploy +name: Deploy on: push: - branches: - - main + branches: [main] + workflow_dispatch: jobs: - build: - runs-on: arc-runner-set - strategy: - fail-fast: false - matrix: - include: - - dockerfile: ./backend/Dockerfile - context: ./backend - image_suffix: macroservices-backend - name: "- backend" - - dockerfile: ./frontend/Dockerfile - context: ./frontend - image_suffix: macroservices-frontend - name: "- frontend" - - name: Build and push to Docker Hub ${{ matrix.name }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.ORG_DOCKERHUB_USERNAME }} - password: ${{ secrets.ORG_DOCKERHUB_TOKEN }} - - - name: Extract commit SHA - id: extractor - run: | - echo "${{ github.sha }}" | sed -E "s|^(.{7}).*$|short_sha=\1|" >> $GITHUB_OUTPUT - - - name: Build and push to Docker Hub - uses: docker/build-push-action@v6 - with: - push: true - context: ${{ matrix.context }} - file: ${{ matrix.dockerfile }} - tags: ${{ secrets.ORG_DOCKERHUB_USERNAME }}/${{ secrets.ORG_DOCKERHUB_REPO }}:${{ matrix.image_suffix }} - deploy: - name: Deploy to Kubernetes - needs: build - runs-on: ubuntu-latest - env: - REPO_NAME: macroservices - COMMIT_SHA: ${{ needs.build.outputs.commit-sha }} - DOCKERHUB_USERNAME: ${{ secrets.ORG_DOCKERHUB_USERNAME }} - DOCKERHUB_REPO: ${{ secrets.ORG_DOCKERHUB_REPO }} - BACKEND_API_KEY: ${{ secrets.BACKEND_API_KEY }} - GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} - GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} - GITHUB_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }} - GITHUB_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }} - I18N_NEXUS_API_KEY: ${{ secrets.I18N_NEXUS_API_KEY }} - MAIL_PASS: ${{ secrets.MAIL_PASS }} - MAIL_USER: ${{ vars.MAIL_USER }} - NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }} - NEXT_PUBLIC_COOKIE_KEY: ${{ secrets.NEXT_PUBLIC_COOKIE_KEY }} - - steps: - - name: Checkout infrastructure config - uses: actions/checkout@v4 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.ORG_DOCKERHUB_USERNAME }} - password: ${{ secrets.ORG_DOCKERHUB_TOKEN }} - - - name: Set kube context - uses: azure/k8s-set-context@v4 - with: - method: service-account - k8s-url: https://kube.masterofcubesau.com:6443 - k8s-secret: ${{ secrets.ORG_K3S_AUTH_TOKEN }} - - - name: Add internal chartmuseum - run: | - helm repo add chartmuseum https://chartmuseum.masterofcubesau.com \ - --username ${{ secrets.ORG_CHARTMUSEUM_USER }} \ - --password ${{ secrets.ORG_CHARTMUSEUM_PASS }} - - - name: Generate env file - run: | - cat frontend/.env.template | envsubst > frontend/.env - - - name: Deploy application to prod - run: | - cat infra/values.yaml | envsubst | \ - helm upgrade --install "$REPO_NAME" chartmuseum/generic-app --version 0.1.3 \ - -f - --set-file frontendEnvConfig=frontend/.env \ - --namespace="$REPO_NAME-prod" --create-namespace --atomic --timeout=1m --cleanup-on-fail + uses: mocbotau/infra-workflows/.github/workflows/generic-deploy.yaml@main + with: + event-name: ${{ github.event_name }} + environment: "prod" + secrets: inherit diff --git a/dagger.json b/dagger.json new file mode 100644 index 0000000..3256d0d --- /dev/null +++ b/dagger.json @@ -0,0 +1,20 @@ +{ + "name": "Macroservices-Invoice-Renderer", + "engineVersion": "v0.19.8", + "sdk": { + "source": "go" + }, + "dependencies": [ + { + "name": "docker", + "source": "https://github.com/mocbotau/infra-dagger-modules/modules/docker@main", + "pin": "7215583765d5322efba4228d522dca174baecda9" + }, + { + "name": "node-ci", + "source": "https://github.com/mocbotau/infra-dagger-modules/modules/node-ci@main", + "pin": "7215583765d5322efba4228d522dca174baecda9" + } + ], + "source": ".dagger" +} diff --git a/frontend/.dockerignore b/frontend/.dockerignore deleted file mode 100644 index 71a80ea..0000000 --- a/frontend/.dockerignore +++ /dev/null @@ -1,16 +0,0 @@ -.env.local.example -coverage/ -logs/ -node_modules/ -persistence/ -.next/ - -tests/ -.eslintrc.json -.prettierrc.json -Dockerfile -.dockerignore -.gitignore -jest.config.js - -*.md diff --git a/helmfile.yaml.gotmpl b/helmfile.yaml.gotmpl new file mode 100644 index 0000000..a1da030 --- /dev/null +++ b/helmfile.yaml.gotmpl @@ -0,0 +1,27 @@ +environments: + prod: + values: + - environment: prod + +--- +repositories: + - name: chartmuseum + url: https://chartmuseum.masterofcubesau.com + username: {{ requiredEnv "CHARTMUSEUM_USER" }} + password: {{ requiredEnv "CHARTMUSEUM_PASS" }} + +helmDefaults: + atomic: true + cleanupOnFail: true + createNamespace: true + timeout: 60 + wait: true + waitForJobs: true + +releases: + - name: macroservices + chart: chartmuseum/generic-app + version: 0.3.0 + namespace: macroservices-{{ .Environment.Values.environment }} + values: + - infra/values.yaml.gotmpl diff --git a/infra/values.yaml b/infra/values.yaml deleted file mode 100644 index b2f0dbd..0000000 --- a/infra/values.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# yaml-language-server: $schema=../../infra-helm-charts/charts/generic-app/values.schema.json -environment: "prod" -version: "${COMMIT_SHA}" -deployments: - - name: "macroservices-backend" - image: "${DOCKERHUB_USERNAME}/${DOCKERHUB_REPO}:${REPO_NAME}-backend" - environment: - HOST: "0.0.0.0" - PORT: "8000" - I18N_NEXUS_API_KEY: "/secrets/i18n-nexus-api-key" - secrets: - i18n-nexus-api-key: "${I18N_NEXUS_API_KEY}" - service: - port: 8000 - ingress: - subdomain: "macroservices" - readinessProbe: - httpGet: - port: 8000 - path: "/api/v3/healthcheck" - volumes: - - name: "backend-data" - mountPath: "/app/persistence" - size: "1Gi" - - - name: "macroservices-frontend" - image: "${DOCKERHUB_USERNAME}/${DOCKERHUB_REPO}:${REPO_NAME}-frontend" - config: - fileVariable: "frontendEnvConfig" - fileName: ".env" - mountPath: "/app/.env" - subPath: ".env" - service: - port: 3000 - ingress: - subdomain: "app-macroservices" - readinessProbe: - httpGet: - port: 3000 - path: / - volumes: - - name: "frontend-data" - mountPath: "/app/persistence" - size: "1Gi" diff --git a/infra/values.yaml.gotmpl b/infra/values.yaml.gotmpl new file mode 100644 index 0000000..fb1a311 --- /dev/null +++ b/infra/values.yaml.gotmpl @@ -0,0 +1,82 @@ +# yaml-language-server: $schema=../../infra-helm-charts/charts/generic-app/values.schema.json +environment: "prod" +version: {{ requiredEnv "COMMIT_SHA" | quote }} + +deployments: + - name: "macroservices-backend" + image: "mocbotau/cloud:macroservices-backend-prod" + environment: + HOST: "0.0.0.0" + PORT: "8000" + I18N_NEXUS_API_KEY: "/secrets/i18n-nexus-api-key" + secretProviderClass: + projectId: "1523620a-e2b9-4772-a5b3-99db47e19172" + secrets: + - fileName: "i18n-nexus-api-key" + secretKey: "I18N_NEXUS_API_KEY" + service: + port: 8000 + ingress: + subdomain: "macroservices" + domain: "masterofcubesau.com" + readinessProbe: + httpGet: + port: 8000 + path: "/api/v3/healthcheck" + volumes: + - name: "backend-data" + mountPath: "/app/persistence" + size: "1Gi" + + - name: "macroservices-frontend" + image: "mocbotau/cloud:macroservices-frontend-prod" + environment: + BACKEND_URL: "http://service-macroservices-backend" + BACKEND_PORT: "8000" + SENDING_API: "dummy" + NEXTAUTH_URL: "https://app-macroservices.masterofcubesau.com" + FRONTEND_URL: "https://app-macroservices.masterofcubesau.com" + service: + port: 3000 + ingress: + subdomain: "app-macroservices" + domain: "masterofcubesau.com" + secretProviderClass: + projectId: "1523620a-e2b9-4772-a5b3-99db47e19172" + syncAsKubernetesSecret: true + secrets: + - fileName: "backend-api-key" + secretKey: "BACKEND_API_KEY" + envName: "BACKEND_API_KEY" + - fileName: "cookie-key" + secretKey: "NEXT_PUBLIC_COOKIE_KEY" + envName: "NEXT_PUBLIC_COOKIE_KEY" + - fileName: "mail-user" + secretKey: "MAIL_USER" + envName: "MAIL_USER" + - fileName: "mail-pass" + secretKey: "MAIL_PASS" + envName: "MAIL_PASS" + - fileName: "nextauth-secret" + secretKey: "NEXTAUTH_SECRET" + envName: "NEXTAUTH_SECRET" + - fileName: "google-client-id" + secretKey: "GOOGLE_CLIENT_ID" + envName: "GOOGLE_CLIENT_ID" + - fileName: "google-client-secret" + secretKey: "GOOGLE_CLIENT_SECRET" + envName: "GOOGLE_CLIENT_SECRET" + - fileName: "github-client-id" + secretKey: "GITHUB_CLIENT_ID" + envName: "GITHUB_CLIENT_ID" + - fileName: "github-client-secret" + secretKey: "GITHUB_CLIENT_SECRET" + envName: "GITHUB_CLIENT_SECRET" + readinessProbe: + httpGet: + port: 3000 + path: / + volumes: + - name: "frontend-data" + mountPath: "/app/persistence" + size: "1Gi"