diff --git a/backend/core/errors/types.go b/backend/core/errors/types.go index c82a913c470..778d8a1a4b4 100644 --- a/backend/core/errors/types.go +++ b/backend/core/errors/types.go @@ -34,6 +34,7 @@ var ( Forbidden = register(&Type{httpCode: http.StatusForbidden, meta: "forbidden"}) NotFound = register(&Type{httpCode: http.StatusNotFound, meta: "not-found"}) Conflict = register(&Type{httpCode: http.StatusConflict, meta: "internal"}) + NotModified = register(&Type{httpCode: http.StatusNotModified, meta: "not-modified"}) //500+ Internal = register(&Type{httpCode: http.StatusInternalServerError, meta: "internal"}) diff --git a/backend/core/models/blueprint.go b/backend/core/models/blueprint.go index b9ff2a4574b..354c94408b0 100644 --- a/backend/core/models/blueprint.go +++ b/backend/core/models/blueprint.go @@ -84,7 +84,7 @@ func (BlueprintScope) TableName() string { type SyncPolicy struct { SkipOnFail bool `json:"skipOnFail"` - FullSync bool `json:"fullSync"` SkipCollectors bool `json:"skipCollectors"` + FullSync bool `json:"fullSync"` TimeAfter *time.Time `json:"timeAfter"` } diff --git a/backend/core/runner/run_task.go b/backend/core/runner/run_task.go index aeace3940b3..aee9c587544 100644 --- a/backend/core/runner/run_task.go +++ b/backend/core/runner/run_task.go @@ -312,12 +312,13 @@ func RunPluginSubTasks( } subtaskFinsied := false if !subtaskMeta.ForceRunOnResume { - sfc := errors.Must1( - basicRes.GetDal().Count( + if task.ID > 0 { + sfc := errors.Must1(basicRes.GetDal().Count( dal.From(&models.Subtask{}), dal.Where("task_id = ? AND name = ? AND finished_at IS NOT NULL", task.ID, subtaskMeta.Name), ), - ) - subtaskFinsied = sfc > 0 + ) + subtaskFinsied = sfc > 0 + } } if subtaskFinsied { logger.Info("subtask %s already finished previously", subtaskMeta.Name) diff --git a/backend/helpers/pluginhelper/api/collector_state_manager.go b/backend/helpers/pluginhelper/api/collector_state_manager.go new file mode 100644 index 00000000000..8aeeffebc52 --- /dev/null +++ b/backend/helpers/pluginhelper/api/collector_state_manager.go @@ -0,0 +1,119 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "time" + + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/dal" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models" +) + +// CollectorStateManager manages the state of the collector. It is used to determine whether +// the collector should run in incremental mode or full sync mode and what time range to collect. +type CollectorStateManager struct { + db dal.Dal + state *models.CollectorLatestState + syncPolicy *models.SyncPolicy + // IsIncremental indicates whether the collector should run in incremental mode or full sync mode + isIncremental bool + // Since is the start time of the time range to collect + since *time.Time + // Until is the end time of the time range to collect + until *time.Time +} + +// NewCollectorStateManager create a new CollectorStateManager +func NewCollectorStateManager(basicRes context.BasicRes, syncPolicy *models.SyncPolicy, rawTable, rawParams string) (stateManager *CollectorStateManager, err errors.Error) { + // load sync policy and make sure it is not nil + if syncPolicy == nil { + syncPolicy = &models.SyncPolicy{} + } + + // load the previous state from the database + db := basicRes.GetDal() + state := &models.CollectorLatestState{} + err = db.First(state, dal.Where(`raw_data_table = ? AND raw_data_params = ?`, rawTable, rawParams)) + if err != nil { + if db.IsErrorNotFound(err) { + state = &models.CollectorLatestState{ + RawDataTable: rawTable, + RawDataParams: rawParams, + } + err = nil + } else { + err = errors.Default.Wrap(err, "failed to load the previous collector state") + return + } + } + + // fullsync by default + now := time.Now() + stateManager = &CollectorStateManager{ + db: db, + state: state, + syncPolicy: syncPolicy, + isIncremental: false, + since: syncPolicy.TimeAfter, + until: &now, + } + // fallback to the previous timeAfter if no new value + if stateManager.since == nil { + stateManager.since = state.TimeAfter + } + + // if fullsync is set or no previous success start time, we are in the full sync mode + if syncPolicy.FullSync || state.LatestSuccessStart == nil { + return + } + + // if timeAfter is not set or NOT before the previous vaule, we are in the incremental mode + if syncPolicy.TimeAfter == nil || state.TimeAfter == nil || !syncPolicy.TimeAfter.Before(*state.TimeAfter) { + stateManager.isIncremental = true + stateManager.since = state.LatestSuccessStart + } + + return +} + +func (c *CollectorStateManager) IsIncremental() bool { + return c.isIncremental +} + +func (c *CollectorStateManager) GetSince() *time.Time { + return c.since +} + +func (c *CollectorStateManager) GetUntil() *time.Time { + return c.until +} + +func (c *CollectorStateManager) Close() errors.Error { + // update timeAfter in the database only for fullsync mode + if !c.isIncremental { + // prefer non-nil value + if c.syncPolicy.TimeAfter != nil { + c.state.TimeAfter = c.syncPolicy.TimeAfter + } + } + // always update the latest success start time + c.state.LatestSuccessStart = c.until + return c.db.Update(c.state) +} diff --git a/backend/helpers/pluginhelper/api/collector_state_manager_test.go b/backend/helpers/pluginhelper/api/collector_state_manager_test.go new file mode 100644 index 00000000000..5ffc39805bb --- /dev/null +++ b/backend/helpers/pluginhelper/api/collector_state_manager_test.go @@ -0,0 +1,160 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "testing" + "time" + + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models" + "github.com/apache/incubator-devlake/helpers/unithelper" + mockcontext "github.com/apache/incubator-devlake/mocks/core/context" + mockdal "github.com/apache/incubator-devlake/mocks/core/dal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestCollectorStateManager(t *testing.T) { + time0 := errors.Must1(time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")) + time1 := errors.Must1(time.Parse(time.RFC3339, "2021-01-01T00:00:00Z")) + time2 := errors.Must1(time.Parse(time.RFC3339, "2022-01-01T00:00:00Z")) + for _, tc := range []struct { + name string + state *models.CollectorLatestState + syncPolicy *models.SyncPolicy + expectedIsIncremental bool + expectedSince *time.Time + expectedNewStateTimeAfter *time.Time + }{ + { + name: "syncPolicy has no timeAfter - First run", + state: &models.CollectorLatestState{LatestSuccessStart: nil}, + syncPolicy: &models.SyncPolicy{TimeAfter: nil}, + expectedIsIncremental: false, + expectedSince: nil, + expectedNewStateTimeAfter: nil, + }, + { + name: "syncPolicy has no timeAfter - Second run", + state: &models.CollectorLatestState{LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{TimeAfter: nil}, + expectedIsIncremental: true, + expectedSince: &time1, + expectedNewStateTimeAfter: nil, + }, + { + name: "syncPolicy has no timeAfter - Third run with timeAfter specified", + state: &models.CollectorLatestState{LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time1}, + expectedIsIncremental: true, + expectedSince: &time1, + expectedNewStateTimeAfter: nil, + }, + { + name: "syncPolicy has timeAfter - First run", + state: &models.CollectorLatestState{LatestSuccessStart: nil}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time1}, + expectedIsIncremental: false, + expectedSince: &time1, + expectedNewStateTimeAfter: &time1, + }, + { + name: "syncPolicy has timeAfter - Second run with a later timeAfter", + state: &models.CollectorLatestState{TimeAfter: &time1, LatestSuccessStart: &time2}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time2}, + expectedIsIncremental: true, + expectedSince: &time2, + expectedNewStateTimeAfter: &time1, + }, + { + name: "syncPolicy has timeAfter - Third run with a earlier timeAfter", + state: &models.CollectorLatestState{TimeAfter: &time1, LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time0}, + expectedIsIncremental: false, + expectedSince: &time0, + expectedNewStateTimeAfter: &time0, + }, + { + name: "syncPolicy has timeAfter - Fourth run with a same timeAfter", + state: &models.CollectorLatestState{TimeAfter: &time1, LatestSuccessStart: &time2}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time1}, + expectedIsIncremental: true, + expectedSince: &time2, + expectedNewStateTimeAfter: &time1, + }, + { + name: "Full sync - with timeAfter", + state: &models.CollectorLatestState{TimeAfter: &time1, LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{FullSync: true}, + expectedIsIncremental: false, + expectedSince: &time1, + expectedNewStateTimeAfter: &time1, + }, + { + name: "Full sync - with newer timeAfter", + state: &models.CollectorLatestState{TimeAfter: &time1, LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time2, FullSync: true}, + expectedIsIncremental: false, + expectedSince: &time2, + expectedNewStateTimeAfter: &time2, + }, + { + name: "Full sync - with older timeAfter", + state: &models.CollectorLatestState{TimeAfter: &time1, LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{TimeAfter: &time0, FullSync: true}, + expectedIsIncremental: false, + expectedSince: &time0, + expectedNewStateTimeAfter: &time0, + }, + { + name: "Full sync - without timeAfter", + state: &models.CollectorLatestState{TimeAfter: nil, LatestSuccessStart: &time1}, + syncPolicy: &models.SyncPolicy{FullSync: true}, + expectedIsIncremental: false, + expectedSince: nil, + expectedNewStateTimeAfter: nil, + }, + } { + started := time.Now() + t.Run(tc.name, func(t *testing.T) { + mockBasicRes := newMockBasicRes(tc.state) + stateManager, err := NewCollectorStateManager(mockBasicRes, tc.syncPolicy, "table", "params") + assert.Nil(t, err) + assert.Equal(t, tc.expectedSince, stateManager.since) + assert.Equal(t, tc.expectedIsIncremental, stateManager.isIncremental) + assert.Nil(t, stateManager.Close()) + assert.Equal(t, tc.expectedNewStateTimeAfter, stateManager.state.TimeAfter) + // LatestSuccessStart should be updated + assert.GreaterOrEqual(t, stateManager.state.LatestSuccessStart.Unix(), started.Unix()) + // First and update should both be called once + mockBasicRes.AssertExpectations(t) + }) + } +} + +func newMockBasicRes(state *models.CollectorLatestState) *mockcontext.BasicRes { + // Refresh Global Variables and set the sql mock + return unithelper.DummyBasicRes(func(mockDal *mockdal.Dal) { + mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + dst := args.Get(0).(*models.CollectorLatestState) + *dst = *state + }).Return(nil).Once() + mockDal.On("Update", mock.Anything, mock.Anything).Return(nil).Once() + }) +} diff --git a/backend/helpers/unithelper/dummy_baesres.go b/backend/helpers/unithelper/dummy_baesres.go index bd87969fbee..346c515ae65 100644 --- a/backend/helpers/unithelper/dummy_baesres.go +++ b/backend/helpers/unithelper/dummy_baesres.go @@ -32,8 +32,8 @@ func DummyBasicRes(callback func(mockDal *mockdal.Dal)) *mockcontext.BasicRes { callback(mockDal) mockRes.On("GetDal").Return(mockDal) - mockRes.On("GetLogger").Return(mockLog) - mockRes.On("GetConfig", mock.Anything).Return("") + mockRes.On("GetLogger").Return(mockLog).Maybe() + mockRes.On("GetConfig", mock.Anything).Return("").Maybe() mockDal.On("AllTables").Return(nil, nil) return mockRes } diff --git a/backend/plugins/gitextractor/parser/clone_gitcli.go b/backend/plugins/gitextractor/parser/clone_gitcli.go index 0fbe0333bd9..bd045faddf5 100644 --- a/backend/plugins/gitextractor/parser/clone_gitcli.go +++ b/backend/plugins/gitextractor/parser/clone_gitcli.go @@ -30,12 +30,16 @@ import ( "github.com/apache/incubator-devlake/core/errors" "github.com/apache/incubator-devlake/core/log" "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" ) var _ RepoCloner = (*GitcliCloner)(nil) +var ErrShallowInfoProcessing = errors.BadInput.New("No data found for the selected time range. Please revise the 'Time Range' on your Project/Blueprint/Configuration page or in the API parameter.") +var ErrNoDataOnIncrementalMode = errors.NotModified.New("No data found since the previous run.") type GitcliCloner struct { - logger log.Logger + logger log.Logger + stateManager *api.CollectorStateManager } func NewGitcliCloner(basicRes context.BasicRes) *GitcliCloner { @@ -45,6 +49,42 @@ func NewGitcliCloner(basicRes context.BasicRes) *GitcliCloner { } func (g *GitcliCloner) CloneRepo(ctx plugin.SubTaskContext, localDir string) errors.Error { + taskData := ctx.GetData().(*GitExtractorTaskData) + // load state + stateManager, err := api.NewCollectorStateManager( + ctx, + ctx.TaskContext().SyncPolicy(), + "gitextractor", + fmt.Sprintf( + `{"RepoId: "%s","SkipCommitStat": %v, "SkipCommitFiles": %v}`, + taskData.Options.RepoId, + *taskData.Options.SkipCommitStat, + *taskData.Options.SkipCommitFiles, + ), + ) + if err != nil { + return err + } + g.stateManager = stateManager + + cmd, err := g.buildCloneCommand(ctx, localDir, g.stateManager.GetSince()) + if err != nil { + return err + } + err = g.execCloneCommand(cmd) + if err != nil { + // it is likely that nothing to collect on incrmental mode + if errors.Is(err, ErrShallowInfoProcessing) && stateManager.IsIncremental() { + return ErrNoDataOnIncrementalMode + } + return err + } + + // save state + return g.stateManager.Close() +} + +func (g *GitcliCloner) buildCloneCommand(ctx plugin.SubTaskContext, localDir string, since *time.Time) (*exec.Cmd, errors.Error) { taskData := ctx.GetData().(*GitExtractorTaskData) args := []string{"clone", taskData.Options.Url, localDir, "--bare", "--progress"} env := []string{} @@ -58,7 +98,7 @@ func (g *GitcliCloner) CloneRepo(ctx plugin.SubTaskContext, localDir string) err if taskData.Options.Proxy != "" { parsedProxyURL, e := url.Parse(taskData.Options.Proxy) if e != nil { - return errors.BadInput.Wrap(e, "failed to parse the proxy URL") + return nil, errors.BadInput.Wrap(e, "failed to parse the proxy URL") } proxyCommand := "corkscrew" sshCmdArgs = append(sshCmdArgs, "-o", fmt.Sprintf(`ProxyCommand="%s %s %s %%h %%p"`, proxyCommand, parsedProxyURL.Hostname(), parsedProxyURL.Port())) @@ -68,16 +108,16 @@ func (g *GitcliCloner) CloneRepo(ctx plugin.SubTaskContext, localDir string) err pkFile, err := os.CreateTemp("", "gitext-pk") if err != nil { g.logger.Error(err, "create temp private key file error") - return errors.Default.New("failed to handle the private key") + return nil, errors.Default.New("failed to handle the private key") } if _, e := pkFile.WriteString(taskData.Options.PrivateKey + "\n"); e != nil { g.logger.Error(err, "write private key file error") - return errors.Default.New("failed to write the private key") + return nil, errors.Default.New("failed to write the private key") } pkFile.Close() if e := os.Chmod(pkFile.Name(), 0600); e != nil { g.logger.Error(err, "chmod private key file error") - return errors.Default.New("failed to modify the private key") + return nil, errors.Default.New("failed to modify the private key") } if taskData.Options.Passphrase != "" { @@ -91,7 +131,7 @@ func (g *GitcliCloner) CloneRepo(ctx plugin.SubTaskContext, localDir string) err if ppout, pperr := pp.CombinedOutput(); pperr != nil { g.logger.Error(pperr, "change private key passphrase error") g.logger.Info(string(ppout)) - return errors.Default.New("failed to decrypt the private key") + return nil, errors.Default.New("failed to decrypt the private key") } } defer os.Remove(pkFile.Name()) @@ -101,23 +141,22 @@ func (g *GitcliCloner) CloneRepo(ctx plugin.SubTaskContext, localDir string) err env = append(env, fmt.Sprintf("GIT_SSH_COMMAND=ssh %s", strings.Join(sshCmdArgs, " "))) } } - // support time after - syncPolicy := ctx.TaskContext().SyncPolicy() - if syncPolicy.TimeAfter != nil { - args = append(args, fmt.Sprintf("--shallow-since=%s", syncPolicy.TimeAfter.Format(time.RFC3339))) + // support time after and diff sync + if since != nil { + args = append(args, fmt.Sprintf("--shallow-since=%s", since.Format(time.RFC3339))) } // support skipping blobs collection if *taskData.Options.SkipCommitStat { args = append(args, "--filter=blob:none") } + // fmt.Printf("args: %v\n", args) + g.logger.Debug("git %v", args) cmd := exec.CommandContext(ctx.GetContext(), "git", args...) cmd.Env = env - // stdout, stderr := cmd.CombinedOutput() - // fmt.Println("stdout" + string(stdout)) - // if stderr != nil { - // fmt.Println("stderr" + stderr.Error()) - // } - // reading progress + return cmd, nil +} + +func (g *GitcliCloner) execCloneCommand(cmd *exec.Cmd) errors.Error { stdout, err := cmd.StdoutPipe() if err != nil { g.logger.Error(err, "stdout pipe error") @@ -158,7 +197,7 @@ func (g *GitcliCloner) CloneRepo(ctx plugin.SubTaskContext, localDir string) err if err != nil { g.logger.Error(err, "git exited with error\n%s", combinedOutput.String()) if strings.Contains(combinedOutput.String(), "stderr: fatal: error processing shallow info: 4") { - return errors.BadInput.New("No data found for the selected time range. Please revise the 'Time Range' on your Project/Blueprint/Configuration page or in the API parameter.") + return ErrShallowInfoProcessing } return errors.Default.New("git exit error") } diff --git a/backend/plugins/gitextractor/parser/repo_gogit.go b/backend/plugins/gitextractor/parser/repo_gogit.go index f93655d86f6..d98ec3d95ed 100644 --- a/backend/plugins/gitextractor/parser/repo_gogit.go +++ b/backend/plugins/gitextractor/parser/repo_gogit.go @@ -124,9 +124,6 @@ func (r *GogitRepoCollector) CountBranches(ctx context.Context) (int, error) { func(r *plumbing.Reference) bool { return r.Name().IsBranch() || r.Name().IsRemote() }, refIter) - if err != nil { - return 0, err - } var branchesCount int headRef, err := r.repo.Head() diff --git a/backend/plugins/gitextractor/parser/taskdata.go b/backend/plugins/gitextractor/parser/taskdata.go index 615d1c62404..b0a44c66040 100644 --- a/backend/plugins/gitextractor/parser/taskdata.go +++ b/backend/plugins/gitextractor/parser/taskdata.go @@ -22,9 +22,10 @@ import ( ) type GitExtractorTaskData struct { - Options *GitExtractorOptions - ParsedURL *url.URL - GitRepo RepoCollector + Options *GitExtractorOptions + ParsedURL *url.URL + GitRepo RepoCollector + SkipAllSubtasks bool // skip all tasks without error if true } type GitExtractorOptions struct { diff --git a/backend/plugins/gitextractor/tasks/repo_cloner.go b/backend/plugins/gitextractor/tasks/repo_cloner.go index 40e7507244c..965b0e8f6ba 100644 --- a/backend/plugins/gitextractor/tasks/repo_cloner.go +++ b/backend/plugins/gitextractor/tasks/repo_cloner.go @@ -56,6 +56,10 @@ func CloneGitRepo(subTaskCtx plugin.SubTaskContext) errors.Error { repoCloner := parser.NewGitcliCloner(subTaskCtx) err = repoCloner.CloneRepo(subTaskCtx, localDir) if err != nil { + if errors.Is(err, parser.ErrNoDataOnIncrementalMode) { + taskData.SkipAllSubtasks = true + return nil + } return err } diff --git a/backend/plugins/gitextractor/tasks/repo_collector.go b/backend/plugins/gitextractor/tasks/repo_collector.go index 02571cf09f8..23fbe173455 100644 --- a/backend/plugins/gitextractor/tasks/repo_collector.go +++ b/backend/plugins/gitextractor/tasks/repo_collector.go @@ -24,6 +24,9 @@ import ( ) func CollectGitCommits(subTaskCtx plugin.SubTaskContext) errors.Error { + if subTaskCtx.TaskContext().GetData().(*parser.GitExtractorTaskData).SkipAllSubtasks { + return nil + } repo := getGitRepo(subTaskCtx) if count, err := repo.CountCommits(subTaskCtx.GetContext()); err != nil { subTaskCtx.GetLogger().Error(err, "unable to get commit count") @@ -36,6 +39,9 @@ func CollectGitCommits(subTaskCtx plugin.SubTaskContext) errors.Error { } func CollectGitBranches(subTaskCtx plugin.SubTaskContext) errors.Error { + if subTaskCtx.TaskContext().GetData().(*parser.GitExtractorTaskData).SkipAllSubtasks { + return nil + } repo := getGitRepo(subTaskCtx) if count, err := repo.CountBranches(subTaskCtx.GetContext()); err != nil { subTaskCtx.GetLogger().Error(err, "unable to get branch count") @@ -48,6 +54,9 @@ func CollectGitBranches(subTaskCtx plugin.SubTaskContext) errors.Error { } func CollectGitTags(subTaskCtx plugin.SubTaskContext) errors.Error { + if subTaskCtx.TaskContext().GetData().(*parser.GitExtractorTaskData).SkipAllSubtasks { + return nil + } repo := getGitRepo(subTaskCtx) if count, err := repo.CountTags(subTaskCtx.GetContext()); err != nil { subTaskCtx.GetLogger().Error(err, "unable to get tag count") @@ -60,6 +69,9 @@ func CollectGitTags(subTaskCtx plugin.SubTaskContext) errors.Error { } func CollectGitDiffLines(subTaskCtx plugin.SubTaskContext) errors.Error { + if subTaskCtx.TaskContext().GetData().(*parser.GitExtractorTaskData).SkipAllSubtasks { + return nil + } repo := getGitRepo(subTaskCtx) opt := subTaskCtx.GetData().(*parser.GitExtractorTaskData).Options if !*opt.SkipCommitStat { diff --git a/env.example b/env.example index a934fbdea5f..2232eb9a765 100755 --- a/env.example +++ b/env.example @@ -67,7 +67,7 @@ IN_SECURE_SKIP_VERIFY= ########################## USE_GO_GIT_IN_GIT_EXTRACTOR=false # NOTE that COMMIT_FILES is part of the COMMIT_STAT -SKIP_COMMIT_STAT=true +SKIP_COMMIT_STAT=false SKIP_COMMIT_FILES=true # Set if response error when requesting /connections/{connection_id}/test should be wrapped or not