Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package chezmoi for openSUSE:Factory checked 
in at 2023-01-24 20:42:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/chezmoi (Old)
 and      /work/SRC/openSUSE:Factory/.chezmoi.new.32243 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "chezmoi"

Tue Jan 24 20:42:52 2023 rev:11 rq:1060686 version:2.29.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/chezmoi/chezmoi.changes  2023-01-16 
18:02:18.527959803 +0100
+++ /work/SRC/openSUSE:Factory/.chezmoi.new.32243/chezmoi.changes       
2023-01-24 20:59:41.261520298 +0100
@@ -1,0 +2,10 @@
+Tue Jan 24 18:10:48 UTC 2023 - Filippo Bonazzi <filippo.bona...@suse.com>
+
+- Update to version 2.29.3:
+  * feat: Add --recursive flag to chattr command
+  * feat: Check config file format in doctor command
+  * fix: Support .zip files with empty internal directories
+  * fix: Only prompt once for KeePassXC password
+  * fix: Remove problematic username guessing in init command
+
+-------------------------------------------------------------------

Old:
----
  chezmoi-2.29.2.obscpio

New:
----
  chezmoi-2.29.3.obscpio

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

Other differences:
------------------
++++++ chezmoi.spec ++++++
--- /var/tmp/diff_new_pack.hM5tAD/_old  2023-01-24 20:59:43.333532264 +0100
+++ /var/tmp/diff_new_pack.hM5tAD/_new  2023-01-24 20:59:43.337532287 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           chezmoi
-Version:        2.29.2
+Version:        2.29.3
 Release:        0
 Summary:        A multi-host manager for dotfiles
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.hM5tAD/_old  2023-01-24 20:59:43.369532472 +0100
+++ /var/tmp/diff_new_pack.hM5tAD/_new  2023-01-24 20:59:43.373532495 +0100
@@ -2,7 +2,7 @@
   <service name="obs_scm" mode="manual">
     <param name="scm">git</param>
     <param name="url">https://github.com/twpayne/chezmoi.git</param>
-    <param name="revision">v2.29.2</param>
+    <param name="revision">v2.29.3</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
   </service>

++++++ chezmoi-2.29.2.obscpio -> chezmoi-2.29.3.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/.github/workflows/main.yml 
new/chezmoi-2.29.3/.github/workflows/main.yml
--- old/chezmoi-2.29.2/.github/workflows/main.yml       2023-01-15 
15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/.github/workflows/main.yml       2023-01-19 
21:40:03.000000000 +0100
@@ -425,17 +425,21 @@
         GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
         SCOOP_GITHUB_TOKEN: ${{ secrets.SCOOP_GITHUB_TOKEN }}
         SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
-      # The following is needed because chezmoi upgrade and
-      # assets/scripts/install.sh have inconsistently looked for
-      # chezmoi_${VERSION}_checksums.txt and checksums.txt. To ensure
-      # compatibility with all versions, upload checksums.txt as well.
+    # The following is needed because chezmoi upgrade and
+    # assets/scripts/install.sh have inconsistently looked for
+    # chezmoi_${VERSION}_checksums.txt and checksums.txt. To ensure
+    # compatibility with all versions, upload checksums.txt as well.
+    #
+    # We need to use a different GitHub token as fine-grained personal access
+    # tokens do not allow access to the GraphQL API, which gh depends on. So, 
we
+    # use a normal PAT instead.
     - name: upload-checksums.txt
       run: |
         VERSION=${GITHUB_REF##*/v}
         cp dist/chezmoi_${VERSION}_checksums.txt dist/checksums.txt
         gh release upload v${VERSION} dist/checksums.txt
       env:
-        GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
+        GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }}
   deploy-website:
     needs:
     - release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/chezmoi-2.29.2/assets/chezmoi.io/docs/links/articles-podcasts-and-videos.md.yaml
 
new/chezmoi-2.29.3/assets/chezmoi.io/docs/links/articles-podcasts-and-videos.md.yaml
--- 
old/chezmoi-2.29.2/assets/chezmoi.io/docs/links/articles-podcasts-and-videos.md.yaml
        2023-01-15 15:35:48.000000000 +0100
+++ 
new/chezmoi-2.29.3/assets/chezmoi.io/docs/links/articles-podcasts-and-videos.md.yaml
        2023-01-19 21:40:03.000000000 +0100
@@ -323,3 +323,7 @@
   title: 'Chezmoiでかんたんクロスプラットフォーム
dotfiles管理のススメ'
   lang: JP
   url: 
https://deflis.hatenablog.com/entry/hatena-advent-calendar-2022-chezmoi-dotfiles
+- date: '2023-01-13'
+  version: 2.29.1
+  title: 'Making the most out of distrobox and toolbx'
+  url: https://www.ypsidanger.com/making-the-most-out-of-distrobox-and-toolbx/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/chezmoi-2.29.2/assets/chezmoi.io/docs/user-guide/tools/merge.md 
new/chezmoi-2.29.3/assets/chezmoi.io/docs/user-guide/tools/merge.md
--- old/chezmoi-2.29.2/assets/chezmoi.io/docs/user-guide/tools/merge.md 
2023-01-15 15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/assets/chezmoi.io/docs/user-guide/tools/merge.md 
2023-01-19 21:40:03.000000000 +0100
@@ -2,9 +2,9 @@
 
 ## Use a custom merge command
 
