Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package digger-cli for openSUSE:Factory 
checked in at 2026-10-01 16:48:08
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/digger-cli (Old)
 and      /work/SRC/openSUSE:Factory/.digger-cli.new.1253 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "digger-cli"

Thu Oct  1 16:48:08 2026 rev:56 rq:1381776 version:0.6.152

Changes:
--------
--- /work/SRC/openSUSE:Factory/digger-cli/digger-cli.changes    2026-09-21 
12:08:55.518450865 +0200
+++ /work/SRC/openSUSE:Factory/.digger-cli.new.1253/digger-cli.changes  
2026-10-01 16:49:37.184511891 +0200
@@ -1,0 +2,8 @@
+Thu Oct 01 05:14:37 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.6.152:
+  * fix: honor git_timeout from digger.yml before cloning (#2724)
+  * fix: adjust GitHub check run char limit considering the MD
+    terraform text wrapper (#2615)
+
+-------------------------------------------------------------------

Old:
----
  digger-cli-0.6.151.obscpio

New:
----
  digger-cli-0.6.152.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ digger-cli.spec ++++++
--- /var/tmp/diff_new_pack.8BjW3e/_old  2026-10-01 16:49:38.524568009 +0200
+++ /var/tmp/diff_new_pack.8BjW3e/_new  2026-10-01 16:49:38.526568093 +0200
@@ -19,7 +19,7 @@
 %define executable_name digger
 
 Name:           digger-cli
-Version:        0.6.151
+Version:        0.6.152
 Release:        0
 Summary:        CLI for the digger open source IaC orchestration tool
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.8BjW3e/_old  2026-10-01 16:49:38.595570983 +0200
+++ /var/tmp/diff_new_pack.8BjW3e/_new  2026-10-01 16:49:38.601571234 +0200
@@ -6,8 +6,8 @@
     <param name="exclude">go.mod</param>
     <param name="exclude">go.work</param>
     <param name="exclude">go.work.sum</param>
-    <param name="revision">refs/tags/v0.6.151</param>
-    <param name="match-tag">v0.6.151</param>
+    <param name="revision">refs/tags/v0.6.152</param>
+    <param name="match-tag">v0.6.152</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.8BjW3e/_old  2026-10-01 16:49:38.648573203 +0200
+++ /var/tmp/diff_new_pack.8BjW3e/_new  2026-10-01 16:49:38.652573370 +0200
@@ -5,6 +5,6 @@
                 <param 
name="url">https://github.com/johanneskastl/digger</param>
               <param 
name="changesrevision">8fe377068e53e2050ff4c745388d8428d2b13bb0</param></service><service
 name="tar_scm">
                 <param 
name="url">https://github.com/diggerhq/digger.git</param>
-              <param 
name="changesrevision">85cd59db7caa3d8bf6cefe303a62c12812b0bc48</param></service></servicedata>
+              <param 
name="changesrevision">9ac4e2237b7b8afa455f422b92737bfe106d495c</param></service></servicedata>
 (No newline at EOF)
 

++++++ digger-cli-0.6.151.obscpio -> digger-cli-0.6.152.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/backend/controllers/cache.go 
new/digger-cli-0.6.152/backend/controllers/cache.go
--- old/digger-cli-0.6.151/backend/controllers/cache.go 2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/backend/controllers/cache.go 2026-09-30 
18:56:09.000000000 +0200
@@ -69,7 +69,7 @@
        cloneUrl := fmt.Sprintf("https://%v/%v";, utils.GetGithubHostname(), 
repo.RepoFullName)
        branch := request.Branch
 
-       _, token, err := utils.GetGithubService(d.GithubClientProvider, 
installationId, repoFullName, repoOwner, repoName)
+       ghService, token, err := utils.GetGithubService(d.GithubClientProvider, 
installationId, repoFullName, repoOwner, repoName)
        if err != nil {
                slog.Error("Could not get GitHub service", "error", err, 
"repoFullName", repoFullName, "orgId", orgId)
                c.String(http.StatusInternalServerError, fmt.Sprintf("could not 
get github service %v %v", repoFullName, orgId))
@@ -83,7 +83,7 @@
        // update the cache here, do it async for immediate response
        go func(ctx context.Context) {
                defer logging.InheritRequestLogger(ctx)()
-               err = git_utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", 
*token, "", func(dir string) error {
+               err = git_utils.CloneGitRepoAndDoActionWithConfig(cloneUrl, 
branch, "", *token, "", ghService.ReadRepositoryFile, func(dir string) error {
                        diggerYmlBytes, err := os.ReadFile(path.Join(dir, 
"digger.yml"))
                        diggerYmlStr = string(diggerYmlBytes)
                        config, _, _, newAtlantisConfig, err = 
dg_configuration.LoadDiggerConfig(dir, true, nil, nil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/backend/controllers/github_helpers.go 
new/digger-cli-0.6.152/backend/controllers/github_helpers.go
--- old/digger-cli-0.6.151/backend/controllers/github_helpers.go        
2026-09-13 11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/backend/controllers/github_helpers.go        
2026-09-30 18:56:09.000000000 +0200
@@ -863,7 +863,7 @@
        var diggerYmlStr string
        var dependencyGraph graph.Graph[string, digger_config.Project]
 
-       err = git_utils.CloneGitRepoAndDoAction(cloneUrl, branch, commitSha, 
*token, "", func(dir string) error {
+       err = git_utils.CloneGitRepoAndDoActionWithConfig(cloneUrl, branch, 
commitSha, *token, "", ghService.ReadRepositoryFile, func(dir string) error {
                slog.Debug("Reading Digger config from cloned repository", 
"directory", dir)
 
                diggerYmlStr, err = digger_config.ReadDiggerYmlFileContents(dir)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/backend/controllers/projects_helpers.go 
new/digger-cli-0.6.152/backend/controllers/projects_helpers.go
--- old/digger-cli-0.6.151/backend/controllers/projects_helpers.go      
2026-09-13 11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/backend/controllers/projects_helpers.go      
2026-09-30 18:56:09.000000000 +0200
@@ -512,9 +512,12 @@
        // Character limit check - GitHub check run text field has a 65535 
character limit
        const maxCheckRunTextLength = 65535
        cutOffMsg := "\n[Character limit exceeded, output truncated]"
-       if utf8.RuneCountInString(job.TerraformOutput) > maxCheckRunTextLength {
+       wrapper := "```terraform\n" + "```\n"
+       maxOutputLength := maxCheckRunTextLength - 
utf8.RuneCountInString(wrapper)
+
+       if utf8.RuneCountInString(job.TerraformOutput) > maxOutputLength {
                runes := []rune(job.TerraformOutput)
-               truncateAt := maxCheckRunTextLength - 
utf8.RuneCountInString(cutOffMsg)
+               truncateAt := maxOutputLength - 
utf8.RuneCountInString(cutOffMsg)
                job.TerraformOutput = string(runes[:truncateAt]) + cutOffMsg
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/backend/controllers/projects_test.go 
new/digger-cli-0.6.152/backend/controllers/projects_test.go
--- old/digger-cli-0.6.151/backend/controllers/projects_test.go 2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/backend/controllers/projects_test.go 2026-09-30 
18:56:09.000000000 +0200
@@ -128,7 +128,7 @@
                },
                {
                        name:           "at limit - no truncation",
-                       inputLength:    65535,
+                       inputLength:    65535 - 17, // account for wrapper
                        expectTruncate: false,
                },
                {
@@ -140,25 +140,29 @@
 
        const maxCheckRunTextLength = 65535
        cutOffMsg := "\n[Character limit exceeded, output truncated]"
+       wrapper := "```terraform\n" + "```\n"
+       maxOutputLength := maxCheckRunTextLength - 
utf8.RuneCountInString(wrapper)
 
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        input := strings.Repeat("a", tt.inputLength)
 
                        result := input
-                       if utf8.RuneCountInString(result) > 
maxCheckRunTextLength {
+                       if utf8.RuneCountInString(result) > maxOutputLength {
                                runes := []rune(result)
-                               truncateAt := maxCheckRunTextLength - 
utf8.RuneCountInString(cutOffMsg)
+                               truncateAt := maxOutputLength - 
utf8.RuneCountInString(cutOffMsg)
                                result = string(runes[:truncateAt]) + cutOffMsg
                        }
 
+                       text := "```terraform\n" + result + "```\n"
+
                        if tt.expectTruncate {
-                               assert.Equal(t, maxCheckRunTextLength, 
utf8.RuneCountInString(result),
+                               assert.Equal(t, maxCheckRunTextLength, 
utf8.RuneCountInString(text),
                                        "truncated output should be exactly 
65535 characters")
                                assert.True(t, strings.HasSuffix(result, 
cutOffMsg),
                                        "truncated output should end with 
cutoff message")
                        } else {
-                               assert.Equal(t, tt.inputLength, 
utf8.RuneCountInString(result),
+                               assert.LessOrEqual(t, 
utf8.RuneCountInString(text), maxCheckRunTextLength,
                                        "non-truncated output should maintain 
original length")
                                assert.False(t, strings.HasSuffix(result, 
cutOffMsg),
                                        "non-truncated output should not have 
cutoff message")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/backend/utils/bitbucket.go 
new/digger-cli-0.6.152/backend/utils/bitbucket.go
--- old/digger-cli-0.6.151/backend/utils/bitbucket.go   2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/backend/utils/bitbucket.go   2026-09-30 
18:56:09.000000000 +0200
@@ -95,7 +95,7 @@
                "changedFilesCount", len(changedFiles),
        )
 
-       err = git_utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", token, 
"x-token-auth", func(dir string) error {
+       err = git_utils.CloneGitRepoAndDoActionWithConfig(cloneUrl, branch, "", 
token, "x-token-auth", service.ReadRepositoryFile, func(dir string) error {
                diggerYmlPath := path.Join(dir, "digger.yml")
                diggerYmlBytes, err := os.ReadFile(diggerYmlPath)
                if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/backend/utils/gitlab.go 
new/digger-cli-0.6.152/backend/utils/gitlab.go
--- old/digger-cli-0.6.151/backend/utils/gitlab.go      2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/backend/utils/gitlab.go      2026-09-30 
18:56:09.000000000 +0200
@@ -114,7 +114,7 @@
                "changedFilesCount", len(changedFiles),
        )
 
-       err = git_utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", token, 
"", func(dir string) error {
+       err = git_utils.CloneGitRepoAndDoActionWithConfig(cloneUrl, branch, "", 
token, "", service.ReadRepositoryFile, func(dir string) error {
                diggerYmlPath := path.Join(dir, "digger.yml")
                diggerYmlBytes, err := os.ReadFile(diggerYmlPath)
                if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/background/projects-refresh-service/projects_refesh_main.go
 
new/digger-cli-0.6.152/background/projects-refresh-service/projects_refesh_main.go
--- 
old/digger-cli-0.6.151/background/projects-refresh-service/projects_refesh_main.go
  2026-09-13 11:13:52.000000000 +0200
+++ 
new/digger-cli-0.6.152/background/projects-refresh-service/projects_refesh_main.go
  2026-09-30 18:56:09.000000000 +0200
@@ -3,6 +3,7 @@
 import (
        "fmt"
        "github.com/diggerhq/digger/backend/models"
+       "github.com/diggerhq/digger/libs/ci/github"
        dg_configuration "github.com/diggerhq/digger/libs/digger_config"
        utils3 "github.com/diggerhq/digger/libs/git_utils"
        "log/slog"
@@ -45,7 +46,12 @@
        models.ConnectDatabase()
 
        slog.Info("refreshing projects from repo", "repoFullName", repoFullName)
-       err := utils3.CloneGitRepoAndDoAction(cloneUrl, branch, "", token, "", 
func(dir string) error {
+       ghService, err := github.NewServiceForCloneURL(cloneUrl, token)
+       if err != nil {
+               slog.Error("failed to create GitHub client", "error", err)
+               os.Exit(1)
+       }
+       err = utils3.CloneGitRepoAndDoActionWithConfig(cloneUrl, branch, "", 
token, "", ghService.ReadRepositoryFile, func(dir string) error {
                config, _, err := dg_configuration.LoadDiggerConfigYaml(dir, 
true, nil, nil)
                if err != nil {
                        slog.Error("failed to load digger.yml: %v", "error", 
err)
@@ -61,7 +67,7 @@
                return nil
        })
        if err != nil {
-               slog.Error("error while cloning repo: %v", err)
+               slog.Error("error while cloning repo", "error", err)
                os.Exit(1)
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/docs/ce/reference/digger.yml.mdx 
new/digger-cli-0.6.152/docs/ce/reference/digger.yml.mdx
--- old/digger-cli-0.6.151/docs/ce/reference/digger.yml.mdx     2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/docs/ce/reference/digger.yml.mdx     2026-09-30 
18:56:09.000000000 +0200
@@ -216,7 +216,7 @@
 </ParamField>
 
 <ParamField path="git_timeout" type="integer" default="30">
-  Timeout in seconds for each git command (clone, checkout) Digger runs when 
it clones a repository. Increase this for large repositories that take longer 
than 30 seconds to clone.
+  Timeout in seconds for each git command (clone, checkout) Digger runs when 
loading repository configuration. Increase this for large repositories, for 
example `git_timeout: 120`. Digger reads this setting through the Git provider 
API before cloning, using the requested branch or commit. It checks 
`digger.yml` first, then `digger.yaml`. An omitted, zero, or negative value 
uses the 30-second default. The initial API lookup has its own 30-second 
timeout.
 </ParamField>
 
 <ParamField path="projects" type="array">
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/drift/utils/github.go 
new/digger-cli-0.6.152/drift/utils/github.go
--- old/digger-cli-0.6.151/drift/utils/github.go        2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/drift/utils/github.go        2026-09-30 
18:56:09.000000000 +0200
@@ -107,7 +107,7 @@
 
        var changedFiles []string = nil
 
-       err = utils2.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", 
func(dir string) error {
+       err = utils2.CloneGitRepoAndDoActionWithConfig(cloneUrl, branch, "", 
*token, "", ghService.ReadRepositoryFile, func(dir string) error {
                diggerYmlBytes, err := os.ReadFile(path.Join(dir, "digger.yml"))
                diggerYmlStr = string(diggerYmlBytes)
                config, _, dependencyGraph, _, err = 
dg_configuration.LoadDiggerConfig(dir, true, changedFiles, nil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/libs/ci/bitbucket/repository_file.go 
new/digger-cli-0.6.152/libs/ci/bitbucket/repository_file.go
--- old/digger-cli-0.6.151/libs/ci/bitbucket/repository_file.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/ci/bitbucket/repository_file.go 2026-09-30 
18:56:09.000000000 +0200
@@ -0,0 +1,34 @@
+package bitbucket
+
+import (
+       "context"
+       "fmt"
+       "io"
+       "net/http"
+       "net/url"
+       "os"
+)
+
+func (b BitbucketAPI) ReadRepositoryFile(ctx context.Context, path, ref 
string) ([]byte, error) {
+       if ref == "" {
+               ref = "HEAD"
+       }
+       endpoint := fmt.Sprintf("%s/repositories/%s/%s/src/%s/%s", 
bitbucketBaseURL, url.PathEscape(b.RepoWorkspace), url.PathEscape(b.RepoName), 
url.PathEscape(ref), url.PathEscape(path))
+       req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, 
nil)
+       if err != nil {
+               return nil, err
+       }
+       req.Header.Set("Authorization", "Bearer "+b.AuthToken)
+       response, err := b.HttpClient.Do(req)
+       if err != nil {
+               return nil, err
+       }
+       defer response.Body.Close()
+       if response.StatusCode == http.StatusNotFound {
+               return nil, fmt.Errorf("%s: %w", path, os.ErrNotExist)
+       }
+       if response.StatusCode != http.StatusOK {
+               return nil, fmt.Errorf("read %s: Bitbucket returned HTTP %d", 
path, response.StatusCode)
+       }
+       return io.ReadAll(response.Body)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/libs/ci/bitbucket/repository_file_test.go 
new/digger-cli-0.6.152/libs/ci/bitbucket/repository_file_test.go
--- old/digger-cli-0.6.151/libs/ci/bitbucket/repository_file_test.go    
1970-01-01 01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/ci/bitbucket/repository_file_test.go    
2026-09-30 18:56:09.000000000 +0200
@@ -0,0 +1,45 @@
+package bitbucket
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "io"
+       "net/http"
+       "os"
+       "strings"
+       "testing"
+)
+
+type repositoryFileTransport func(*http.Request) (*http.Response, error)
+
+func (f repositoryFileTransport) RoundTrip(r *http.Request) (*http.Response, 
error) {
+       return f(r)
+}
+
+func TestReadRepositoryFile(t *testing.T) {
+       for _, status := range []int{http.StatusOK, http.StatusNotFound, 
http.StatusForbidden} {
+               t.Run(fmt.Sprint(status), func(t *testing.T) {
+                       svc := BitbucketAPI{
+                               RepoWorkspace: "owner", RepoName: "repo", 
AuthToken: "test-token",
+                               HttpClient: http.Client{Transport: 
repositoryFileTransport(func(r *http.Request) (*http.Response, error) {
+                                       if r.URL.EscapedPath() != 
"/2.0/repositories/owner/repo/src/feature%2Ftimeout/digger.yml" {
+                                               t.Errorf("unexpected request: 
%s", r.URL)
+                                       }
+                                       if r.Header.Get("Authorization") != 
"Bearer test-token" {
+                                               t.Error("missing 
authentication")
+                                       }
+                                       return &http.Response{StatusCode: 
status, Body: io.NopCloser(strings.NewReader("git_timeout: 120\n")), Header: 
make(http.Header)}, nil
+                               })},
+                       }
+                       data, err := 
svc.ReadRepositoryFile(context.Background(), "digger.yml", "feature/timeout")
+                       if status == http.StatusOK {
+                               if err != nil || string(data) != "git_timeout: 
120\n" {
+                                       t.Fatalf("got %q, %v", data, err)
+                               }
+                       } else if err == nil || errors.Is(err, os.ErrNotExist) 
!= (status == http.StatusNotFound) {
+                               t.Fatalf("unexpected error for HTTP %d: %v", 
status, err)
+                       }
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/libs/ci/github/repository_file.go 
new/digger-cli-0.6.152/libs/ci/github/repository_file.go
--- old/digger-cli-0.6.151/libs/ci/github/repository_file.go    1970-01-01 
01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/ci/github/repository_file.go    2026-09-30 
18:56:09.000000000 +0200
@@ -0,0 +1,48 @@
+package github
+
+import (
+       "context"
+       "fmt"
+       "net/http"
+       "net/url"
+       "os"
+       "strings"
+
+       "github.com/google/go-github/v61/github"
+)
+
+// NewServiceForCloneURL creates a client for workers that only have a clone 
URL and token.
+func NewServiceForCloneURL(cloneURL, token string) (*GithubService, error) {
+       u, err := url.Parse(cloneURL)
+       if err != nil || u.Host == "" || (u.Scheme != "https" && u.Scheme != 
"http") {
+               return nil, fmt.Errorf("expected an HTTP(S) GitHub clone URL")
+       }
+       parts := strings.Split(strings.Trim(strings.TrimSuffix(u.Path, ".git"), 
"/"), "/")
+       if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
+               return nil, fmt.Errorf("expected owner/repository in GitHub 
clone URL")
+       }
+       client := github.NewClient(nil).WithAuthToken(token)
+       if u.Host != "github.com" {
+               baseURL := u.Scheme + "://" + u.Host + "/"
+               client, err = client.WithEnterpriseURLs(baseURL, baseURL)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       return &GithubService{Client: client, Owner: parts[0], RepoName: 
parts[1]}, nil
+}
+
+func (svc GithubService) ReadRepositoryFile(ctx context.Context, path, ref 
string) ([]byte, error) {
+       file, _, response, err := svc.Client.Repositories.GetContents(ctx, 
svc.Owner, svc.RepoName, path, &github.RepositoryContentGetOptions{Ref: ref})
+       if response != nil && response.StatusCode == http.StatusNotFound {
+               return nil, fmt.Errorf("%s: %w", path, os.ErrNotExist)
+       }
+       if err != nil {
+               return nil, err
+       }
+       if file == nil {
+               return nil, fmt.Errorf("%s is not a file", path)
+       }
+       contents, err := file.GetContent()
+       return []byte(contents), err
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/libs/ci/github/repository_file_test.go 
new/digger-cli-0.6.152/libs/ci/github/repository_file_test.go
--- old/digger-cli-0.6.151/libs/ci/github/repository_file_test.go       
1970-01-01 01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/ci/github/repository_file_test.go       
2026-09-30 18:56:09.000000000 +0200
@@ -0,0 +1,68 @@
+package github
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "net/http"
+       "net/http/httptest"
+       "os"
+       "testing"
+)
+
+func TestReadRepositoryFile(t *testing.T) {
+       for _, status := range []int{http.StatusOK, http.StatusNotFound, 
http.StatusForbidden} {
+               t.Run(fmt.Sprint(status), func(t *testing.T) {
+                       server := httptest.NewServer(http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
+                               if r.URL.Path != 
"/api/v3/repos/owner/repo/contents/digger.yml" || r.URL.Query().Get("ref") != 
"feature/timeout" {
+                                       t.Errorf("unexpected request: %s", 
r.URL)
+                               }
+                               if r.Header.Get("Authorization") != "Bearer 
test-token" {
+                                       t.Error("missing authentication")
+                               }
+                               w.Header().Set("Content-Type", 
"application/json")
+                               w.WriteHeader(status)
+                               if status == http.StatusOK {
+                                       fmt.Fprint(w, 
`{"type":"file","encoding":"base64","content":"Z2l0X3RpbWVvdXQ6IDEyMAo="}`)
+                               } else {
+                                       fmt.Fprint(w, 
`{"message":"unavailable"}`)
+                               }
+                       }))
+                       defer server.Close()
+                       svc, err := 
NewServiceForCloneURL(server.URL+"/owner/repo.git", "test-token")
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       data, err := 
svc.ReadRepositoryFile(context.Background(), "digger.yml", "feature/timeout")
+                       switch status {
+                       case http.StatusOK:
+                               if err != nil || string(data) != "git_timeout: 
120\n" {
+                                       t.Fatalf("got %q, %v", data, err)
+                               }
+                       case http.StatusNotFound:
+                               if !errors.Is(err, os.ErrNotExist) {
+                                       t.Fatalf("expected missing file, got 
%v", err)
+                               }
+                       case http.StatusForbidden:
+                               if err == nil || errors.Is(err, os.ErrNotExist) 
{
+                                       t.Fatalf("expected API failure, got 
%v", err)
+                               }
+                       }
+               })
+       }
+}
+
+func TestNewServiceForCloneURL(t *testing.T) {
+       svc, err := NewServiceForCloneURL("https://github.com/owner/repo.git";, 
"test-token")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if svc.Client.BaseURL.String() != "https://api.github.com/"; || 
svc.Owner != "owner" || svc.RepoName != "repo" {
+               t.Fatalf("unexpected service: %+v", svc)
+       }
+       for _, cloneURL := range []string{"", "file:///tmp/repo", 
"https://github.com/owner";, "https://github.com/owner/repo/extra"} {
+               if _, err := NewServiceForCloneURL(cloneURL, "test-token"); err 
== nil {
+                       t.Errorf("accepted invalid clone URL: %s", cloneURL)
+               }
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/libs/ci/gitlab/repository_file.go 
new/digger-cli-0.6.152/libs/ci/gitlab/repository_file.go
--- old/digger-cli-0.6.151/libs/ci/gitlab/repository_file.go    1970-01-01 
01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/ci/gitlab/repository_file.go    2026-09-30 
18:56:09.000000000 +0200
@@ -0,0 +1,18 @@
+package gitlab
+
+import (
+       "context"
+       "fmt"
+       "net/http"
+       "os"
+
+       gitlab "github.com/xanzy/go-gitlab"
+)
+
+func (svc GitLabService) ReadRepositoryFile(ctx context.Context, path, ref 
string) ([]byte, error) {
+       contents, response, err := 
svc.Client.RepositoryFiles.GetRawFile(*svc.Context.ProjectId, path, 
&gitlab.GetRawFileOptions{Ref: &ref}, gitlab.WithContext(ctx))
+       if response != nil && response.StatusCode == http.StatusNotFound {
+               return nil, fmt.Errorf("%s: %w", path, os.ErrNotExist)
+       }
+       return contents, err
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/libs/ci/gitlab/repository_file_test.go 
new/digger-cli-0.6.152/libs/ci/gitlab/repository_file_test.go
--- old/digger-cli-0.6.151/libs/ci/gitlab/repository_file_test.go       
1970-01-01 01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/ci/gitlab/repository_file_test.go       
2026-09-30 18:56:09.000000000 +0200
@@ -0,0 +1,45 @@
+package gitlab
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "net/http"
+       "net/http/httptest"
+       "os"
+       "testing"
+
+       gitlab "github.com/xanzy/go-gitlab"
+)
+
+func TestReadRepositoryFile(t *testing.T) {
+       for _, status := range []int{http.StatusOK, http.StatusNotFound, 
http.StatusForbidden} {
+               t.Run(fmt.Sprint(status), func(t *testing.T) {
+                       server := httptest.NewServer(http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
+                               if r.URL.Path != 
"/api/v4/projects/42/repository/files/digger.yml/raw" || 
r.URL.Query().Get("ref") != "feature/timeout" {
+                                       t.Errorf("unexpected request: %s", 
r.URL)
+                               }
+                               if r.Header.Get("PRIVATE-TOKEN") != 
"test-token" {
+                                       t.Error("missing authentication")
+                               }
+                               w.WriteHeader(status)
+                               fmt.Fprint(w, "git_timeout: 120\n")
+                       }))
+                       defer server.Close()
+                       client, err := gitlab.NewClient("test-token", 
gitlab.WithBaseURL(server.URL))
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       projectID := 42
+                       svc := GitLabService{Client: client, Context: 
&GitLabContext{ProjectId: &projectID}}
+                       data, err := 
svc.ReadRepositoryFile(context.Background(), "digger.yml", "feature/timeout")
+                       if status == http.StatusOK {
+                               if err != nil || string(data) != "git_timeout: 
120\n" {
+                                       t.Fatalf("got %q, %v", data, err)
+                               }
+                       } else if err == nil || errors.Is(err, os.ErrNotExist) 
!= (status == http.StatusNotFound) {
+                               t.Fatalf("unexpected error for HTTP %d: %v", 
status, err)
+                       }
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/libs/git_utils/clone_utils.go 
new/digger-cli-0.6.152/libs/git_utils/clone_utils.go
--- old/digger-cli-0.6.151/libs/git_utils/clone_utils.go        2026-09-13 
11:13:52.000000000 +0200
+++ new/digger-cli-0.6.152/libs/git_utils/clone_utils.go        2026-09-30 
18:56:09.000000000 +0200
@@ -29,25 +29,34 @@
        return CloneGitRepoAndDoActionWithTimeout(repoUrl, branch, commitHash, 
token, tokenUsername, defaultGitTimeout, action)
 }
 
-// CloneGitRepoAndDoActionWithTimeout is CloneGitRepoAndDoAction with a 
per-git-command timeout (digger.yml git_timeout, in seconds)
+// CloneGitRepoAndDoActionWithTimeout overrides the per-command timeout.
+// A non-positive timeout uses the default of 30 seconds.
 func CloneGitRepoAndDoActionWithTimeout(repoUrl string, branch string, 
commitHash string, token string, tokenUsername string, timeout time.Duration, 
action action) error {
        dir, err := createTempDir()
        if err != nil {
                slog.Error("Failed to create temporary directory", "error", err)
                return err
        }
+       defer func() {
+               slog.Debug("Removing cloned directory", "directory", dir)
+               if err := os.RemoveAll(dir); err != nil {
+                       slog.Warn("Failed to remove directory", "directory", 
dir, "error", err)
+               }
+       }()
+
+       git := NewGitShellWithTokenAuth(dir, token, tokenUsername)
+       if timeout > 0 {
+               git.timeout = timeout
+       }
 
        slog.Debug("Cloning git repository",
                "repoUrl", repoUrl,
                "branch", branch,
                "commitHash", commitHash,
                "directory", dir,
+               "timeout", git.timeout,
        )
 
-       git := NewGitShellWithTokenAuth(dir, token, tokenUsername)
-       if timeout > 0 {
-               git.timeout = timeout
-       }
        err = git.Clone(repoUrl, branch)
        if err != nil {
                slog.Error("Failed to clone repository",
@@ -69,14 +78,6 @@
                }
        }
 
-       defer func() {
-               slog.Debug("Removing cloned directory", "directory", dir)
-               ferr := os.RemoveAll(dir)
-               if ferr != nil {
-                       slog.Warn("Failed to remove directory", "directory", 
dir, "error", ferr)
-               }
-       }()
-
        err = action(dir)
        if err != nil {
                slog.Error("Error performing action on repository", 
"directory", dir, "error", err)
@@ -176,8 +177,11 @@
 
        err := cmd.Run()
        if err != nil {
+               if ctx.Err() != nil {
+                       return "", fmt.Errorf("git command timed out after %s: 
%w: %s", g.timeout, ctx.Err(), stderr.String())
+               }
                if stderr.Len() > 0 {
-                       return "", fmt.Errorf("git command failed: %v: %s", 
err, stderr.String())
+                       return "", fmt.Errorf("git command failed: %w: %s", 
err, stderr.String())
                }
                return "", err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/libs/git_utils/clone_utils_test.go 
new/digger-cli-0.6.152/libs/git_utils/clone_utils_test.go
--- old/digger-cli-0.6.151/libs/git_utils/clone_utils_test.go   1970-01-01 
01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/git_utils/clone_utils_test.go   2026-09-30 
18:56:09.000000000 +0200
@@ -0,0 +1,107 @@
+package git_utils
+
+import (
+       "context"
+       "errors"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "strings"
+       "testing"
+       "time"
+)
+
+func installFakeGit(t *testing.T, script string) {
+       t.Helper()
+       binDir := t.TempDir()
+       if err := os.WriteFile(filepath.Join(binDir, "git"), 
[]byte("#!/bin/sh\n"+script), 0700); err != nil {
+               t.Fatal(err)
+       }
+       t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
+}
+
+func TestCloneUsesConfigTimeoutForEachCommand(t *testing.T) {
+       // Each command fits within two seconds, but together they exceed it.
+       installFakeGit(t, "echo \"$1\" >> \"$GIT_TEST_COMMANDS\"\nexec sleep 
1.1\n")
+       commandsPath := filepath.Join(t.TempDir(), "commands")
+       t.Setenv("GIT_TEST_COMMANDS", commandsPath)
+       config := []byte("git_timeout: 2\n")
+       readFile := func(ctx context.Context, path, ref string) ([]byte, error) 
{
+               if path != "digger.yml" || ref != "commit" {
+                       t.Fatalf("unexpected config lookup: %s at %s", path, 
ref)
+               }
+               if _, err := os.Stat(commandsPath); !os.IsNotExist(err) {
+                       t.Fatal("Git started before reading configuration")
+               }
+               return config, nil
+       }
+       var clonedDir string
+       err := CloneGitRepoAndDoActionWithConfig("https://example.com/repo";, 
"main", "commit", "", "", readFile, func(dir string) error {
+               clonedDir = dir
+               return nil
+       })
+       if err != nil {
+               t.Fatal(err)
+       }
+       if clonedDir == "" {
+               t.Fatal("repository action was not called")
+       }
+       if _, err := os.Stat(clonedDir); !os.IsNotExist(err) {
+               t.Fatalf("clone directory was not removed: %v", err)
+       }
+       commands, err := os.ReadFile(commandsPath)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if string(commands) != "clone\ncheckout\n" {
+               t.Fatalf("unexpected commands: %q", commands)
+       }
+
+       // The same clone must fail with a shorter YAML timeout.
+       if err := os.Remove(commandsPath); err != nil {
+               t.Fatal(err)
+       }
+       config = []byte("git_timeout: 1\n")
+       err = CloneGitRepoAndDoActionWithConfig("https://example.com/repo";, 
"main", "commit", "", "", readFile, func(string) error {
+               t.Fatal("action called after timeout")
+               return nil
+       })
+       if !errors.Is(err, context.DeadlineExceeded) {
+               t.Fatalf("expected timeout, got %v", err)
+       }
+}
+
+func TestExplicitCloneTimeoutAndCleanup(t *testing.T) {
+       installFakeGit(t, "exec sleep 10\n")
+       cloneRoot := t.TempDir()
+       t.Setenv("TMPDIR", cloneRoot)
+       err := CloneGitRepoAndDoActionWithTimeout("https://example.com/repo";, 
"main", "", "", "", 20*time.Millisecond, func(string) error {
+               t.Fatal("action called after timeout")
+               return nil
+       })
+       if !errors.Is(err, context.DeadlineExceeded) {
+               t.Fatalf("expected timeout, got %v", err)
+       }
+       if !strings.Contains(err.Error(), "timed out after 20ms") {
+               t.Fatalf("timeout duration missing: %v", err)
+       }
+       entries, err := os.ReadDir(cloneRoot)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(entries) != 0 {
+               t.Fatalf("failed clone left temporary files: %v", entries)
+       }
+}
+
+func TestGitCommandFailureIsNotTimeout(t *testing.T) {
+       installFakeGit(t, "echo 'clone failed' >&2\nexit 1\n")
+       _, err := NewGitShell(t.TempDir(), nil).runCommand("clone")
+       var exitErr *exec.ExitError
+       if !errors.As(err, &exitErr) || errors.Is(err, 
context.DeadlineExceeded) {
+               t.Fatalf("expected process failure, got %v", err)
+       }
+       if !strings.Contains(err.Error(), "clone failed") {
+               t.Fatalf("command stderr missing: %v", err)
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/digger-cli-0.6.151/libs/git_utils/config_timeout.go 
new/digger-cli-0.6.152/libs/git_utils/config_timeout.go
--- old/digger-cli-0.6.151/libs/git_utils/config_timeout.go     1970-01-01 
01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/git_utils/config_timeout.go     2026-09-30 
18:56:09.000000000 +0200
@@ -0,0 +1,57 @@
+package git_utils
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "os"
+       "time"
+
+       "gopkg.in/yaml.v3"
+)
+
+// RepositoryFileReader reads a file at a branch or commit through the 
provider API.
+// Missing files must return an error wrapping os.ErrNotExist.
+type RepositoryFileReader func(ctx context.Context, path, ref string) ([]byte, 
error)
+
+// CloneGitRepoAndDoActionWithConfig reads git_timeout before starting the 
clone.
+func CloneGitRepoAndDoActionWithConfig(repoURL, branch, commitHash, token, 
tokenUsername string, readFile RepositoryFileReader, action action) error {
+       ref := branch
+       if commitHash != "" {
+               ref = commitHash
+       }
+       timeout, err := readGitTimeout(readFile, ref)
+       if err != nil {
+               return err
+       }
+       return CloneGitRepoAndDoActionWithTimeout(repoURL, branch, commitHash, 
token, tokenUsername, timeout, action)
+}
+
+func readGitTimeout(readFile RepositoryFileReader, ref string) (time.Duration, 
error) {
+       ctx, cancel := context.WithTimeout(context.Background(), 
defaultGitTimeout)
+       defer cancel()
+       for _, path := range []string{"digger.yml", "digger.yaml"} {
+               contents, err := readFile(ctx, path, ref)
+               if errors.Is(err, os.ErrNotExist) {
+                       continue
+               }
+               if err != nil {
+                       return 0, fmt.Errorf("read %s before cloning: %w", 
path, err)
+               }
+               // Project generation needs a checkout; only decode the timeout 
here.
+               var config struct {
+                       GitTimeout int64 `yaml:"git_timeout"`
+               }
+               if err := yaml.Unmarshal(contents, &config); err != nil {
+                       return 0, fmt.Errorf("parse %s before cloning: %w", 
path, err)
+               }
+               if config.GitTimeout <= 0 {
+                       return defaultGitTimeout, nil
+               }
+               if config.GitTimeout > int64((1<<63-1)/time.Second) {
+                       return 0, fmt.Errorf("git_timeout in %s is too large", 
path)
+               }
+               return time.Duration(config.GitTimeout) * time.Second, nil
+       }
+       return defaultGitTimeout, nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/digger-cli-0.6.151/libs/git_utils/config_timeout_test.go 
new/digger-cli-0.6.152/libs/git_utils/config_timeout_test.go
--- old/digger-cli-0.6.151/libs/git_utils/config_timeout_test.go        
1970-01-01 01:00:00.000000000 +0100
+++ new/digger-cli-0.6.152/libs/git_utils/config_timeout_test.go        
2026-09-30 18:56:09.000000000 +0200
@@ -0,0 +1,77 @@
+package git_utils
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "os"
+       "reflect"
+       "testing"
+       "time"
+)
+
+func TestReadGitTimeout(t *testing.T) {
+       for _, test := range []struct {
+               name    string
+               files   map[string]string
+               want    time.Duration
+               paths   []string
+               wantErr bool
+       }{
+               {"configured", map[string]string{"digger.yml": "git_timeout: 
120\nprojects: []"}, 120 * time.Second, []string{"digger.yml"}, false},
+               {"yaml fallback", map[string]string{"digger.yaml": 
"git_timeout: 90"}, 90 * time.Second, []string{"digger.yml", "digger.yaml"}, 
false},
+               {"yml precedence", map[string]string{"digger.yml": 
"git_timeout: 60", "digger.yaml": "git_timeout: 90"}, time.Minute, 
[]string{"digger.yml"}, false},
+               {"missing files", nil, 30 * time.Second, []string{"digger.yml", 
"digger.yaml"}, false},
+               {"omitted", map[string]string{"digger.yml": "projects: []"}, 30 
* time.Second, []string{"digger.yml"}, false},
+               {"zero", map[string]string{"digger.yml": "git_timeout: 0"}, 30 
* time.Second, []string{"digger.yml"}, false},
+               {"negative", map[string]string{"digger.yml": "git_timeout: 
-1"}, 30 * time.Second, []string{"digger.yml"}, false},
+               {"invalid type", map[string]string{"digger.yml": "git_timeout: 
slow"}, 0, []string{"digger.yml"}, true},
+               {"overflow", map[string]string{"digger.yml": "git_timeout: 
9223372037"}, 0, []string{"digger.yml"}, true},
+               {"malformed", map[string]string{"digger.yml": "git_timeout: 
["}, 0, []string{"digger.yml"}, true},
+       } {
+               t.Run(test.name, func(t *testing.T) {
+                       var paths []string
+                       got, err := readGitTimeout(func(ctx context.Context, 
path, ref string) ([]byte, error) {
+                               paths = append(paths, path)
+                               if ref != "feature/timeout" {
+                                       t.Fatalf("unexpected ref: %s", ref)
+                               }
+                               if _, ok := ctx.Deadline(); !ok {
+                                       t.Fatal("API lookup has no deadline")
+                               }
+                               if data, ok := test.files[path]; ok {
+                                       return []byte(data), nil
+                               }
+                               return nil, fmt.Errorf("missing %s: %w", path, 
os.ErrNotExist)
+                       }, "feature/timeout")
+                       if (err != nil) != test.wantErr || got != test.want {
+                               t.Fatalf("got %s, %v; want %s, error=%v", got, 
err, test.want, test.wantErr)
+                       }
+                       if !reflect.DeepEqual(paths, test.paths) {
+                               t.Fatalf("read %v; want %v", paths, test.paths)
+                       }
+               })
+       }
+}
+
+func TestConfigLookupFailurePreventsClone(t *testing.T) {
+       commandsPath := t.TempDir() + "/commands"
+       t.Setenv("GIT_TEST_COMMANDS", commandsPath)
+       installFakeGit(t, "echo clone > \"$GIT_TEST_COMMANDS\"\n")
+       apiErr := errors.New("API unavailable")
+       err := CloneGitRepoAndDoActionWithConfig("https://example.com/repo";, 
"main", "", "", "", func(ctx context.Context, path, ref string) ([]byte, error) 
{
+               if ref != "main" {
+                       t.Fatalf("unexpected ref: %s", ref)
+               }
+               return nil, apiErr
+       }, func(string) error {
+               t.Fatal("action called after configuration lookup failed")
+               return nil
+       })
+       if !errors.Is(err, apiErr) {
+               t.Fatalf("expected API error, got %v", err)
+       }
+       if _, err := os.Stat(commandsPath); !os.IsNotExist(err) {
+               t.Fatal("clone started after configuration lookup failed")
+       }
+}

++++++ digger-cli.obsinfo ++++++
--- /var/tmp/diff_new_pack.8BjW3e/_old  2026-10-01 16:49:40.346644314 +0200
+++ /var/tmp/diff_new_pack.8BjW3e/_new  2026-10-01 16:49:40.351644524 +0200
@@ -1,5 +1,5 @@
 name: digger-cli
-version: 0.6.151
-mtime: 1789290832
-commit: 85cd59db7caa3d8bf6cefe303a62c12812b0bc48
+version: 0.6.152
+mtime: 1790787369
+commit: 9ac4e2237b7b8afa455f422b92737bfe106d495c
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/digger-cli/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.digger-cli.new.1253/vendor.tar.gz differ: char 32, 
line 2

Reply via email to