Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package forgejo-runner for openSUSE:Factory checked in at 2026-03-26 21:09:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/forgejo-runner (Old) and /work/SRC/openSUSE:Factory/.forgejo-runner.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "forgejo-runner" Thu Mar 26 21:09:45 2026 rev:42 rq:1342725 version:12.7.3 Changes: -------- --- /work/SRC/openSUSE:Factory/forgejo-runner/forgejo-runner.changes 2026-03-17 19:07:32.472239766 +0100 +++ /work/SRC/openSUSE:Factory/.forgejo-runner.new.8177/forgejo-runner.changes 2026-03-27 06:39:46.741919992 +0100 @@ -1,0 +2,7 @@ +Thu Mar 26 08:18:42 UTC 2026 - Richard Rahl <[email protected]> + +- Update to version 12.7.3: + * feat: set a custom user-agent header for all gRPC requests + * fix: replace logger.Panicf with panics that have a useful error message + +------------------------------------------------------------------- Old: ---- _servicedata forgejo-runner-12.7.2.obscpio New: ---- forgejo-runner-12.7.3.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ forgejo-runner.spec ++++++ --- /var/tmp/diff_new_pack.L6iB21/_old 2026-03-27 06:39:47.597954920 +0100 +++ /var/tmp/diff_new_pack.L6iB21/_new 2026-03-27 06:39:47.601955083 +0100 @@ -18,7 +18,7 @@ %define services %{name}.service Name: forgejo-runner -Version: 12.7.2 +Version: 12.7.3 Release: 0 Summary: Daemon that connects to a Forgejo instance and runs CI jobs License: GPL-3.0-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.L6iB21/_old 2026-03-27 06:39:47.641956715 +0100 +++ /var/tmp/diff_new_pack.L6iB21/_new 2026-03-27 06:39:47.649957042 +0100 @@ -2,7 +2,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://code.forgejo.org/forgejo/runner</param> <param name="scm">git</param> - <param name="revision">refs/tags/v12.7.2</param> + <param name="revision">refs/tags/v12.7.3</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">disable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ forgejo-runner-12.7.2.obscpio -> forgejo-runner-12.7.3.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/.forgejo/workflows/build-release-integration.yml new/forgejo-runner-12.7.3/.forgejo/workflows/build-release-integration.yml --- old/forgejo-runner-12.7.2/.forgejo/workflows/build-release-integration.yml 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/.forgejo/workflows/build-release-integration.yml 2026-03-24 15:02:59.000000000 +0100 @@ -19,7 +19,7 @@ enable-email-notifications: true env: - FORGEJO_VERSION: 11.0.10 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo + FORGEJO_VERSION: 11.0.11 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo jobs: release-simulation: @@ -29,7 +29,7 @@ - uses: https://data.forgejo.org/actions/checkout@v4 - id: forgejo - uses: https://data.forgejo.org/actions/[email protected] + uses: https://data.forgejo.org/actions/[email protected] with: user: root password: admin1234 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/.forgejo/workflows/docker-build-push-action-in-lxc.yml new/forgejo-runner-12.7.3/.forgejo/workflows/docker-build-push-action-in-lxc.yml --- old/forgejo-runner-12.7.2/.forgejo/workflows/docker-build-push-action-in-lxc.yml 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/.forgejo/workflows/docker-build-push-action-in-lxc.yml 2026-03-24 15:02:59.000000000 +0100 @@ -21,7 +21,7 @@ enable-email-notifications: true env: - FORGEJO_VERSION: 11.0.10 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo + FORGEJO_VERSION: 11.0.11 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo FORGEJO_USER: root FORGEJO_PASSWORD: admin1234 @@ -34,7 +34,7 @@ - name: install Forgejo so it can be used as a container registry id: registry - uses: https://data.forgejo.org/actions/[email protected] + uses: https://data.forgejo.org/actions/[email protected] with: user: ${{ env.FORGEJO_USER }} password: ${{ env.FORGEJO_PASSWORD }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/.forgejo/workflows/example-lxc-systemd.yml new/forgejo-runner-12.7.3/.forgejo/workflows/example-lxc-systemd.yml --- old/forgejo-runner-12.7.2/.forgejo/workflows/example-lxc-systemd.yml 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/.forgejo/workflows/example-lxc-systemd.yml 2026-03-24 15:02:59.000000000 +0100 @@ -14,7 +14,7 @@ SERIAL: "30" LIFETIME: "60" SYSTEMD_OPTIONS: "--no-pager --full" - USE_VERSION: 11.0.10 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo + USE_VERSION: 11.0.11 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo jobs: example-lxc-systemd: @@ -54,7 +54,7 @@ done - id: forgejo - uses: https://data.forgejo.org/actions/[email protected] + uses: https://data.forgejo.org/actions/[email protected] with: user: root password: admin1234 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/.forgejo/workflows/example-on-demand.yml new/forgejo-runner-12.7.3/.forgejo/workflows/example-on-demand.yml --- old/forgejo-runner-12.7.2/.forgejo/workflows/example-on-demand.yml 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/.forgejo/workflows/example-on-demand.yml 2026-03-24 15:02:59.000000000 +0100 @@ -29,7 +29,7 @@ - uses: https://data.forgejo.org/actions/checkout@v5 - - uses: https://data.forgejo.org/actions/[email protected] + - uses: https://data.forgejo.org/actions/[email protected] with: user: "${{ env.username }}" password: "${{ env.password }}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/Makefile new/forgejo-runner-12.7.3/Makefile --- old/forgejo-runner-12.7.2/Makefile 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/Makefile 2026-03-24 15:02:59.000000000 +0100 @@ -14,7 +14,7 @@ GOFILES := $(shell find . -type f -name "*.go" -o -name "go.mod" ! -name "generated.*") MOCKERY_PACKAGE ?= github.com/vektra/mockery/[email protected] # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/[email protected] # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/[email protected] # renovate: datasource=go DOCKER_IMAGE ?= gitea/act_runner DOCKER_TAG ?= nightly diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/act/artifactcache/handler.go new/forgejo-runner-12.7.3/act/artifactcache/handler.go --- old/forgejo-runner-12.7.2/act/artifactcache/handler.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/act/artifactcache/handler.go 2026-03-24 15:02:59.000000000 +0100 @@ -25,7 +25,7 @@ var fatal = func(logger logrus.FieldLogger, err error) { logger.Errorf("unrecoverable error in the cache: %v", err) if err := common.Suicide(common.CacheUnrecoverableError); err != nil { - logger.Panicf("unrecoverable error in the cache: failed to send the TERM signal to shutdown the daemon %v", err) + panic(fmt.Sprintf("unrecoverable error in the cache: failed to send the TERM signal to shutdown the daemon %v", err)) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/act/common/git/git.go new/forgejo-runner-12.7.3/act/common/git/git.go --- old/forgejo-runner-12.7.2/act/common/git/git.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/act/common/git/git.go 2026-03-24 15:02:59.000000000 +0100 @@ -2,6 +2,7 @@ import ( "context" + "encoding/base64" "errors" "fmt" "net/url" @@ -9,6 +10,7 @@ "os/exec" "path/filepath" "regexp" + "runtime" "strings" log "github.com/sirupsen/logrus" @@ -442,41 +444,16 @@ return "", fmt.Errorf("failed to parse remote URL %q to use git token: %w", options.remoteURL, err) } - credentialsFile, err := os.CreateTemp("", "forgejo-runner-git-token-") - if err != nil { - return "", fmt.Errorf("failed to create temporary file to store Git token: %w", err) - } - - credentialsPath := credentialsFile.Name() - defer func() { - _ = credentialsFile.Close() - if err := os.Remove(credentialsPath); err != nil { - log.Warnf("Unable to remove temporary file to store Git token %s: %v", credentialsPath, err) - } - }() - - // Git credential store expects an URL with embedded credentials. - // Use a fixed username to keep behaviour consistent across providers. - remoteURL.User = url.UserPassword("x-access-token", options.token) - if remoteURL.Path == "" { - remoteURL.Path = "/" - } - - _, err = credentialsFile.Write([]byte(remoteURL.String() + "\n")) - if err != nil { - return "", fmt.Errorf("failed to write Git token to temporary file %s: %w", credentialsPath, err) - } - err = credentialsFile.Close() - if err != nil { - return "", fmt.Errorf("failed to close temporary file %s that stores Git token: %w", credentialsPath, err) - } - - gitArguments = append(gitArguments, "-c", fmt.Sprintf("credential.helper=store --file=%s", credentialsPath)) - gitArguments = append(gitArguments, "-c", "credential.useHttpPath=true") + const envVarName = "GIT_AUTH_HEADER" + scopedExtraHeader := fmt.Sprintf("http.%s://%s/.extraHeader", remoteURL.Scheme, remoteURL.Host) + gitArguments = append(gitArguments, "--config-env", fmt.Sprintf("%s=%s", scopedExtraHeader, envVarName)) } if options.ignoreInvalidCertificates { gitArguments = append(gitArguments, "-c", "http.sslVerify=false") } + if runtime.GOOS == "windows" { + gitArguments = append(gitArguments, "-c", "core.longpaths=true") + } if options.workingDirectory != "" { gitArguments = append(gitArguments, "-C", options.workingDirectory) } @@ -487,6 +464,13 @@ logger.Debugf(" git %s", strings.Join(gitArguments, " ")) cmd := exec.CommandContext(ctx, "git", gitArguments...) + + if options.token != "" { + auth := "x-access-token:" + options.token + authHeader := "Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + cmd.Env = append(os.Environ(), "GIT_AUTH_HEADER="+authHeader) + } + output, err := cmd.Output() trimmedOutput := strings.TrimSpace(string(output)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/act/common/git/git_test.go new/forgejo-runner-12.7.3/act/common/git/git_test.go --- old/forgejo-runner-12.7.2/act/common/git/git_test.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/act/common/git/git_test.go 2026-03-24 15:02:59.000000000 +0100 @@ -747,7 +747,6 @@ })) defer srv.Close() - // Any path is fine; useHttpPath=true makes git include the path in credential lookup. url := srv.URL + "/org/repo" _, err := Clone(t.Context(), CloneInput{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/act/container/docker_run.go new/forgejo-runner-12.7.3/act/container/docker_run.go --- old/forgejo-runner-12.7.2/act/container/docker_run.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/act/container/docker_run.go 2026-03-24 15:02:59.000000000 +0100 @@ -103,16 +103,13 @@ // supportsContainerImagePlatform returns true if the underlying Docker server // API version is 1.41 and beyond func supportsContainerImagePlatform(ctx context.Context, cli client.APIClient) bool { - logger := common.Logger(ctx) ver, err := cli.ServerVersion(ctx) if err != nil { - logger.Panicf("Failed to get Docker API Version: %s", err) - return false + panic(fmt.Sprintf("Failed to get Docker API Version: %s", err)) } sv, err := semver.NewVersion(ver.APIVersion) if err != nil { - logger.Panicf("Failed to unmarshal Docker Version: %s", err) - return false + panic(fmt.Sprintf("Failed to unmarshal Docker Version: %s", err)) } constraint, _ := semver.NewConstraint(">= 1.41") return constraint.Check(sv) @@ -121,16 +118,13 @@ // supportsImageInspectPlatform returns true if the underlying Docker server supports using // `client.ImageInspectWithPlatform`, which is API version 1.49 and beyond. func supportsImageInspectPlatform(ctx context.Context, cli client.APIClient) bool { - logger := common.Logger(ctx) ver, err := cli.ServerVersion(ctx) if err != nil { - logger.Panicf("Failed to get Docker API Version: %s", err) - return false + panic(fmt.Sprintf("Failed to get Docker API Version: %s", err)) } sv, err := semver.NewVersion(ver.APIVersion) if err != nil { - logger.Panicf("Failed to unmarshal Docker Version: %s", err) - return false + panic(fmt.Sprintf("Failed to unmarshal Docker Version: %s", err)) } constraint, _ := semver.NewConstraint(">= 1.49") return constraint.Check(sv) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/examples/docker-compose/compose-forgejo-and-runner.yml new/forgejo-runner-12.7.3/examples/docker-compose/compose-forgejo-and-runner.yml --- old/forgejo-runner-12.7.2/examples/docker-compose/compose-forgejo-and-runner.yml 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/examples/docker-compose/compose-forgejo-and-runner.yml 2026-03-24 15:02:59.000000000 +0100 @@ -51,7 +51,7 @@ - 8080:3000 runner-register: - image: data.forgejo.org/forgejo/runner:12.7.1 + image: data.forgejo.org/forgejo/runner:12.7.2 links: - docker-in-docker - forgejo @@ -77,7 +77,7 @@ ' runner-daemon: - image: data.forgejo.org/forgejo/runner:12.7.1 + image: data.forgejo.org/forgejo/runner:12.7.2 links: - docker-in-docker - forgejo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/examples/lxc-systemd/forgejo-runner-service.sh new/forgejo-runner-12.7.3/examples/lxc-systemd/forgejo-runner-service.sh --- old/forgejo-runner-12.7.2/examples/lxc-systemd/forgejo-runner-service.sh 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/examples/lxc-systemd/forgejo-runner-service.sh 2026-03-24 15:02:59.000000000 +0100 @@ -22,7 +22,7 @@ : ${INPUTS_LIFETIME:=7d} DEFAULT_LXC_HELPERS_VERSION=1.1.3 # renovate: datasource=forgejo-tags depName=forgejo/lxc-helpers : ${INPUTS_LXC_HELPERS_VERSION:=$DEFAULT_LXC_HELPERS_VERSION} -DEFAULT_RUNNER_VERSION=12.7.1 # renovate: datasource=forgejo-releases depName=forgejo/runner +DEFAULT_RUNNER_VERSION=12.7.2 # renovate: datasource=forgejo-releases depName=forgejo/runner : ${INPUTS_RUNNER_VERSION:=$DEFAULT_RUNNER_VERSION} : ${KILL_AFTER:=21600} # 6h == 21600 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/go.mod new/forgejo-runner-12.7.3/go.mod --- old/forgejo-runner-12.7.2/go.mod 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/go.mod 2026-03-24 15:02:59.000000000 +0100 @@ -31,6 +31,7 @@ github.com/moby/patternmatcher v0.6.0 github.com/opencontainers/image-spec v1.1.1 github.com/opencontainers/selinux v1.13.0 + github.com/powerman/fileuri v0.2.0 github.com/rhysd/actionlint v1.7.10 github.com/sirupsen/logrus v1.9.4 github.com/spf13/cobra v1.10.2 @@ -40,7 +41,7 @@ go.etcd.io/bbolt v1.4.3 go.yaml.in/yaml/v3 v3.0.4 golang.org/x/sys v0.42.0 - golang.org/x/term v0.40.0 + golang.org/x/term v0.41.0 golang.org/x/time v0.15.0 google.golang.org/protobuf v1.36.11 gotest.tools/v3 v3.5.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/go.sum new/forgejo-runner-12.7.3/go.sum --- old/forgejo-runner-12.7.2/go.sum 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/go.sum 2026-03-24 15:02:59.000000000 +0100 @@ -160,6 +160,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/powerman/fileuri v0.2.0 h1:+CsMkkrY8HtSXUpoZNp2F53R20hA7caiBjpU2jYnY1g= +github.com/powerman/fileuri v0.2.0/go.mod h1:nFHerdvcP+wa5FNgLCbXpG4wdZfHSswr2O4dpggGFTs= github.com/rhysd/actionlint v1.7.10 h1:FL3XIEs72G4/++168vlv5FKOWMSWvWIQw1kBCadyOcM= github.com/rhysd/actionlint v1.7.10/go.mod h1:ZHX/hrmknlsJN73InPTKsKdXpAv9wVdrJy8h8HAwFHg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -255,8 +257,8 @@ golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/app/cmd/cmd.go new/forgejo-runner-12.7.3/internal/app/cmd/cmd.go --- old/forgejo-runner-12.7.2/internal/app/cmd/cmd.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/app/cmd/cmd.go 2026-03-24 15:02:59.000000000 +0100 @@ -55,7 +55,9 @@ Use: "one-job", Short: "Run only one job", Args: cobra.MaximumNArgs(1), - RunE: runJob(ctx, &configFile, &runJobArgs), + RunE: func(cmd *cobra.Command, args []string) error { + return runJob(ctx, &configFile, &runJobArgs) + }, } jobCmd.Flags().BoolVarP(&runJobArgs.wait, "wait", "w", false, "waits until task has been assigned") rootCmd.AddCommand(jobCmd) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/app/cmd/job.go new/forgejo-runner-12.7.3/internal/app/cmd/job.go --- old/forgejo-runner-12.7.2/internal/app/cmd/job.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/app/cmd/job.go 2026-03-24 15:02:59.000000000 +0100 @@ -6,107 +6,75 @@ import ( "context" "fmt" - "os" - "strings" log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" "code.forgejo.org/forgejo/runner/v12/act/cacheproxy" "code.forgejo.org/forgejo/runner/v12/internal/app/job" "code.forgejo.org/forgejo/runner/v12/internal/app/run" - "code.forgejo.org/forgejo/runner/v12/internal/pkg/client" "code.forgejo.org/forgejo/runner/v12/internal/pkg/config" - "code.forgejo.org/forgejo/runner/v12/internal/pkg/envcheck" - "code.forgejo.org/forgejo/runner/v12/internal/pkg/ver" ) type runJobArgs struct { wait bool } -func runJob(ctx context.Context, configFile *string, runJobArgs *runJobArgs) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - cfg, err := config.New( - config.FromFile(*configFile), - config.FromRegistration, - ) - if err != nil { - return fmt.Errorf("invalid configuration: %w", err) - } else if len(cfg.Server.Connections) != 1 { - return fmt.Errorf("one-job is only supported with a single connection, but %d connections are configured", len(cfg.Server.Connections)) - } - - var connName string - var conn *config.Connection - for name, c := range cfg.Server.Connections { - connName = name - conn = c - } - - initLogging(cfg) - log.Infoln("Starting job") - - requireDocker := false - for _, conn := range cfg.Server.Connections { - if conn.Labels.RequireDocker() { - requireDocker = true - break - } - } - if requireDocker { - dockerSocketPath, err := getDockerSocketPath(cfg.Container.DockerHost) - if err != nil { - return err - } - if err := envcheck.CheckIfDockerRunning(ctx, dockerSocketPath); err != nil { - return err - } - // if dockerSocketPath passes the check, override DOCKER_HOST with dockerSocketPath - os.Setenv("DOCKER_HOST", dockerSocketPath) - // cfg.Container.DockerHost set to "automount" means act_runner need to find an available docker host automatically - // and assign the path to cfg.Container.DockerHost - if cfg.Container.DockerHost == "automount" { - cfg.Container.DockerHost = dockerSocketPath - } - // check the scheme, if the scheme is not npipe or unix - // set cfg.Container.DockerHost to "-" because it can't be mounted to the job container - if protoIndex := strings.Index(cfg.Container.DockerHost, "://"); protoIndex != -1 { - scheme := cfg.Container.DockerHost[:protoIndex] - if !strings.EqualFold(scheme, "npipe") && !strings.EqualFold(scheme, "unix") { - cfg.Container.DockerHost = "-" - } - } - } +var initializeRunJobConfig = func(configFile *string) (*config.Config, error) { + cfg, err := config.New( + config.FromFile(*configFile), + config.FromRegistration, + ) + if err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + return cfg, nil +} - cli := client.New( - conn.URL.String(), - cfg.Runner.Insecure, - conn.UUID.String(), - conn.Token, - ver.Version(), - conn.FetchInterval, - ) - - var cacheProxy *cacheproxy.Handler - if cfg.Cache.Enabled { - cacheProxy = run.SetupCache(cfg) - defer func() { - if cacheProxy != nil { - err := cacheProxy.Close() - if err != nil { - log.WithError(err).Error("failed to close cache") - } - } - }() - } +func runJob(ctx context.Context, configFile *string, runJobArgs *runJobArgs) error { + cfg, err := initializeRunJobConfig(configFile) + if err != nil { + return err + } else if len(cfg.Server.Connections) != 1 { + return fmt.Errorf("one-job is only supported with a single connection, but %d connections are configured", len(cfg.Server.Connections)) + } - runner, _, _, err := createRunner(ctx, connName, cfg, cli, conn.Labels, cacheProxy) - if err != nil { - return err - } + var connName string + var conn *config.Connection + for name, c := range cfg.Server.Connections { + connName = name + conn = c - j := job.NewJob(cfg, cli, runner) - return j.Run(ctx, runJobArgs.wait) + // We always take the first (and only) connection. + break } + + initLogging(cfg) + log.Infoln("Starting job") + + err = configCheck(ctx, cfg) + if err != nil { + return err + } + + var cacheProxy *cacheproxy.Handler + if cfg.Cache.Enabled { + cacheProxy = run.SetupCache(cfg) + defer func() { + if cacheProxy != nil { + err := cacheProxy.Close() + if err != nil { + log.WithError(err).Error("failed to close cache") + } + } + }() + } + + client := createClient(cfg, conn) + runner, _, _, err := createRunner(ctx, connName, cfg, client, conn.Labels, cacheProxy) + if err != nil { + return err + } + + j := job.NewJob(cfg, client, runner) + return j.Run(ctx, runJobArgs.wait) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/app/cmd/job_test.go new/forgejo-runner-12.7.3/internal/app/cmd/job_test.go --- old/forgejo-runner-12.7.2/internal/app/cmd/job_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/app/cmd/job_test.go 2026-03-24 15:02:59.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright 2026 The Forgejo Authors +// SPDX-License-Identifier: GPL-3.0-or-later + +package cmd + +import ( + "context" + "os" + "path/filepath" + "testing" + + runnerv1 "code.forgejo.org/forgejo/actions-proto/runner/v1" + "code.forgejo.org/forgejo/runner/v12/act/cacheproxy" + "code.forgejo.org/forgejo/runner/v12/internal/app/run" + mock_runner "code.forgejo.org/forgejo/runner/v12/internal/app/run/mocks" + "code.forgejo.org/forgejo/runner/v12/internal/pkg/client" + mock_client "code.forgejo.org/forgejo/runner/v12/internal/pkg/client/mocks" + "code.forgejo.org/forgejo/runner/v12/internal/pkg/config" + "code.forgejo.org/forgejo/runner/v12/internal/pkg/labels" + "code.forgejo.org/forgejo/runner/v12/testutils" + "connectrpc.com/connect" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestRunJob(t *testing.T) { + rawConfig := ` +cache: + enabled: false +server: + connections: + example: + url: https://example.com/forgejo + uuid: 41414141-4141-4141-4141-414141414141 + token: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +` + + tempDir := t.TempDir() + configPath := filepath.Join(tempDir, "config.yaml") + + err := os.WriteFile(configPath, []byte(rawConfig), 0o644) + require.NoError(t, err) + + mockClient := mock_client.NewClient(t) + mockClient. + On("FetchTask", mock.Anything, connect.NewRequest(&runnerv1.FetchTaskRequest{})). + Return(connect.NewResponse(&runnerv1.FetchTaskResponse{Task: &runnerv1.Task{}, TasksVersion: int64(1)}), nil) + + mockRunner := mock_runner.NewRunnerInterface(t) + mockRunner. + On("Run", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) {}) + + defer testutils.MockVariable(&initLogging, func(cfg *config.Config) {})() + defer testutils.MockVariable(&createClient, func(cfg *config.Config, conn *config.Connection) client.Client { + assert.Equal(t, "https://example.com/forgejo", conn.URL.String()) + assert.Equal(t, "41414141-4141-4141-4141-414141414141", conn.UUID.String()) + assert.Equal(t, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", conn.Token) + + return mockClient + })() + defer testutils.MockVariable(&createRunner, func(ctx context.Context, name string, cfg *config.Config, cli client.Client, ls labels.Labels, cacheProxy *cacheproxy.Handler) (run.RunnerInterface, string, bool, error) { + if name == "example" { + return mockRunner, "example", false, nil + } + t.Fatalf("unexpected connection name: %q", name) + return nil, "", false, nil + })() + + mockSignalContext, cancelSignal := context.WithCancel(t.Context()) + runJobCompleted := make(chan any) + go func() { + err := runJob(mockSignalContext, &configPath, &runJobArgs{}) + require.NoError(t, err) + + // Signal that runJob() has completed. + close(runJobCompleted) + }() + + mockRunner.On("Run", mock.Anything, mock.Anything) + + // Cancel the goroutine that runs runJob(). + cancelSignal() + + // Wait for the goroutine that executes runJob() to stop. + <-runJobCompleted +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/pkg/client/header.go new/forgejo-runner-12.7.3/internal/pkg/client/header.go --- old/forgejo-runner-12.7.2/internal/pkg/client/header.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/pkg/client/header.go 2026-03-24 15:02:59.000000000 +0100 @@ -7,4 +7,5 @@ UUIDHeader = "x-runner-uuid" TokenHeader = "x-runner-token" RequestKeyHeader = "x-runner-request-key" + UserAgentHeader = "user-agent" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/pkg/client/http.go new/forgejo-runner-12.7.3/internal/pkg/client/http.go --- old/forgejo-runner-12.7.2/internal/pkg/client/http.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/pkg/client/http.go 2026-03-24 15:02:59.000000000 +0100 @@ -6,6 +6,7 @@ import ( "context" "crypto/tls" + "fmt" "net/http" "os" "strconv" @@ -73,6 +74,7 @@ if client.requestKey != nil { req.Header().Set(RequestKeyHeader, client.requestKey.String()) } + req.Header().Set(UserAgentHeader, fmt.Sprintf("forgejo-runner/%s", version)) return next(ctx, req) } }))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/pkg/config/config.go new/forgejo-runner-12.7.3/internal/pkg/config/config.go --- old/forgejo-runner-12.7.2/internal/pkg/config/config.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/pkg/config/config.go 2026-03-24 15:02:59.000000000 +0100 @@ -16,6 +16,7 @@ "code.forgejo.org/forgejo/runner/v12/internal/pkg/labels" gouuid "github.com/google/uuid" "github.com/joho/godotenv" + "github.com/powerman/fileuri" log "github.com/sirupsen/logrus" "go.yaml.in/yaml/v3" ) @@ -216,7 +217,7 @@ if config.Runner.Envs == nil { config.Runner.Envs = make(map[string]string, len(s.Envs)) } - env, err := readEnvFile(s.EnvFile) + env, err := readEnvFile(filepath.FromSlash(s.EnvFile)) if err != nil { return err } @@ -403,7 +404,7 @@ func (s *serializedHostSettings) applyTo(config *Config) error { if s.WorkdirParent != "" { - config.Host.WorkdirParent = s.WorkdirParent + config.Host.WorkdirParent = filepath.FromSlash(s.WorkdirParent) } return nil @@ -624,9 +625,11 @@ hostname := fileURL.Hostname() if hostname != "" { log.Warnf("Ignoring hostname %q in secret: %q", hostname, input) + fileURL.Host = "" } - value, err := os.ReadFile(fileURL.Path) + filePath, _ := fileuri.ToFilePath(fileURL) + value, err := os.ReadFile(filePath) if err != nil { return "", fmt.Errorf("cannot read secret %q: %w", input, err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.7.2/internal/pkg/config/config_test.go new/forgejo-runner-12.7.3/internal/pkg/config/config_test.go --- old/forgejo-runner-12.7.2/internal/pkg/config/config_test.go 2026-03-09 17:04:00.000000000 +0100 +++ new/forgejo-runner-12.7.3/internal/pkg/config/config_test.go 2026-03-24 15:02:59.000000000 +0100 @@ -9,14 +9,17 @@ "os" "path/filepath" "reflect" + "runtime" "strings" "testing" "time" "code.forgejo.org/forgejo/runner/v12/internal/pkg/labels" gouuid "github.com/google/uuid" + "github.com/powerman/fileuri" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gotest.tools/v3/skip" ) func TestNew(t *testing.T) { @@ -24,7 +27,7 @@ config, err := New(FromFile("does-not-exist")) assert.Nil(t, config) - assert.ErrorContains(t, err, "open does-not-exist: no such file or directory") + assert.ErrorContains(t, err, "open does-not-exist:") }) t.Run("Malformed configuration file results in error", func(t *testing.T) { @@ -38,7 +41,7 @@ config, err := New(FromFile(configPath)) assert.Nil(t, config) - assert.ErrorContains(t, err, fmt.Sprintf(`cannot parse config file "%s"`, configPath)) + assert.ErrorContains(t, err, fmt.Sprintf(`cannot parse config file %q`, configPath)) }) t.Run("Without configuration file", func(t *testing.T) { @@ -259,7 +262,7 @@ external_server: https://external.example.com/ actions_cache_url_override: https://override.example.com/ secret: vruvRdu5Rm - secret_url: + secret_url: container: network: host network_mode: bridge @@ -377,7 +380,7 @@ assert.True(t, config.Container.ForceRebuild) assert.NotEqual(t, defaultConfig.Host.WorkdirParent, config.Host.WorkdirParent) - assert.True(t, strings.HasSuffix(config.Host.WorkdirParent, "some/path")) + assert.True(t, strings.HasSuffix(config.Host.WorkdirParent, filepath.FromSlash("some/path"))) assert.True(t, filepath.IsAbs(config.Host.WorkdirParent)) assert.NotEqual(t, len(defaultConfig.Server.Connections), len(config.Server.Connections)) @@ -390,12 +393,31 @@ assert.Equal(t, labels.MustParse("debian:docker://node:24-trixie"), config.Server.Connections["example"].Labels[0]) }) - t.Run("Imports optional env file", func(t *testing.T) { + t.Run("Imports optional env file configured with os-native path", func(t *testing.T) { tempDir := t.TempDir() configFile := filepath.Join(tempDir, "config.yaml") envFile := filepath.Join(tempDir, ".env") - rawConfig := fmt.Sprintf(`{ runner: { env_file: "%s" } }`, envFile) + rawConfig := fmt.Sprintf(`{ runner: { env_file: %q } }`, envFile) + err := os.WriteFile(configFile, []byte(rawConfig), 0o644) + require.NoError(t, err) + + err = os.WriteFile(envFile, []byte("SOME_ENV_VAR=some-value"), 0o644) + require.NoError(t, err) + + config, err := New(FromFile(configFile)) + require.NoError(t, err) + + assert.Equal(t, envFile, config.Runner.EnvFile) + assert.Equal(t, map[string]string{"SOME_ENV_VAR": "some-value"}, config.Runner.Envs) + }) + + t.Run("Imports optional env file configured with UNIX path", func(t *testing.T) { + tempDir := t.TempDir() + configFile := filepath.Join(tempDir, "config.yaml") + envFile := filepath.ToSlash(filepath.Join(tempDir, ".env")) + + rawConfig := fmt.Sprintf(`{ runner: { env_file: %q } }`, envFile) err := os.WriteFile(configFile, []byte(rawConfig), 0o644) require.NoError(t, err) @@ -418,7 +440,7 @@ runner: envs: MY_VARIABLE: value - env_file: "%s" + env_file: %q `, envFile) err := os.WriteFile(configFile, []byte(rawConfig), 0o644) require.NoError(t, err) @@ -438,7 +460,7 @@ configFile := filepath.Join(tempDir, "config.yaml") envFile := filepath.Join(tempDir, ".env") - rawConfig := fmt.Sprintf(`{ runner: { env_file: "%s" } }`, envFile) + rawConfig := fmt.Sprintf(`{ runner: { env_file: %q } }`, envFile) err := os.WriteFile(configFile, []byte(rawConfig), 0o644) require.NoError(t, err) @@ -454,7 +476,7 @@ configFile := filepath.Join(tempDir, "config.yaml") envFile := filepath.Join(tempDir, ".env") - rawConfig := fmt.Sprintf(`{ runner: { env_file: "%s" } }`, envFile) + rawConfig := fmt.Sprintf(`{ runner: { env_file: %q } }`, envFile) err := os.WriteFile(configFile, []byte(rawConfig), 0o644) require.NoError(t, err) @@ -913,6 +935,9 @@ err := os.WriteFile(secretPath, []byte("y114lUUM"), 0o644) require.NoError(t, err) + secretURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + booleanTrue := true settings := serializedCacheSettings{ Enabled: &booleanTrue, @@ -923,7 +948,7 @@ ExternalServer: "external.local", ActionsCacheURLOverride: "https://example.com/", Secret: "", - SecretURL: fmt.Sprintf("file:%s", secretPath), + SecretURL: secretURL.String(), } config := Config{} @@ -1253,11 +1278,14 @@ serverURL, err := url.Parse("https://example.com/") require.NoError(t, err) + tokenURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + serialized := serializedConnectionSettings{ URL: serverURL.String(), UUID: "009e3230-0881-4690-8e0e-43ce2c01d2f9", Token: "", - TokenURL: fmt.Sprintf("file:%s", secretPath), + TokenURL: tokenURL.String(), Labels: []string{"label-1"}, } @@ -1365,7 +1393,10 @@ err := os.WriteFile(secretPath, []byte(rawSecret), 0o644) require.NoError(t, err) - secret, err := resolveSecretURL(fmt.Sprintf("file://%s", secretPath)) + secretURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + + secret, err := resolveSecretURL(secretURL.String()) require.NoError(t, err) assert.Equal(t, rawSecret, secret) @@ -1394,7 +1425,10 @@ err := os.WriteFile(secretPath, []byte(rawSecret), 0o644) require.NoError(t, err) - secret, err := resolveFileSecret(fmt.Sprintf("file://%s", secretPath)) + secretURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + + secret, err := resolveFileSecret(secretURL.String()) require.NoError(t, err) assert.Equal(t, rawSecret, secret) @@ -1409,7 +1443,10 @@ err := os.WriteFile(secretPath, []byte(rawSecret), 0o644) require.NoError(t, err) - secret, err := resolveFileSecret(fmt.Sprintf("file:%s", secretPath)) + secretURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + + secret, err := resolveFileSecret(secretURL.String()) require.NoError(t, err) assert.Equal(t, rawSecret, secret) @@ -1423,14 +1460,18 @@ err := os.WriteFile(secretPath, []byte(rawSecret), 0o644) require.NoError(t, err) + secretURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + secretURL.Host = "some-host" - secret, err := resolveFileSecret(fmt.Sprintf("file://some-host%s", secretPath)) + secret, err := resolveFileSecret(secretURL.String()) require.NoError(t, err) assert.Equal(t, rawSecret, secret) }) t.Run("with env variable CREDENTIALS_DIRECTORY", func(t *testing.T) { + skip.If(t, runtime.GOOS != "linux") // The $CREDENTIALS_DIRECTORY environment variable is only relevant on Linux. rawSecret := "zoN4nQX" tempDir := t.TempDir() @@ -1462,7 +1503,10 @@ err := os.WriteFile(secretPath, []byte{}, 0o644) require.NoError(t, err) - secret, err := resolveFileSecret(fmt.Sprintf("file://%s", secretPath)) + secretURL, err := fileuri.FromFilePath(secretPath) + require.NoError(t, err) + + secret, err := resolveFileSecret(secretURL.String()) require.NoError(t, err) assert.Empty(t, secret) ++++++ forgejo-runner.obsinfo ++++++ --- /var/tmp/diff_new_pack.L6iB21/_old 2026-03-27 06:39:50.754083696 +0100 +++ /var/tmp/diff_new_pack.L6iB21/_new 2026-03-27 06:39:50.826086633 +0100 @@ -1,5 +1,5 @@ name: forgejo-runner -version: 12.7.2 -mtime: 1773072240 -commit: 7d42cbf894cb44b28171604c1fd7e9e2cc927540 +version: 12.7.3 +mtime: 1774360979 +commit: f89f3e3cdb1c89b51c925c78028752d78c2478c1 ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/.editorconfig new/vendor/github.com/powerman/fileuri/.editorconfig --- old/vendor/github.com/powerman/fileuri/.editorconfig 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/.editorconfig 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,25 @@ +# Doc: https://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 94 + +[*.md] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[{*.go,go.mod,go.work}] +indent_style = tab +indent_size = unset + +[*.proto] +indent_size = 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/.gitattributes new/vendor/github.com/powerman/fileuri/.gitattributes --- old/vendor/github.com/powerman/fileuri/.gitattributes 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/.gitattributes 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,8 @@ +# /name - apply (* doesn't match /) to file "name" beginning in project root +# na/me - apply (* doesn't match /) to file "na/me" anywhere +# name - apply (* do match /) to file "name" anywhere +# name/** - apply … to dir … +# **/name - apply (* doesn't match /) to file "name" in any dir including project root +# na/**/me - apply (* doesn't match /) to file "na/me", "na/*/me", "na/*/*/me", … +go.sum binary +*.*.go binary diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/.gitignore new/vendor/github.com/powerman/fileuri/.gitignore --- old/vendor/github.com/powerman/fileuri/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/.gitignore 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,11 @@ +# /name - exclude path (* doesn't match /) to file/dir "name" beginning in project root +# na/me - exclude path (* doesn't match /) to file/dir "na/me" anywhere +# name - exclude path (* do match /) to file/dir "name" anywhere +# name/ - exclude path (* doesn't match /) to dir "name" anywhere +# **/name - exclude path (* doesn't match /) to file/dir "name" in any dir including project root +# na/**/me - exclude path (* doesn't match /) to file/dir "na/me", "na/*/me", "na/*/*/me", … +# !name - include previously excluded path … +/.cache/ + +**/*mise.local.toml +**/*mise/config.local.toml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/.golangci.yml new/vendor/github.com/powerman/fileuri/.golangci.yml --- old/vendor/github.com/powerman/fileuri/.golangci.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/.golangci.yml 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,413 @@ +# Origin: https://github.com/powerman/golangci-lint-strict version 2.4.0 +version: "2" +linters: + default: all + disable: + - containedctx # Questionable. + - contextcheck # Questionable. + - cyclop # Prefer gocognit. + - dogsled # Questionable (assignment to how many blank identifiers is not okay?). + - dupl + - forcetypeassert # Questionable (often we actually want panic). + - gocyclo # Prefer gocognit. + - interfacebloat # Questionable. + - ireturn # Questionable (is returning unexported types better?). + - lll # Questionable (sometimes long lines improve readability). + - nlreturn # Questionable (often no blank line before return improve readability). + - nonamedreturns # Questionable (named return act as a documentation). + - perfsprint # Questionable (force performance over readability and sometimes safety). + - varnamelen + - wrapcheck # Questionable (see https://github.com/tomarrell/wrapcheck/issues/1). + - wsl # Questionable (too much style differences, hard to consider). + - wsl_v5 # Questionable (too much style differences, hard to consider). + settings: + decorder: + disable-init-func-first-check: false # `init` funcs have to be declared before all other functions. + depguard: + rules: + main: + deny: + - pkg: math/rand$ + desc: use math/rand/v2 + - pkg: github.com/prometheus/common/log + desc: Should be replaced by standard lib log/slog package + - pkg: github.com/sirupsen/logrus + desc: Should be replaced by standard lib log/slog package + - pkg: github.com/go-errors/errors + desc: Should be replaced by standard lib errors package + - pkg: github.com/pkg/errors + desc: Should be replaced by standard lib errors package + - pkg: github.com/prometheus/client_golang/prometheus/promauto + desc: Not allowed because it uses global variables + - pkg: github.com/golang/protobuf + desc: Should be replaced by google.golang.org/protobuf package + dupl: + threshold: 100 + embeddedstructfieldcheck: + forbid-mutex: true + errcheck: + exclude-functions: + - encoding/json.Marshal # Required because of errchkjson.check-error-free-encoding. + - encoding/json.MarshalIndent # Required because of errchkjson.check-error-free-encoding. + errchkjson: + check-error-free-encoding: true + report-no-exported: true # Encoded struct must have exported fields. + exhaustive: + check: + - switch + - map + explicit-exhaustive-map: true # Only check maps with "//exhaustive:enforce" comment. + exhaustruct: + include: + - ^$ # Only check structs which domain.tld/package/name.structname match this regexp. + fatcontext: + check-struct-pointers: true + forbidigo: + forbid: + - pattern: ^print(ln)?$ + exclude-godoc-examples: false + analyze-types: true + funcorder: + struct-method: false + gochecksumtype: + default-signifies-exhaustive: false + include-shared-interfaces: true + gocognit: + min-complexity: 20 + gocritic: + enable-all: true + disabled-checks: + - exposedSyncMutex # Questionable. + - hugeParam # Premature optimization. + - paramTypeCombine # Questionable. + - switchTrue # Questionable. + - todoCommentWithoutDetail # Questionable. + - yodaStyleExpr # Questionable. + settings: + captLocal: + paramsOnly: false # Do not restrict checker to params only. + ruleguard: + failOn: all + truncateCmp: + skipArchDependent: false # Do not skip int/uint/uintptr types. + underef: + skipRecvDeref: false + unnamedResult: + checkExported: true + godot: + exclude: + - :$ # Allow line followed by details in next line(s). + - '^\s*- ' # Allow line with a list item. + godox: + keywords: + - BUG # Marks issues that should be moved to issue tracker before merging. + - FIXME # Marks issues that should be resolved before merging. + - DEBUG # Marks temporary code that should be removed before merging. + gomodguard: + blocked: + versions: + - github.com/cenkalti/backoff: + version: < 4.0.0 + reason: use actual version + gosec: + excludes: + - G104 # Audit errors not checked + config: + global: + audit: true + govet: + enable-all: true + disable: + - fieldalignment + settings: + shadow: + strict: true + grouper: + import-require-single-import: true # Use a single 'import' declaration. + iface: + enable: + - identical # Identifies interfaces in the same package that have identical method sets. + - unused # Identifies interfaces that are not used anywhere in the same package where the interface is defined. + - opaque # Identifies functions that return interfaces, but the actual returned value is always a single concrete implementation. + - unexported # Identifies interfaces that are not exported but are used in exported functions or methods. + importas: + alias: + - pkg: errors + alias: "" + - pkg: net/url + alias: urlpkg + loggercheck: + require-string-key: true # Logging keys must be inlined constant strings. + no-printf-like: true + misspell: + mode: restricted # Check only comments. + nestif: + min-complexity: 4 + nilnil: + detect-opposite: true + nolintlint: + require-explanation: true # Disable linters this way: //nolint:first,second // Reason here. + require-specific: true # Do not allow //nolint without specific linter name(s). + paralleltest: + ignore-missing: true # Do not require `t.Parallel()` everywhere. + ignore-missing-subtests: true # Do not require `t.Parallel()` in all subtests. + reassign: + patterns: + - .* # Check all global variables. + revive: + rules: + - name: add-constant + disabled: true # Duplicates goconst and mnd linters. + - name: argument-limit + disabled: true # Questionable. + - name: atomic + - name: banned-characters + arguments: [] # [ "Ω","Σ","σ", "7" ] + - name: bare-return + disabled: true # Questionable (in some cases bare return improves readability). + - name: blank-imports + - name: bool-literal-in-expr + - name: call-to-gc + - name: cognitive-complexity + disabled: true # Duplicates gocognit linter. + - name: comment-spacings + arguments: + - nolint # Allow //nolint without a space. + - name: comments-density + disabled: true # Questionable. + - name: confusing-naming + disabled: true # Questionable (valid use case: Method() as a thin wrapper for method()). + - name: confusing-results + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: cyclomatic + disabled: true # Duplicates cyclop and gocyclo linters. + - name: datarace + - name: deep-exit + - name: defer + - name: dot-imports + - name: duplicated-imports + - name: early-return + - name: empty-block + disabled: true # https://github.com/mgechev/revive/issues/386 + - name: empty-lines + - name: enforce-map-style + arguments: + - make # Use `make(map[A]B)` instead of literal `map[A]B{}`. + - name: enforce-repeated-arg-type-style + disabled: true # Questionable (short form for similar args and full otherwise may improve readability). + - name: enforce-slice-style + disabled: true # Questionable (sometimes we need a nil slice, sometimes not nil). + - name: enforce-switch-style + arguments: + - allowNoDefault + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: file-header + - name: file-length-limit + - name: filename-format + - name: flag-parameter + - name: function-length + disabled: true # Duplicates funlen linter. + - name: function-result-limit + disabled: true # Questionable. + - name: get-return + - name: identical-branches + - name: if-return + - name: import-alias-naming + - name: import-shadowing + - name: imports-blocklist + - name: increment-decrement + - name: indent-error-flow + - name: line-length-limit + disabled: true # Duplicates lll linter. + - name: max-control-nesting + - name: max-public-structs + disabled: true # Questionable. + - name: modifies-parameter + - name: modifies-value-receiver + - name: nested-structs + disabled: true # Questionable (useful in tests, may worth enabling for non-tests). + - name: optimize-operands-order + - name: package-comments + - name: range + - name: range-val-address + - name: range-val-in-closure + - name: receiver-naming + - name: redefines-builtin-id + - name: redundant-build-tag + - name: redundant-import-alias + - name: redundant-test-main-exit + - name: string-format + arguments: + - - fmt.Errorf[0] + - /(^|[^\.!?])$/ + - must not end in punctuation + - - panic + - /^[^\n]*$/ + - must not contain line breaks + - name: string-of-int + - name: struct-tag + - name: superfluous-else + - name: time-date + - name: time-equal + - name: time-naming + - name: unchecked-type-assertion + disabled: true # Duplicates errcheck and forcetypeassert linters. + - name: unconditional-recursion + - name: unexported-naming + - name: unexported-return + - name: unhandled-error + disabled: true # Duplicates errcheck linter. + - name: unnecessary-format + - name: unnecessary-stmt + - name: unreachable-code + - name: unused-parameter + - name: unused-receiver + - name: use-any + - name: use-errors-new + - name: use-fmt-print + - name: useless-break + - name: var-declaration + - name: var-naming + - name: waitgroup-by-value + rowserrcheck: + packages: + - github.com/jmoiron/sqlx + - github.com/powerman/sqlxx + sloglint: + context: scope + static-msg: true + msg-style: capitalized + key-naming-case: snake + forbidden-keys: + - time # Used by standard slog.JSONHandler or slog.TextHandler. + - level # Used by standard slog.JSONHandler or slog.TextHandler. + - msg # Used by standard slog.JSONHandler or slog.TextHandler. + - source # Used by standard slog.JSONHandler or slog.TextHandler. + tagalign: + order: + - json + - yaml + - yml + - toml + - env + - mod + - mapstructure + - binding + - validate + strict: true + tagliatelle: + case: + rules: + json: snake + yaml: kebab + xml: camel + toml: camel + bson: camel + avro: snake + mapstructure: kebab + envconfig: upperSnake + whatever: snake + testifylint: + enable-all: true + testpackage: + skip-regexp: .*_internal_test\.go + thelper: + test: + name: false # Allow *testing.T param to have any name, not only `t`. + usestdlibvars: + time-date-month: true + time-month: true + time-layout: true + crypto-hash: true + default-rpc-path: true + sql-isolation-level: true + tls-signature-scheme: true + wrapcheck: + report-internal-errors: true + exclusions: + rules: + - path: (.+)\.go$ + text: declaration of "(log|err|ctx)" shadows + - path: (.+)\.go$ + text: 'missing cases in switch of type \S+: \S+_UNSPECIFIED$' + - path: _test\.go|testing(_.*)?\.go + linters: + - bodyclose + - dupl + - errcheck + - forcetypeassert + - funlen + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gosec + - maintidx + - reassign + - source: '[Cc]onst' # Define global const-vars like: var SomeGlobal = []int{42} // Const. + linters: + - gochecknoglobals + - path: _test\.go|testing(_.*)?\.go + text: (unnamedResult|exitAfterDefer|rangeValCopy|unnecessaryBlock) + linters: + - gocritic + - path: _test\.go + text: '"t" shadows' + linters: + - govet + - path: ^(.*/)?embed.go$ + linters: + - gochecknoglobals + - linters: + - lll + source: '^//go:generate ' + paths: + - \.[\w-]+\.go$ # Use this pattern to name auto-generated files. +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + - golines + settings: + gci: + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - localmodule # Local module section: contains all local packages. This section is not present unless explicitly enabled. + no-inline-comments: true + no-prefix-comments: true + gofmt: + rewrite-rules: + - pattern: interface{} + replacement: any + - pattern: a[b:len(a)] + replacement: a[b:] + golines: + max-len: 200 + tab-len: 8 + exclusions: + generated: strict + paths: + - \.[\w-]+\.go$ # Use this pattern to name auto-generated files. +issues: + max-issues-per-linter: 0 + max-same-issues: 0 +output: + sort-order: + - linter + - severity + - file # filepath, line, and column. + show-stats: false +run: + timeout: 1m + modules-download-mode: readonly +severity: + default: error diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/CHANGELOG.md new/vendor/github.com/powerman/fileuri/CHANGELOG.md --- old/vendor/github.com/powerman/fileuri/CHANGELOG.md 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/CHANGELOG.md 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] - 2025-08-24 + +### 🚀 Added + +- Add extended-length path prefix support by @powerman in [af72ba9] + +### 🐛 Fixed + +- Require UNC share name by @powerman in [bb632aa] + +[0.2.0]: https://github.com/powerman/fileuri/compare/%40%7B10year%7D..v0.2.0 +[bb632aa]: https://github.com/powerman/fileuri/commit/bb632aa608bef21cf43d70108b6b225d55140453 +[af72ba9]: https://github.com/powerman/fileuri/commit/af72ba9ea2c93f670d0cda14b974bb6bdc327039 + +<!-- generated by git-cliff --> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/LICENSE new/vendor/github.com/powerman/fileuri/LICENSE --- old/vendor/github.com/powerman/fileuri/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/LICENSE 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Alex Efros + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/LICENSE-go new/vendor/github.com/powerman/fileuri/LICENSE-go --- old/vendor/github.com/powerman/fileuri/LICENSE-go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/LICENSE-go 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/README.md new/vendor/github.com/powerman/fileuri/README.md --- old/vendor/github.com/powerman/fileuri/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/README.md 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,19 @@ +# fileuri is a Go package to work with file:// URI + +[](LICENSE) +[](https://go.dev/) +[](https://github.com/powerman/fileuri/actions/workflows/test.yml) +[](https://github.com/powerman/fileuri/actions/workflows/test.yml) +[](https://goreportcard.com/report/github.com/powerman/fileuri) +[](https://github.com/powerman/fileuri/releases/latest) +[](https://pkg.go.dev/github.com/powerman/fileuri) + + + + + +Implements [RFC 8089 The "file" URI Scheme](https://datatracker.ietf.org/doc/html/rfc8089). + +The implementation is based on `cmd/go/internal/web` package from Go 1.25.0. + +See also [Go issue 32456](https://github.com/golang/go/issues/32456). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/fileuri.go new/vendor/github.com/powerman/fileuri/fileuri.go --- old/vendor/github.com/powerman/fileuri/fileuri.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/fileuri.go 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,129 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE-go file. + +// Copyright 2025 Alex Efros <[email protected]>. All rights reserved. +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. + +// Package fileuri provides functions to work with file:// URLs. +// It implements RFC 8089 The "file" URI Scheme. +package fileuri + +import ( + "errors" + "net/url" + "path/filepath" + "strings" +) + +// TODO(golang.org/issue/32456): If accepted, move these functions into the +// net/url package. + +var ( + errNotAbsolute = errors.New("path is not absolute") + errNonFile = errors.New("non-file URL") + errMissingPath = errors.New("file URL missing path") +) + +// ToFilePath converts a file URL to a filesystem path. +// It returns an error if the URL is not a file URL or if the path is not absolute. +// The returned path is in the format of the current operating system. +// +// ToFilePath handles file URLs as specified in RFC 8089. +// In particular, it supports both hostless file URLs (file:///path/to/file) +// and UNC file URLs (file://host/share/path/to/file). +// +// ToFilePath does not access the filesystem and does not verify that the path exists. +func ToFilePath(u *url.URL) (string, error) { + if u.Scheme != "file" { + return "", errNonFile + } + + checkAbs := func(path string) (string, error) { + if !filepath.IsAbs(path) { + return "", errNotAbsolute + } + return path, nil + } + + if u.Path == "" { + if u.Host != "" || u.Opaque == "" { + return "", errMissingPath + } + return checkAbs(filepath.FromSlash(u.Opaque)) + } + + path, err := convertFileURLPath(u.Host, u.Path) + if err != nil { + return path, err + } + return checkAbs(path) +} + +// FromFilePath converts an absolute filesystem path to a file URL. +// It returns an error if the path is not absolute. +// +// FromFilePath handles paths on Windows and Unix as specified in RFC 8089. +// In particular, it converts Windows UNC paths (\\host\share\path\to\file) +// to UNC file URLs (file://host/share/path/to/file). +// +// FromFilePath does not access the filesystem and does not verify that the path exists. +func FromFilePath(path string) (*url.URL, error) { + // Convert extended-length UNC path to regular UNC path. + // See https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#unc-paths. + if strings.HasPrefix(path, `\\?\UNC`) { + path = `\` + path[7:] + } else if strings.HasPrefix(path, `\\?\`) { + path = path[4:] + } + + if !filepath.IsAbs(path) { + return nil, errNotAbsolute + } + + // If path has a Windows volume name, convert the volume to a host and prefix + // per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/. + if vol := filepath.VolumeName(path); vol != "" { + if strings.HasPrefix(vol, `\\`) { + path = filepath.ToSlash(path[2:]) + i := strings.IndexByte(path, '/') + + if i < 0 { + // A degenerate case. + // \\host.example.com (without a share name) + // becomes + // file://host.example.com/ + return &url.URL{ + Scheme: "file", + Host: path, + Path: "/", + }, nil + } + + // \\host.example.com\Share\path\to\file + // becomes + // file://host.example.com/Share/path/to/file + return &url.URL{ + Scheme: "file", + Host: path[:i], + Path: filepath.ToSlash(path[i:]), + }, nil + } + + // C:\path\to\file + // becomes + // file:///C:/path/to/file + return &url.URL{ + Scheme: "file", + Path: "/" + filepath.ToSlash(path), + }, nil + } + + // /path/to/file + // becomes + // file:///path/to/file + return &url.URL{ + Scheme: "file", + Path: filepath.ToSlash(path), + }, nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/fileuri_other.go new/vendor/github.com/powerman/fileuri/fileuri_other.go --- old/vendor/github.com/powerman/fileuri/fileuri_other.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/fileuri_other.go 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,26 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE-go file. + +// Copyright 2025 Alex Efros <[email protected]>. All rights reserved. +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. + +//go:build !windows + +package fileuri + +import ( + "errors" + "path/filepath" +) + +var errNonLocalHost = errors.New("file URL specifies non-local host") + +func convertFileURLPath(host, path string) (string, error) { + switch host { + case "", "localhost": + default: + return "", errNonLocalHost + } + return filepath.FromSlash(path), nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/fileuri_windows.go new/vendor/github.com/powerman/fileuri/fileuri_windows.go --- old/vendor/github.com/powerman/fileuri/fileuri_windows.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/fileuri_windows.go 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,67 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE-go file. + +// Copyright 2025 Alex Efros <[email protected]>. All rights reserved. +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. + +package fileuri + +import ( + "errors" + "path/filepath" + "strings" +) + +const maxPath = 260 // Windows MAX_PATH limitation. + +var ( + errVolumeInHost = errors.New("file URL encodes volume in host field: too few slashes?") + errMissingVolume = errors.New("file URL missing drive letter") + errMissingShare = errors.New("file URL missing UNC share name") +) + +func convertFileURLPath(host, path string) (string, error) { + if path == "" || path[0] != '/' { + return "", errNotAbsolute + } + + path = filepath.FromSlash(path) + + // We interpret Windows file URLs per the description in + // https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/. + + // The host part of a file URL (if any) is the UNC volume name, + // but RFC 8089 reserves the authority "localhost" for the local machine. + if host != "" && host != "localhost" { + // A common "legacy" format omits the leading slash before a drive letter, + // encoding the drive letter as the host instead of part of the path. + // (See https://blogs.msdn.microsoft.com/freeassociations/2005/05/19/the-bizarre-and-unhappy-story-of-file-urls/.) + // We do not support that format, but we should at least emit a more + // helpful error message for it. + if filepath.VolumeName(host) != "" { + return "", errVolumeInHost + } + if path == `\` { + return "", errMissingShare + } + path = `\\` + host + path + if len(path) >= maxPath { // Too strict because check bytes instead of UTF-16 chars. + // Use the extended-length path prefix to avoid MAX_PATH limitations. + // See https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation. + path = `\\?\UNC` + filepath.Clean(path[1:]) + } + return path, nil + } + + // If host is empty, path must contain an initial slash followed by a + // drive letter and path. Remove the slash and verify that the path is valid. + if vol := filepath.VolumeName(path[1:]); vol == "" || strings.HasPrefix(vol, `\\`) { + return "", errMissingVolume + } + path = path[1:] + if len(path) >= maxPath { // Too strict because check bytes instead of UTF-16 chars. + path = `\\?\` + filepath.Clean(path) + } + return path, nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/powerman/fileuri/mise.toml new/vendor/github.com/powerman/fileuri/mise.toml --- old/vendor/github.com/powerman/fileuri/mise.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/powerman/fileuri/mise.toml 2026-03-26 09:17:46.000000000 +0100 @@ -0,0 +1,115 @@ +[settings] +experimental = true # Required to use lockfile. +lockfile = true # DO NOT FORGET TO `touch mise.lock` if mise.lock does not exist! + + +[tools] +go = 'latest' + +#--- Generate +# MockGen generates mock implementations of Go interfaces. +'go:go.uber.org/mock/mockgen' = 'latest' + +#--- Lint +# Static checker for GitHub Actions workflow files. +actionlint = 'latest' +# Fast linters runner for Go. +golangci-lint = 'latest' + +#--- Test +# 'go test' runner with output optimized for humans. +gotestsum = 'latest' + +#--- Release +# A highly customizable Changelog Generator that follows Conventional Commit specifications. +git-cliff = 'latest' +# GitHub's official command line tool. +gh = 'latest' + + +[vars] +cover = '.cache/cover.out' + + +[tasks.'changelog:skip-commit'] +description = 'Add commit hash to .cliffignore to exclude from CHANGELOG' +usage = 'arg "<commit>" help="Git revision (e.g. HEAD or a1b2c4d)"' +run = 'git rev-parse --verify "${usage_commit}" >> .cliffignore' + +[tasks.generate] +description = 'Generate everything' +depends = ['generate:*'] + +[tasks.fmt] +description = 'Format all files' +depends = ['fmt:*'] + +[tasks.lint] +description = 'Run all linters' +depends = ['lint:*'] + +[tasks.test] +description = 'Run all tests' +depends = ['test:*'] + +[tasks.default] +description = 'Run all linters and tests' +depends = ['lint', 'test'] + +[tasks.ci] +description = 'Run all CI tasks' +depends = ['generate', 'fmt', 'lint', 'test'] +# Is working tree clean after fmt? +run = '! git status --porcelain | tee /dev/stderr | grep -q .' +run_windows = 'powershell -c "$status = git status --porcelain; if ($status) { $status; exit 1 }"' + +[tasks.'generate:go'] +description = 'Generate Go code' +run = 'go generate ./...' + +[tasks.'fmt:go'] +description = 'Format Go code' +run = 'golangci-lint fmt' + +[tasks.'lint:workflows'] +description = 'Lint GitHub Action workflows' +run = 'actionlint' + +[tasks.'lint:go'] +description = 'Lint Go files' +run = 'golangci-lint run' + +[tasks.'lint:go-compile-windows'] +description = 'Check Go test compiles on Windows' +run = 'GOOS=windows go test -c -o /dev/null ./...' + +[tasks.'test:go'] +description = 'Run Go tests for a whole project' +wait_for = ['generate:*', 'lint:*'] # Avoid interleaved output with linters. +run = 'gotestsum -- -race -timeout=60s ./...' + +[tasks.'cover:go:total'] +description = 'Show Go test coverage total' +depends = 'cover:go:generate' +run = 'go tool cover -func={{vars.cover}} | tail -n 1 | xargs echo' + +[tasks.'cover:go:browse'] +description = 'Show Go test coverage in a browser' +depends = 'cover:go:generate' +run = 'go tool cover -html={{vars.cover}}' + +[tasks.'cover:go:generate'] +hide = true +depends = 'cachedir' +sources = ['**/*.go'] +outputs = ['{{vars.cover}}'] +run = ''' +gotestsum -- \ + -coverpkg="$(go list ./... | paste -s -d,)" \ + -coverprofile {{vars.cover}} \ + ./... +''' + +[tasks.cachedir] +hide = true +run = 'mkdir -p .cache' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2026-03-17 11:44:22.000000000 +0100 +++ new/vendor/modules.txt 2026-03-26 09:17:46.000000000 +0100 @@ -378,6 +378,9 @@ # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib +# github.com/powerman/fileuri v0.2.0 +## explicit; go 1.24.6 +github.com/powerman/fileuri # github.com/rhysd/actionlint v1.7.10 ## explicit; go 1.24.0 github.com/rhysd/actionlint @@ -510,8 +513,8 @@ golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.40.0 -## explicit; go 1.24.0 +# golang.org/x/term v0.41.0 +## explicit; go 1.25.0 golang.org/x/term # golang.org/x/time v0.15.0 ## explicit; go 1.25.0