-By default, chezmoi uses `vimdiff.` You can use a custom command by setting the
+By default, chezmoi uses `vimdiff`. You can use a custom command by setting the
 `merge.command` and `merge.args` configuration variables. The elements of
-`merge.args` are interprested as templates with the variables `.Destination`,
+`merge.args` are interpreted as templates with the variables `.Destination`,
 `.Source`, and `.Target` containing filenames of the file in the destination
 state, source state, and target state respectively. For example, to use
 [neovim's diff mode](https://neovim.io/doc/user/diff.html), specify:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/chezmoi/archive.go 
new/chezmoi-2.29.3/pkg/chezmoi/archive.go
--- old/chezmoi-2.29.2/pkg/chezmoi/archive.go   2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/chezmoi/archive.go   2023-01-19 21:40:03.000000000 
+0100
@@ -12,6 +12,7 @@
        "io/fs"
        "path"
        "strings"
+       "time"
 
        "github.com/ulikunitz/xz"
 )
@@ -132,10 +133,40 @@
        return err == nil
 }
 
+func implicitDirHeader(dir string, modTime time.Time) *tar.Header {
+       return &tar.Header{
+               Typeflag: tar.TypeDir,
+               Name:     dir,
+               Mode:     0o777,
+               Size:     0,
+               ModTime:  modTime,
+       }
+}
+
 // walkArchiveTar walks over all the entries in a tar archive.
 func walkArchiveTar(r io.Reader, f WalkArchiveFunc) error {
        tarReader := tar.NewReader(r)
        var skippedDirPrefixes []string
+       seenDirs := newSet[string]()
+       processHeader := func(header *tar.Header, dir string) error {
+               for _, skippedDirPrefix := range skippedDirPrefixes {
+                       if strings.HasPrefix(header.Name, skippedDirPrefix) {
+                               return fs.SkipDir
+                       }
+               }
+               if seenDirs.contains(dir) {
+                       return nil
+               }
+               seenDirs.add(dir)
+               name := strings.TrimSuffix(header.Name, "/")
+               switch err := f(name, header.FileInfo(), tarReader, 
header.Linkname); {
+               case errors.Is(err, fs.SkipDir):
+                       skippedDirPrefixes = append(skippedDirPrefixes, 
header.Name)
+               case err != nil:
+                       return err
+               }
+               return nil
+       }
 HEADER:
        for {
                header, err := tarReader.Next()
@@ -145,24 +176,28 @@
                case err != nil:
                        return err
                }
-               for _, skippedDirPrefix := range skippedDirPrefixes {
-                       if strings.HasPrefix(header.Name, skippedDirPrefix) {
-                               continue HEADER
-                       }
-               }
-               name := strings.TrimSuffix(header.Name, "/")
                switch header.Typeflag {
-               case tar.TypeDir, tar.TypeReg:
-                       switch err := f(name, header.FileInfo(), tarReader, 
""); {
-                       case errors.Is(err, fs.SkipDir):
-                               skippedDirPrefixes = append(skippedDirPrefixes, 
header.Name)
-                       case errors.Is(err, Break):
-                               return nil
-                       case err != nil:
-                               return err
+               case tar.TypeReg, tar.TypeDir, tar.TypeSymlink:
+                       if header.Typeflag == tar.TypeReg {
+                               dirs, _ := path.Split(header.Name)
+                               dirComponents := 
strings.Split(strings.TrimSuffix(dirs, "/"), "/")
+                               for i := range dirComponents {
+                                       dir := 
strings.Join(dirComponents[0:i+1], "/")
+                                       if len(dir) > 0 {
+                                               switch err := 
processHeader(implicitDirHeader(dir+"/", header.ModTime), dir+"/"); {
+                                               case errors.Is(err, fs.SkipDir):
+                                                       continue HEADER
+                                               case errors.Is(err, Break):
+                                                       return nil
+                                               case err != nil:
+                                                       return err
+                                               }
+                                       }
+                               }
                        }
-               case tar.TypeSymlink:
-                       switch err := f(name, header.FileInfo(), nil, 
header.Linkname); {
+                       switch err := processHeader(header, header.Name); {
+                       case errors.Is(err, fs.SkipDir):
+                               continue HEADER
                        case errors.Is(err, Break):
                                return nil
                        case err != nil:
@@ -183,6 +218,28 @@
                return err
        }
        var skippedDirPrefixes []string
+       seenDirs := newSet[string]()
+       processHeader := func(fileInfo fs.FileInfo, dir string) error {
+               for _, skippedDirPrefix := range skippedDirPrefixes {
+                       if strings.HasPrefix(dir, skippedDirPrefix) {
+                               return fs.SkipDir
+                       }
+               }
+               if seenDirs.contains(dir) {
+                       return nil
+               }
+               seenDirs.add(dir)
+               name := strings.TrimSuffix(dir, "/")
+               dirFileInfo := implicitDirHeader(dir, 
fileInfo.ModTime()).FileInfo()
+               switch err := f(name, dirFileInfo, nil, ""); {
+               case errors.Is(err, fs.SkipDir):
+                       skippedDirPrefixes = append(skippedDirPrefixes, dir)
+                       return err
+               case err != nil:
+                       return err
+               }
+               return nil
+       }
 FILE:
        for _, zipFile := range zipReader.File {
                zipFileReader, err := zipFile.Open()
@@ -203,9 +260,25 @@
 
                switch fileInfo := zipFile.FileInfo(); fileInfo.Mode() & 
fs.ModeType {
                case 0:
+                       dirs, _ := path.Split(name)
+                       dirComponents := strings.Split(strings.TrimSuffix(dirs, 
"/"), "/")
+                       for i := range dirComponents {
+                               dir := strings.Join(dirComponents[0:i+1], "/")
+                               if len(dir) > 0 {
+                                       switch err := processHeader(fileInfo, 
dir+"/"); {
+                                       case errors.Is(err, fs.SkipDir):
+                                               continue FILE
+                                       case errors.Is(err, Break):
+                                               return nil
+                                       case err != nil:
+                                               return err
+                                       }
+                               }
+                       }
+
                        err = f(name, fileInfo, zipFileReader, "")
                case fs.ModeDir:
-                       err = f(name, fileInfo, nil, "")
+                       err = processHeader(fileInfo, name+"/")
                case fs.ModeSymlink:
                        var linknameBytes []byte
                        linknameBytes, err = io.ReadAll(zipFileReader)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/chezmoi/archive_test.go 
new/chezmoi-2.29.3/pkg/chezmoi/archive_test.go
--- old/chezmoi-2.29.2/pkg/chezmoi/archive_test.go      2023-01-15 
15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/pkg/chezmoi/archive_test.go      2023-01-19 
21:40:03.000000000 +0100
@@ -12,50 +12,79 @@
 )
 
 func TestWalkArchive(t *testing.T) {
+       nestedRoot := map[string]any{
+               "dir1": map[string]any{
+                       "subdir1": map[string]any{
+                               "file1": "",
+                               "file2": "",
+                       },
+                       "subdir2": map[string]any{
+                               "file1": "",
+                               "file2": "",
+                       },
+               },
+               "dir2": map[string]any{
+                       "subdir1": map[string]any{
+                               "file1": "",
+                               "file2": "",
+                       },
+                       "subdir2": map[string]any{
+                               "file1": "",
+                               "file2": "",
+                       },
+               },
+               "file1":    "",
+               "file2":    "",
+               "symlink1": &archivetest.Symlink{Target: "file1"},
+               "symlink2": &archivetest.Symlink{Target: "file2"},
+       }
+       flatRoot := map[string]any{
+               "dir1/subdir1/file1": "",
+               "dir1/subdir1/file2": "",
+               "dir1/subdir2/file1": "",
+               "dir1/subdir2/file2": "",
+               "dir2/subdir1/file1": "",
+               "dir2/subdir1/file2": "",
+               "dir2/subdir2/file1": "",
+               "dir2/subdir2/file2": "",
+               "file1":              "",
+               "file2":              "",
+               "symlink1":           &archivetest.Symlink{Target: "file1"},
+               "symlink2":           &archivetest.Symlink{Target: "file2"},
+       }
        for _, tc := range []struct {
                name          string
+               root          map[string]any
                dataFunc      func(map[string]any) ([]byte, error)
                archiveFormat ArchiveFormat
        }{
                {
                        name:          "tar",
+                       root:          nestedRoot,
                        dataFunc:      archivetest.NewTar,
                        archiveFormat: ArchiveFormatTar,
                },
                {
                        name:          "zip",
+                       root:          nestedRoot,
+                       dataFunc:      archivetest.NewZip,
+                       archiveFormat: ArchiveFormatZip,
+               },
+               {
+                       name:          "zip-flat",
+                       root:          flatRoot,
                        dataFunc:      archivetest.NewZip,
                        archiveFormat: ArchiveFormatZip,
                },
+               {
+                       name:          "tar-flat",
+                       root:          flatRoot,
+                       dataFunc:      archivetest.NewTar,
+                       archiveFormat: ArchiveFormatTar,
+               },
        } {
                t.Run(tc.name, func(t *testing.T) {
-                       root := map[string]any{
-                               "dir1": map[string]any{
-                                       "subdir1": map[string]any{
-                                               "file1": "",
-                                               "file2": "",
-                                       },
-                                       "subdir2": map[string]any{
-                                               "file1": "",
-                                               "file2": "",
-                                       },
-                               },
-                               "dir2": map[string]any{
-                                       "subdir1": map[string]any{
-                                               "file1": "",
-                                               "file2": "",
-                                       },
-                                       "subdir2": map[string]any{
-                                               "file1": "",
-                                               "file2": "",
-                                       },
-                               },
-                               "file1":    "",
-                               "file2":    "",
-                               "symlink1": &archivetest.Symlink{Target: 
"file1"},
-                               "symlink2": &archivetest.Symlink{Target: 
"file2"},
-                       }
-                       data, err := tc.dataFunc(root)
+                       data, err := tc.dataFunc(tc.root)
                        require.NoError(t, err)
 
                        expectedNames := []string{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/annotation.go 
new/chezmoi-2.29.3/pkg/cmd/annotation.go
--- old/chezmoi-2.29.2/pkg/cmd/annotation.go    2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/annotation.go    2023-01-19 21:40:03.000000000 
+0100
@@ -5,7 +5,6 @@
 // Annotations.
 var (
        createSourceDirectoryIfNeeded = 
tagAnnotation("chezmoi_create_source_directory_if_needed")
-       doesNotRequireValidConfig     = 
tagAnnotation("chezmoi_does_not_require_valid_config")
        modifiesConfigFile            = 
tagAnnotation("chezmoi_modifies_config_file")
        modifiesDestinationDirectory  = 
tagAnnotation("chezmoi_modifies_destination_directory")
        modifiesSourceDirectory       = 
tagAnnotation("chezmoi_modifies_source_directory")
@@ -13,6 +12,7 @@
        requiresSourceDirectory       = 
tagAnnotation("chezmoi_requires_source_directory")
        requiresWorkingTree           = 
tagAnnotation("chezmoi_requires_working_tree")
        runsCommands                  = tagAnnotation("chezmoi_runs_commands")
+       runsWithInvalidConfig         = 
tagAnnotation("chezmoi_runs_with_invalid_config")
 )
 
 // Persistent state modes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/catconfigcmd.go 
new/chezmoi-2.29.3/pkg/cmd/catconfigcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/catconfigcmd.go  2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/catconfigcmd.go  2023-01-19 21:40:03.000000000 
+0100
@@ -11,8 +11,8 @@
                Args:    cobra.NoArgs,
                RunE:    c.runCatConfigCmd,
                Annotations: newAnnotations(
-                       doesNotRequireValidConfig,
                        requiresConfigDirectory,
+                       runsWithInvalidConfig,
                ),
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/cdcmd.go 
new/chezmoi-2.29.3/pkg/cmd/cdcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/cdcmd.go 2023-01-15 15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/pkg/cmd/cdcmd.go 2023-01-19 21:40:03.000000000 +0100
@@ -22,9 +22,9 @@
                Args:    cobra.MaximumNArgs(1),
                Annotations: newAnnotations(
                        createSourceDirectoryIfNeeded,
-                       doesNotRequireValidConfig,
                        requiresWorkingTree,
                        runsCommands,
+                       runsWithInvalidConfig,
                ),
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/chattrcmd.go 
new/chezmoi-2.29.3/pkg/cmd/chattrcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/chattrcmd.go     2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/chattrcmd.go     2023-01-19 21:40:03.000000000 
+0100
@@ -10,6 +10,10 @@
        "github.com/twpayne/chezmoi/v2/pkg/chezmoi"
 )
 
+type chattrCmdConfig struct {
+       recursive bool
+}
+
 type boolModifier int
 
 const (
@@ -81,6 +85,9 @@
                ),
        }
 
+       flags := chattrCmd.Flags()
+       flags.BoolVarP(&c.chattr.recursive, "recursive", "r", 
c.chattr.recursive, "Recurse into subdirectories")
+
        return chattrCmd
 }
 
@@ -144,6 +151,7 @@
 
        targetRelPaths, err := c.targetRelPaths(sourceState, args[1:], 
targetRelPathsOptions{
                mustBeInSourceState: true,
+               recursive:           c.chattr.recursive,
        })
        if err != nil {
                return err
@@ -151,7 +159,7 @@
 
        // Sort targets in reverse so we update children before their parent
        // directories.
-       sort.Sort(targetRelPaths)
+       sort.Sort(sort.Reverse(targetRelPaths))
 
        encryptedSuffix := sourceState.Encryption().EncryptedSuffix()
        for _, targetRelPath := range targetRelPaths {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/cmd.go 
new/chezmoi-2.29.3/pkg/cmd/cmd.go
--- old/chezmoi-2.29.2/pkg/cmd/cmd.go   2023-01-15 15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/pkg/cmd/cmd.go   2023-01-19 21:40:03.000000000 +0100
@@ -29,7 +29,8 @@
 var (
        noArgs = []string(nil)
 
-       trailingSpaceRx = regexp.MustCompile(` +\n`)
+       deDuplicateErrorRx = regexp.MustCompile(`:\s+`)
+       trailingSpaceRx    = regexp.MustCompile(` +\n`)
 
        helps = make(map[string]*help)
 )
@@ -109,12 +110,28 @@
                if errors.As(err, &errExitCode) {
                        return int(errExitCode)
                }
-               fmt.Fprintf(os.Stderr, "chezmoi: %v\n", err)
+               fmt.Fprintf(os.Stderr, "chezmoi: %s\n", deDuplicateError(err))
                return 1
        }
        return 0
 }
 
+// deDuplicateError returns err's human-readable string with duplicate 
components
+// removed.
+func deDuplicateError(err error) string {
+       components := deDuplicateErrorRx.Split(err.Error(), -1)
+       seenComponents := make(map[string]struct{}, len(components))
+       uniqueComponents := make([]string, 0, len(components))
+       for _, component := range components {
+               if _, ok := seenComponents[component]; ok {
+                       continue
+               }
+               uniqueComponents = append(uniqueComponents, component)
+               seenComponents[component] = struct{}{}
+       }
+       return strings.Join(uniqueComponents, ": ")
+}
+
 // example returns command's example.
 func example(command string) string {
        help, ok := helps[command]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/cmd_test.go 
new/chezmoi-2.29.3/pkg/cmd/cmd_test.go
--- old/chezmoi-2.29.2/pkg/cmd/cmd_test.go      2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/cmd_test.go      2023-01-19 21:40:03.000000000 
+0100
@@ -1,6 +1,8 @@
 package cmd
 
 import (
+       "errors"
+       "strconv"
        "testing"
 
        "github.com/stretchr/testify/assert"
@@ -15,6 +17,51 @@
        chezmoi.Umask = chezmoitest.Umask
 }
 
+func TestDeDuplicateError(t *testing.T) {
+       for i, tc := range []struct {
+               errStr   string
+               expected string
+       }{
+               {
+                       errStr:   "",
+                       expected: "",
+               },
+               {
+                       errStr:   "a",
+                       expected: "a",
+               },
+               {
+                       errStr:   "a: a",
+                       expected: "a",
+               },
+               {
+                       errStr:   "a: b",
+                       expected: "a: b",
+               },
+               {
+                       errStr:   "a: a: b", //nolint:dupword
+                       expected: "a: b",
+               },
+               {
+                       errStr:   "a: b: b",
+                       expected: "a: b",
+               },
+               {
+                       errStr:   "a: b: c: b: a: d",
+                       expected: "a: b: c: d",
+               },
+               {
+                       errStr:   "a: b: a: b: c",
+                       expected: "a: b: c",
+               },
+       } {
+               t.Run(strconv.Itoa(i), func(t *testing.T) {
+                       actual := deDuplicateError(errors.New(tc.errStr))
+                       assert.Equal(t, tc.expected, actual)
+               })
+       }
+}
+
 func TestMustGetLongHelpPanics(t *testing.T) {
        assert.Panics(t, func() {
                mustLongHelp("non-existent-command")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/completioncmd.go 
new/chezmoi-2.29.3/pkg/cmd/completioncmd.go
--- old/chezmoi-2.29.2/pkg/cmd/completioncmd.go 2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/completioncmd.go 2023-01-19 21:40:03.000000000 
+0100
@@ -21,7 +21,7 @@
                Example:   example("completion"),
                RunE:      c.runCompletionCmd,
                Annotations: newAnnotations(
-                       doesNotRequireValidConfig,
+                       runsWithInvalidConfig,
                ),
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/config.go 
new/chezmoi-2.29.3/pkg/cmd/config.go
--- old/chezmoi-2.29.2/pkg/cmd/config.go        2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/config.go        2023-01-19 21:40:03.000000000 
+0100
@@ -157,6 +157,7 @@
        // Command configurations, not settable in the config file.
        apply           applyCmdConfig
        archive         archiveCmdConfig
+       chattr          chattrCmdConfig
        dump            dumpCmdConfig
        executeTemplate executeTemplateCmdConfig
        _import         importCmdConfig
@@ -1658,7 +1659,7 @@
        })
 
        // Read the config file.
-       if annotations.hasTag(doesNotRequireValidConfig) {
+       if annotations.hasTag(runsWithInvalidConfig) {
                if c.configFileAbsPathErr == nil {
                        _ = c.readConfig()
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/doctorcmd.go 
new/chezmoi-2.29.3/pkg/cmd/doctorcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/doctorcmd.go     2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/doctorcmd.go     2023-01-19 21:40:03.000000000 
+0100
@@ -152,8 +152,8 @@
                Long:    mustLongHelp("doctor"),
                RunE:    c.runDoctorCmd,
                Annotations: newAnnotations(
-                       doesNotRequireValidConfig,
                        runsCommands,
+                       runsWithInvalidConfig,
                ),
        }
 
@@ -490,7 +490,11 @@
                if filenameAbsPath != c.expected {
                        return checkResultFailed, fmt.Sprintf("found %s, 
expected %s", filenameAbsPath, c.expected)
                }
-               if _, err := system.ReadFile(filenameAbsPath); err != nil {
+               config, err := newConfig()
+               if err != nil {
+                       return checkResultError, err.Error()
+               }
+               if err := config.decodeConfigFile(filenameAbsPath, 
&config.ConfigFile); err != nil {
                        return checkResultError, fmt.Sprintf("%s: %v", 
filenameAbsPath, err)
                }
                fileInfo, err := system.Stat(filenameAbsPath)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/dumpconfigcmd.go 
new/chezmoi-2.29.3/pkg/cmd/dumpconfigcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/dumpconfigcmd.go 2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/dumpconfigcmd.go 2023-01-19 21:40:03.000000000 
+0100
@@ -10,9 +10,6 @@
                Example: example("dump-config"),
                Args:    cobra.NoArgs,
                RunE:    c.runDumpConfigCmd,
-               Annotations: newAnnotations(
-                       doesNotRequireValidConfig,
-               ),
        }
 
        flags := dumpConfigCmd.Flags()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/editconfigcmd.go 
new/chezmoi-2.29.3/pkg/cmd/editconfigcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/editconfigcmd.go 2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/editconfigcmd.go 2023-01-19 21:40:03.000000000 
+0100
@@ -13,10 +13,10 @@
                Args:    cobra.NoArgs,
                RunE:    c.runEditConfigCmd,
                Annotations: newAnnotations(
-                       doesNotRequireValidConfig,
                        modifiesConfigFile,
                        requiresConfigDirectory,
                        runsCommands,
+                       runsWithInvalidConfig,
                ),
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/generatecmd.go 
new/chezmoi-2.29.3/pkg/cmd/generatecmd.go
--- old/chezmoi-2.29.2/pkg/cmd/generatecmd.go   2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/generatecmd.go   2023-01-19 21:40:03.000000000 
+0100
@@ -19,7 +19,7 @@
                ValidArgs: []string{"install.sh"},
                RunE:      c.runGenerateCmd,
                Annotations: newAnnotations(
-                       doesNotRequireValidConfig,
+                       runsWithInvalidConfig,
                ),
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/initcmd.go 
new/chezmoi-2.29.3/pkg/cmd/initcmd.go
--- old/chezmoi-2.29.2/pkg/cmd/initcmd.go       2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/initcmd.go       2023-01-19 21:40:03.000000000 
+0100
@@ -4,11 +4,9 @@
        "errors"
        "fmt"
        "io/fs"
-       "net/url"
        "regexp"
        "runtime"
        "strconv"
-       "strings"
 
        "github.com/go-git/go-git/v5"
        "github.com/go-git/go-git/v5/plumbing"
@@ -43,55 +41,46 @@
        rx                *regexp.Regexp
        httpRepoGuessRepl string
        sshRepoGuessRepl  string
-       usernameGuessRepl string
 }{
        {
                rx:                regexp.MustCompile(`\A([-0-9A-Za-z]+)\z`),
                httpRepoGuessRepl: "https://github.com/$1/dotfiles.git";,
                sshRepoGuessRepl:  "g...@github.com:$1/dotfiles.git",
-               usernameGuessRepl: "$1",
        },
        {
                rx:                
regexp.MustCompile(`\A([-0-9A-Za-z]+)/([-0-9A-Za-z]+)(\.git)?\z`),
                httpRepoGuessRepl: "https://github.com/$1/$2.git";,
                sshRepoGuessRepl:  "g...@github.com:$1/$2.git",
-               usernameGuessRepl: "$1",
        },
        {
                rx:                
regexp.MustCompile(`\A([-.0-9A-Za-z]+)/([-0-9A-Za-z]+)\z`),
                httpRepoGuessRepl: "https://$1/$2/dotfiles.git";,
                sshRepoGuessRepl:  "git@$1:$2/dotfiles.git",
-               usernameGuessRepl: "$2",
        },
        {
                rx:                
regexp.MustCompile(`\A([-0-9A-Za-z]+)/([-0-9A-Za-z]+)/([-.0-9A-Za-z]+)\z`),
                httpRepoGuessRepl: "https://$1/$2/$3.git";,
                sshRepoGuessRepl:  "git@$1:$2/$3.git",
-               usernameGuessRepl: "$2",
        },
        {
                rx:                
regexp.MustCompile(`\A([-.0-9A-Za-z]+)/([-0-9A-Za-z]+)/([-0-9A-Za-z]+)(\.git)?\z`),
                httpRepoGuessRepl: "https://$1/$2/$3.git";,
                sshRepoGuessRepl:  "git@$1:$2/$3.git",
-               usernameGuessRepl: "$2",
        },
        {
                rx:                
regexp.MustCompile(`\A(https?://)([-.0-9A-Za-z]+)/([-0-9A-Za-z]+)/([-0-9A-Za-z]+)(\.git)?\z`),
                httpRepoGuessRepl: "$1$2/$3/$4.git",
                sshRepoGuessRepl:  "git@$2:$3/$4.git",
-               usernameGuessRepl: "$3",
        },
        {
                rx:                
regexp.MustCompile(`\Asr\.ht/~([a-z_][a-z0-9_-]+)\z`),
                httpRepoGuessRepl: "https://git.sr.ht/~$1/dotfiles";,
                sshRepoGuessRepl:  "g...@git.sr.ht:~$1/dotfiles",
-               usernameGuessRepl: "$1",
        },
        {
                rx:                
regexp.MustCompile(`\Asr\.ht/~([a-z_][a-z0-9_-]+)/([-0-9A-Za-z]+)\z`),
                httpRepoGuessRepl: "https://git.sr.ht/~$1/$2";,
                sshRepoGuessRepl:  "g...@git.sr.ht:~$1/$2",
-               usernameGuessRepl: "$1",
        },
 }
 
@@ -170,14 +159,14 @@
                                return err
                        }
                } else {
-                       var username, repoURLStr string
+                       var repoURLStr string
                        if c.init.guessRepoURL {
-                               username, repoURLStr = guessRepoURL(args[0], 
c.init.ssh)
+                               repoURLStr = guessRepoURL(args[0], c.init.ssh)
                        } else {
                                repoURLStr = args[0]
                        }
                        if useBuiltinGit {
-                               if err := c.builtinGitClone(username, 
repoURLStr, workingTreeRawPath); err != nil {
+                               if err := c.builtinGitClone(repoURLStr, 
workingTreeRawPath); err != nil {
                                        return err
                                }
                        } else {
@@ -199,16 +188,6 @@
                                                "--depth", 
strconv.Itoa(c.init.depth),
                                        )
                                }
-                               if c.init.guessRepoURL && 
(strings.HasPrefix(repoURLStr, "http://";) || strings.HasPrefix(repoURLStr, 
"https://";)) {
-                                       repoURL, err := url.Parse(repoURLStr)
-                                       if err != nil {
-                                               return err
-                                       }
-                                       if repoURL.User == nil {
-                                               repoURL.User = 
url.User(username)
-                                               repoURLStr = repoURL.String()
-                                       }
-                               }
                                args = append(args,
                                        repoURLStr,
                                        workingTreeRawPath.String(),
@@ -263,7 +242,7 @@
 }
 
 // builtinGitClone clones a repo using the builtin git command.
-func (c *Config) builtinGitClone(username, repoURLStr string, 
workingTreeRawPath chezmoi.AbsPath) error {
+func (c *Config) builtinGitClone(repoURLStr string, workingTreeRawPath 
chezmoi.AbsPath) error {
        endpoint, err := transport.NewEndpoint(repoURLStr)
        if err != nil {
                return err
@@ -299,12 +278,9 @@
                        return err
                }
                var basicAuth http.BasicAuth
-               if basicAuth.Username, err = c.readString("Username? ", 
&username); err != nil {
+               if basicAuth.Username, err = c.readString("Username? ", nil); 
err != nil {
                        return err
                }
-               if basicAuth.Username == "" {
-                       basicAuth.Username = username
-               }
                if basicAuth.Password, err = c.readPassword("Password? "); err 
!= nil {
                        return err
                }
@@ -365,21 +341,16 @@
 }
 
 // guessRepoURL guesses the user's username and repo from arg.
-func guessRepoURL(arg string, ssh bool) (username, repo string) {
+func guessRepoURL(arg string, ssh bool) string {
        for _, repoGuess := range repoGuesses {
-               if !repoGuess.rx.MatchString(arg) {
-                       continue
-               }
                switch {
+               case !repoGuess.rx.MatchString(arg):
+                       continue
                case ssh && repoGuess.sshRepoGuessRepl != "":
-                       repo = repoGuess.rx.ReplaceAllString(arg, 
repoGuess.sshRepoGuessRepl)
-                       return
+                       return repoGuess.rx.ReplaceAllString(arg, 
repoGuess.sshRepoGuessRepl)
                case !ssh && repoGuess.httpRepoGuessRepl != "":
-                       username = repoGuess.rx.ReplaceAllString(arg, 
repoGuess.usernameGuessRepl)
-                       repo = repoGuess.rx.ReplaceAllString(arg, 
repoGuess.httpRepoGuessRepl)
-                       return
+                       return repoGuess.rx.ReplaceAllString(arg, 
repoGuess.httpRepoGuessRepl)
                }
        }
-       repo = arg
-       return
+       return arg
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/initcmd_test.go 
new/chezmoi-2.29.3/pkg/cmd/initcmd_test.go
--- old/chezmoi-2.29.2/pkg/cmd/initcmd_test.go  2023-01-15 15:35:48.000000000 
+0100
+++ new/chezmoi-2.29.3/pkg/cmd/initcmd_test.go  2023-01-19 21:40:03.000000000 
+0100
@@ -17,7 +17,6 @@
                arg                 string
                expectedHTTPRepoURL string
                expectedSSHRepoURL  string
-               expectedUsername    string
        }{
                {
                        arg:                 
"g...@github.com:user/dotfiles.git",
@@ -28,84 +27,70 @@
                        arg:                 "codeberg.org/user",
                        expectedHTTPRepoURL: 
"https://codeberg.org/user/dotfiles.git";,
                        expectedSSHRepoURL:  
"g...@codeberg.org:user/dotfiles.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "codeberg.org/user/dots",
                        expectedHTTPRepoURL: 
"https://codeberg.org/user/dots.git";,
                        expectedSSHRepoURL:  "g...@codeberg.org:user/dots.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "gitlab.com/user",
                        expectedHTTPRepoURL: 
"https://gitlab.com/user/dotfiles.git";,
                        expectedSSHRepoURL:  
"g...@gitlab.com:user/dotfiles.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "gitlab.com/user/dots",
                        expectedHTTPRepoURL: "https://gitlab.com/user/dots.git";,
                        expectedSSHRepoURL:  "g...@gitlab.com:user/dots.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "gitlab.com/user/dots.git",
                        expectedHTTPRepoURL: "https://gitlab.com/user/dots.git";,
                        expectedSSHRepoURL:  "g...@gitlab.com:user/dots.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "http://gitlab.com/user/dots.git";,
                        expectedHTTPRepoURL: "http://gitlab.com/user/dots.git";,
                        expectedSSHRepoURL:  "g...@gitlab.com:user/dots.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "https://gitlab.com/user/dots.git";,
                        expectedHTTPRepoURL: "https://gitlab.com/user/dots.git";,
                        expectedSSHRepoURL:  "g...@gitlab.com:user/dots.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "sr.ht/~user_name",
                        expectedHTTPRepoURL: 
"https://git.sr.ht/~user_name/dotfiles";,
                        expectedSSHRepoURL:  
"g...@git.sr.ht:~user_name/dotfiles",
-                       expectedUsername:    "user_name",
                },
                {
                        arg:                 "sr.ht/~user_name/dots",
                        expectedHTTPRepoURL: 
"https://git.sr.ht/~user_name/dots";,
                        expectedSSHRepoURL:  "g...@git.sr.ht:~user_name/dots",
-                       expectedUsername:    "user_name",
                },
                {
                        arg:                 "user",
                        expectedHTTPRepoURL: 
"https://github.com/user/dotfiles.git";,
                        expectedSSHRepoURL:  
"g...@github.com:user/dotfiles.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "user/dots",
                        expectedHTTPRepoURL: "https://github.com/user/dots.git";,
                        expectedSSHRepoURL:  "g...@github.com:user/dots.git",
-                       expectedUsername:    "user",
                },
                {
                        arg:                 "user/dots.git",
                        expectedHTTPRepoURL: "https://github.com/user/dots.git";,
                        expectedSSHRepoURL:  "g...@github.com:user/dots.git",
-                       expectedUsername:    "user",
                },
        } {
                t.Run(tc.arg, func(t *testing.T) {
                        ssh := false
-                       actualHTTPUsername, actualHTTPRepoURL := 
guessRepoURL(tc.arg, ssh)
-                       assert.Equal(t, tc.expectedUsername, 
actualHTTPUsername, "HTTPUsername")
+                       actualHTTPRepoURL := guessRepoURL(tc.arg, ssh)
                        assert.Equal(t, tc.expectedHTTPRepoURL, 
actualHTTPRepoURL, "HTTPRepoURL")
 
                        ssh = true
-                       actualSSHUsername, actualSSHRepoURL := 
guessRepoURL(tc.arg, ssh)
-                       assert.Equal(t, "", actualSSHUsername, "SSHUsername")
+                       actualSSHRepoURL := guessRepoURL(tc.arg, ssh)
                        assert.Equal(t, tc.expectedSSHRepoURL, 
actualSSHRepoURL, "SSHRepoURL")
                })
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/keepassxctemplatefuncs.go 
new/chezmoi-2.29.3/pkg/cmd/keepassxctemplatefuncs.go
--- old/chezmoi-2.29.2/pkg/cmd/keepassxctemplatefuncs.go        2023-01-15 
15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/pkg/cmd/keepassxctemplatefuncs.go        2023-01-19 
21:40:03.000000000 +0100
@@ -141,6 +141,8 @@
                        return nil, err
                }
                c.Keepassxc.password = password
+       }
+       if c.Keepassxc.password != "" {
                cmd.Stdin = bytes.NewBufferString(c.Keepassxc.password + "\n")
        } else {
                cmd.Stdin = os.Stdin
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/chezmoi-2.29.2/pkg/cmd/testdata/scripts/chattr.txtar 
new/chezmoi-2.29.3/pkg/cmd/testdata/scripts/chattr.txtar
--- old/chezmoi-2.29.2/pkg/cmd/testdata/scripts/chattr.txtar    2023-01-15 
15:35:48.000000000 +0100
+++ new/chezmoi-2.29.3/pkg/cmd/testdata/scripts/chattr.txtar    2023-01-19 
21:40:03.000000000 +0100
@@ -128,6 +128,13 @@
 exec chezmoi chattr --dry-run --verbose +executable $HOME${/}.file
 cmp stdout golden/chattr-diff
 
+# test that chezmoi chattr --recursive noexact recurses into subdirectories
+exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir
+exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir/exact_subdir
+exec chezmoi chattr --recursive noexact $HOME${/}.dir
+exists $CHEZMOISOURCEDIR/readonly_dot_dir
+exists $CHEZMOISOURCEDIR/readonly_dot_dir/subdir
+
 -- golden/chattr-diff --
 diff --git a/dot_file b/executable_dot_file
 rename from dot_file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/chezmoi-2.29.2/pkg/cmd/testdata/scripts/issue2695.txtar 
new/chezmoi-2.29.3/pkg/cmd/testdata/scripts/issue2695.txtar
--- old/chezmoi-2.29.2/pkg/cmd/testdata/scripts/issue2695.txtar 1970-01-01 
01:00:00.000000000 +0100
+++ new/chezmoi-2.29.3/pkg/cmd/testdata/scripts/issue2695.txtar 2023-01-19 
21:40:03.000000000 +0100
@@ -0,0 +1,40 @@
+# test that chezmoi status returns an error when the JSON config file is 
invalid
+! exec chezmoi status
+stderr 'invalid config'
+! stderr 'json.*json'
+
+# check that chezmoi doctor warns about invalid JSON config files
+! exec chezmoi doctor
+stdout 'error\s+config-file\s+.*invalid character'
+
+chhome home2/user
+
+# test that chezmoi status returns an error when the TOML config file is 
invalid
+! exec chezmoi status
+stderr 'invalid config'
+! stderr 'chezmoi\.toml.*chezmoi\.toml'
+
+# check that chezmoi doctor warns about invalid TOML config files
+! exec chezmoi doctor
+stdout 'error\s+config-file\s+.*incomplete number'
+
+chhome home3/user
+
+# test that chezmoi status returns an error when the YAML config file is 
invalid
+! exec chezmoi status
+stderr 'invalid config'
+! stderr 'chezmoi\.yaml.*chezmoi\.yaml'
+
+# check that chezmoi doctor warns about invalid YAML config files
+! exec chezmoi doctor
+stdout 'error\s+config-file\s+.*unmarshal errors'
+
+-- home/user/.config/chezmoi/chezmoi.json --
+{
+    "string": unquoted
+}
+-- home2/user/.config/chezmoi/chezmoi.toml --
+[example]
+    string = unquoted
+-- home3/user/.config/chezmoi/chezmoi.yaml --
+string

++++++ chezmoi.obsinfo ++++++
--- /var/tmp/diff_new_pack.hM5tAD/_old  2023-01-24 20:59:43.745534643 +0100
+++ /var/tmp/diff_new_pack.hM5tAD/_new  2023-01-24 20:59:43.745534643 +0100
@@ -1,5 +1,5 @@
 name: chezmoi
-version: 2.29.2
-mtime: 1673793348
-commit: b9536aae9100a2367e7b5baad8a7e251542c45bd
+version: 2.29.3
+mtime: 1674160803
+commit: fec55002b83b3688d13988055226aa716b0c7c14
 

++++++ vendor.tar.gz ++++++

Reply via email to