diff --git a/.gitignore b/.gitignore index c15a80d..bcd4403 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ tmp go.work *.pem + +*.env + +test_output diff --git a/cmd/server/api_get_params_test.go b/cmd/server/api_get_params_test.go index 0a4cabd..51d3e61 100644 --- a/cmd/server/api_get_params_test.go +++ b/cmd/server/api_get_params_test.go @@ -1,6 +1,97 @@ package main -import "testing" +import ( + "bytes" + "cmp" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" -func TestGetParams(t *testing.T) { + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/nestoca/joy-generator/internal/generator" + "github.com/nestoca/joy-generator/internal/github" +) + +func TestGetParamsE2E(t *testing.T) { + logs := &TestLogOutputs{} + + logger := zerolog.New(logs) + + user := github.User{ + Name: os.Getenv("GH_USER"), + Token: os.Getenv("GH_TOKEN"), + } + + catalog := github.RepoMetadata{ + Path: cmp.Or(os.Getenv("CATALOG_PATH"), filepath.Join(os.TempDir(), "catalog-test")), + URL: os.Getenv("CATALOG_URL"), + TargetRevision: os.Getenv("CATALOG_REVISION"), + } + + require.NoError(t, os.RemoveAll(catalog.Path)) + + repo, err := user.NewRepo(catalog) + require.NoError(t, err) + + repo = repo.WithLogger(logger) + + handler := Handler(HandlerParams{ + pluginToken: "test-token", + logger: logger, + repo: repo, + generator: &generator.Generator{ + LoadJoyContext: generator.RepoLoader(repo), + Logger: logger, + }, + }) + + server := httptest.NewServer(handler) + defer server.Close() + + req, err := http.NewRequest("GET", server.URL+"/api/v1/getparams.execute", strings.NewReader("{}")) + require.NoError(t, err) + + req.Header.Set("Authorization", "Bearer test-token") + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + var body bytes.Buffer + _, err = io.Copy(&body, resp.Body) + require.NoError(t, err) + + require.Equal(t, 200, resp.StatusCode, body.String()) + + var response generator.GetParamsResponse + require.NoError(t, json.Unmarshal(body.Bytes(), &response)) + + require.Greater(t, len(response.Output.Parameters), 0) + + require.Greater(t, len(logs.Records), 0) + for _, record := range logs.Records { + require.NotEmpty(t, record["level"]) + require.NotEqual(t, "error", record["level"]) + } +} + +type TestLogOutputs struct { + Records []map[string]any +} + +func (output *TestLogOutputs) Write(data []byte) (int, error) { + var record map[string]any + if err := json.Unmarshal(data, &record); err != nil { + return 0, fmt.Errorf("invalid record: %w", err) + } + output.Records = append(output.Records, record) + return len(data), nil } diff --git a/cmd/server/config.go b/cmd/server/config.go index 91b4098..4793285 100644 --- a/cmd/server/config.go +++ b/cmd/server/config.go @@ -1,6 +1,8 @@ package main import ( + "os" + "path/filepath" "time" "github.com/davidmdm/conf" @@ -9,10 +11,8 @@ import ( ) type Config struct { - Port string - GracefulShutdown time.Duration - - Debug bool + Port string + GracePeriod time.Duration PluginToken string @@ -24,14 +24,14 @@ type Config struct { } } -func GetConfig() (cfg Config) { - defer conf.Environ.MustParse() +func GetConfig() Config { + var cfg Config conf.Var(conf.Environ, &cfg.Port, "PORT", conf.Default(":3000")) - conf.Var(conf.Environ, &cfg.Debug, "DEBUG") + conf.Var(conf.Environ, &cfg.GracePeriod, "GRACE_PERIOD", conf.Default(10*time.Second)) conf.Var(conf.Environ, &cfg.PluginToken, "PLUGIN_TOKEN") - conf.Var(conf.Environ, &cfg.Catalog.URL, "CATALOG_REPO_URL") - conf.Var(conf.Environ, &cfg.Catalog.Path, "CATALOG_DIR") + conf.Var(conf.Environ, &cfg.Catalog.URL, "CATALOG_URL") + conf.Var(conf.Environ, &cfg.Catalog.Path, "CATALOG_DIR", conf.Default(filepath.Join(os.TempDir(), "catalog"))) conf.Var(conf.Environ, &cfg.Catalog.TargetRevision, "CATALOG_REVISION") conf.Var(conf.Environ, &cfg.Github.User.Token, "GH_TOKEN") conf.Var(conf.Environ, &cfg.Github.User.Name, "GH_USER") @@ -39,5 +39,7 @@ func GetConfig() (cfg Config) { conf.Var(conf.Environ, &cfg.Github.App.InstallationID, "GH_APP_INSTALLATION_ID") conf.Var(conf.Environ, &cfg.Github.App.PrivateKeyPath, "GH_APP_PRIVATE_KEY_PATH") - return + conf.Environ.MustParse() + + return cfg } diff --git a/cmd/server/handler.go b/cmd/server/handler.go index fa22d43..51c62a4 100644 --- a/cmd/server/handler.go +++ b/cmd/server/handler.go @@ -14,6 +14,10 @@ import ( "github.com/nestoca/joy-generator/internal/github" ) +func init() { + gin.SetMode(gin.ReleaseMode) +} + type HandlerParams struct { pluginToken string logger zerolog.Logger @@ -24,13 +28,53 @@ type HandlerParams struct { func Handler(params HandlerParams) http.Handler { engine := gin.New() - engine.Use(func(c *gin.Context) { + engine.Use( + RecoveryMiddleware(params.logger), + ObservabilityMiddleware(params.logger), + ) + + engine.GET("/api/v1/health", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + engine.GET("/api/v1/readiness", func(c *gin.Context) { + if err := params.repo.Pull(); err != nil { + c.JSON(500, gin.H{ + "status": "error", + "detail": err.Error(), + }) + return + } + + c.JSON(200, gin.H{"status": "ok"}) + }) + + generatorAPI := generator.API{ + Logger: params.logger, + Generator: params.generator, + } + + engine.POST( + "/api/v1/getparams.execute", + func(c *gin.Context) { + if c.GetHeader("Authorization") != "Bearer "+params.pluginToken { + c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"}) + } + }, + generatorAPI.HandleGetParams, + ) + + return engine.Handler() +} + +func RecoveryMiddleware(logger zerolog.Logger) gin.HandlerFunc { + return func(c *gin.Context) { defer func() { err := recover() if err == nil { return } - params.logger.Err(fmt.Errorf("%v", err)).Msg("recovered from panic") + logger.Err(fmt.Errorf("%v", err)).Msg("recovered from panic") if c.Writer.Written() { return @@ -38,9 +82,15 @@ func Handler(params HandlerParams) http.Handler { c.JSON(500, gin.H{"error": err}) }() - }) + // Important: c.Next() is needed so that defer statement doesn't execute immediately + // but only after middleware chain is complete or has panicked. + // Great catch by Mr Silphid + c.Next() + } +} - engine.Use(func(c *gin.Context) { +func ObservabilityMiddleware(logger zerolog.Logger) gin.HandlerFunc { + return func(c *gin.Context) { start := time.Now() recorder := ErrorRecorder{ @@ -54,51 +104,18 @@ func Handler(params HandlerParams) http.Handler { event := func() *zerolog.Event { if err := recorder.buffer.String(); err != "" { - return params.logger.Err(errors.New(err)) + return logger.Err(errors.New(err)) } - return params.logger.Info() + return logger.Info() }() event. Str("method", c.Request.Method). Str("path", c.Request.URL.Path). Int("code", c.Writer.Status()). - Dur("elapsed", time.Since(start)). + Str("elapsed", time.Since(start).String()). Msg("served request") - }) - - engine.GET("/api/v1/health", func(c *gin.Context) { - c.JSON(200, gin.H{"status": "ok"}) - }) - - engine.GET("/api/v1/readiness", func(c *gin.Context) { - if err := params.repo.Pull(); err != nil { - c.JSON(500, gin.H{ - "status": "error", - "detail": err.Error(), - }) - return - } - - c.JSON(200, gin.H{"status": "ok"}) - }) - - generatorAPI := generator.API{ - Logger: params.logger, - Generator: params.generator, } - - engine.GET( - "/api/v1/getparams.execute", - func(c *gin.Context) { - if c.GetHeader("Authorization") != "Bearer "+params.pluginToken { - c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"}) - } - }, - generatorAPI.HandleGetParams, - ) - - return engine.Handler() } type ErrorRecorder struct { diff --git a/cmd/server/main.go b/cmd/server/main.go index 64dd914..407423a 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -42,6 +42,8 @@ func run() error { return fmt.Errorf("failed to create repo: %w", err) } + logger.Info().Str("catalog_path", repo.Metadata.Path).Msg("initialized repo") + repo = repo.WithLogger(logger) server := &http.Server{ @@ -61,6 +63,7 @@ func run() error { errChan := make(chan error, 1) go func() { + logger.Info().Str("address", server.Addr).Msg("starting server") if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { errChan <- err } @@ -75,7 +78,7 @@ func run() error { case <-ctx.Done(): } - shutdownContext, cancel := context.WithTimeout(context.Background(), cfg.GracefulShutdown) + shutdownContext, cancel := context.WithTimeout(context.Background(), cfg.GracePeriod) defer cancel() if err := server.Shutdown(shutdownContext); err != nil { diff --git a/go.mod b/go.mod index 72f9441..ed843af 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/davidmdm/x/xcontext v0.0.2 github.com/gin-gonic/gin v1.9.1 github.com/go-git/go-git/v5 v5.11.0 - github.com/nestoca/joy v0.39.0 + github.com/nestoca/joy v0.39.1 github.com/rs/zerolog v1.32.0 github.com/stretchr/testify v1.9.0 gopkg.in/yaml.v3 v3.0.1 @@ -20,7 +20,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/TwiN/go-color v1.4.1 // indirect - github.com/bytedance/sonic v1.11.0 // indirect + github.com/bytedance/sonic v1.11.3 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect @@ -35,7 +35,7 @@ require ( github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.18.0 // indirect + github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -46,7 +46,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -55,11 +55,11 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nestoca/survey/v2 v2.0.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect - github.com/skeema/knownhosts v1.2.1 // indirect + github.com/skeema/knownhosts v1.2.2 // indirect github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -72,8 +72,8 @@ require ( golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.18.0 // indirect - google.golang.org/protobuf v1.32.0 // indirect + golang.org/x/tools v0.19.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/godo.v2 v2.0.9 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 3cb6a38..668392f 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/bradleyfalzon/ghinstallation/v2 v2.9.0/go.mod h1:wmkTDJf8CmVypxE8ijIS github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo= -github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= +github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -75,10 +75,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= -github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -107,8 +105,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -137,14 +135,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/nestoca/joy v0.39.0 h1:thJ6MA8mgGlhU33cd7fIu3jXL2pwunyQ2DnfvX+eDfY= -github.com/nestoca/joy v0.39.0/go.mod h1:z6dv2SRpGwSFMcJR4kzzjvqjsGc5FEBkDyM3uvnYDxU= +github.com/nestoca/joy v0.39.1 h1:x/wOc5omtY18yuxotOYYa6OGRUzu6+5rrIXqL8nPWpI= +github.com/nestoca/joy v0.39.1/go.mod h1:z6dv2SRpGwSFMcJR4kzzjvqjsGc5FEBkDyM3uvnYDxU= github.com/nestoca/survey/v2 v2.0.0 h1:orM/TXtBSQJCPiZy9N51hcwH0WzFjQJ7TQKCfMTnGoQ= github.com/nestoca/survey/v2 v2.0.0/go.mod h1:QfmcQfCRtqsiBpePFhHEW0MY9QH7lSpw3CQinsdC/dc= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= +github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -160,8 +158,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD 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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -169,6 +167,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -255,12 +254,12 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/internal/generator/api.go b/internal/generator/api.go index 0a41c12..5587834 100644 --- a/internal/generator/api.go +++ b/internal/generator/api.go @@ -29,7 +29,7 @@ type API struct { func (api API) HandleGetParams(c *gin.Context) { var request GetParamsRequest - if err := c.BindJSON(request); err != nil { + if err := c.BindJSON(&request); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "invalid request body", "detail": err.Error(), diff --git a/internal/github/github.go b/internal/github/github.go index 9339ef2..fceed9a 100644 --- a/internal/github/github.go +++ b/internal/github/github.go @@ -13,7 +13,6 @@ import ( "github.com/go-git/go-git/v5/plumbing" githttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/rs/zerolog" - "github.com/rs/zerolog/log" ) type User struct { @@ -112,7 +111,7 @@ func (r *Repo) init() error { r.mutex.Lock() defer r.mutex.Unlock() - log.Debug().Msg("opening git repository") + r.logger.Debug().Msg("opening git repository") repository, err := git.PlainOpen(r.Metadata.Path) if err != nil { @@ -132,6 +131,25 @@ func (r *Repo) init() error { } } + if revision := r.Metadata.TargetRevision; revision != "" { + hash, err := repository.ResolveRevision(plumbing.Revision("refs/remotes/origin/" + revision)) + if err != nil { + return fmt.Errorf("resolving revision %s: %w", revision, err) + } + + worktree, err := repository.Worktree() + if err != nil { + return fmt.Errorf("getting worktree: %w", err) + } + + checkoutOpts := &git.CheckoutOptions{ + Hash: *hash, + } + if err := worktree.Checkout(checkoutOpts); err != nil { + return fmt.Errorf("checking out: %s: %w", revision, err) + } + } + r.repository = repository return nil } @@ -150,7 +168,8 @@ func (r *Repo) Pull() error { defer r.mutex.Unlock() r.logger.Debug().Msg("load git worktree") - w, err := r.repository.Worktree() + + worktree, err := r.repository.Worktree() if err != nil { return fmt.Errorf("loading git worktree: %w", err) } @@ -162,7 +181,13 @@ func (r *Repo) Pull() error { Force: true, } - if err := w.Pull(pullOpts); !errors.Is(err, git.NoErrAlreadyUpToDate) && !errors.Is(err, git.ErrNonFastForwardUpdate) { + if r.Metadata.TargetRevision != "" { + pullOpts.ReferenceName = plumbing.ReferenceName("refs/heads/" + r.Metadata.TargetRevision) + } + + if err := worktree.Pull(pullOpts); err == nil || errors.Is(err, git.NoErrAlreadyUpToDate) { + return nil + } else if !errors.Is(err, git.ErrNonFastForwardUpdate) { return err } @@ -181,7 +206,7 @@ func (r *Repo) Pull() error { if localHead.Hash() != *remoteHeadHash { r.logger.Debug().Msg("resetting to previous commit") - if err := w.Reset(&git.ResetOptions{Commit: *remoteHeadHash, Mode: git.HardReset}); err != nil { + if err := worktree.Reset(&git.ResetOptions{Commit: *remoteHeadHash, Mode: git.HardReset}); err != nil { return fmt.Errorf("resetting to previous commit: %w", err) } }