├── testdata ├── empty.txt └── zipme │ ├── .hidden │ ├── folder │ ├── file.pdf │ ├── file.png │ ├── file.rtf │ └── subfolder │ │ └── subfolder1 │ │ └── file.png │ ├── folder2 │ ├── file.txt │ ├── file1.txt │ └── file2.txt │ ├── folder3 │ └── file.txt │ ├── .ignore │ └── .ignore1 ├── .gitignore ├── .goreleaser.yml ├── .github └── workflows │ ├── release.yml │ └── codeql-analysis.yml ├── LICENSE ├── go.mod ├── README.md ├── pack_test.go ├── pack.go └── go.sum /testdata/empty.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/.hidden: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder/file.pdf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder/file.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder/file.rtf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder2/file.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder2/file1.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder2/file2.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/folder3/file.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/zipme/.ignore: -------------------------------------------------------------------------------- 1 | *.png 2 | folder2/ 3 | -------------------------------------------------------------------------------- /testdata/zipme/.ignore1: -------------------------------------------------------------------------------- 1 | *.pdf 2 | folder2/ 3 | -------------------------------------------------------------------------------- /testdata/zipme/folder/subfolder/subfolder1/file.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | *~ 4 | *.out 5 | vendor/ 6 | dist/ 7 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod download 4 | builds: 5 | - env: 6 | - CGO_ENABLED=0 7 | goos: 8 | - linux 9 | - darwin 10 | archives: 11 | - replacements: 12 | darwin: Darwin 13 | linux: Linux 14 | 386: i386 15 | amd64: x86_64 16 | checksum: 17 | name_template: 'checksums.txt' 18 | snapshot: 19 | name_template: "{{ .Tag }}-next" 20 | changelog: 21 | sort: asc 22 | filters: 23 | exclude: 24 | - '^testdata:' 25 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: 1.15 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v2 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ankit Pokhrel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ankitpokhrel/pack 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/git-lfs/wildmatch v1.0.4 7 | github.com/mholt/archiver/v3 v3.5.1 8 | github.com/stretchr/testify v1.6.1 9 | github.com/urfave/cli/v2 v2.11.1 10 | ) 11 | 12 | require ( 13 | github.com/andybalholm/brotli v1.0.4 // indirect 14 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect 17 | github.com/golang/snappy v0.0.4 // indirect 18 | github.com/klauspost/compress v1.15.9 // indirect 19 | github.com/klauspost/pgzip v1.2.5 // indirect 20 | github.com/kr/text v0.2.0 // indirect 21 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 22 | github.com/nwaples/rardecode v1.1.3 // indirect 23 | github.com/pierrec/lz4/v4 v4.1.15 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 26 | github.com/ulikunitz/xz v0.5.14 // indirect 27 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect 28 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 29 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect 30 | gopkg.in/yaml.v3 v3.0.1 // indirect 31 | ) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Pack 2 | 3 | A small utility to create archives while ignoring any hidden or unnecessary files and folders. It uses git style pattern matching. 4 | 5 | ### Installation 6 | 7 | Install the runnable binary to your `$GOPATH/bin`. 8 | 9 | ```sh 10 | go get github.com/ankitpokhrel/pack 11 | ``` 12 | 13 | Or, download the [latest release](https://github.com/ankitpokhrel/pack/releases). 14 | 15 | ### Usage 16 | 17 | ```sh 18 | NAME: 19 | pack - Pack create archives while ignoring any hidden or unnecessary files and folders 20 | 21 | USAGE: 22 | pack [global options] command [command options] 23 | 24 | COMMANDS: 25 | help, h Shows a list of commands or help for one command 26 | 27 | GLOBAL OPTIONS: 28 | --ignore value, --ig value Ignore list from given files 29 | --help, -h show help (default: false) 30 | ``` 31 | 32 | ### Example 33 | 34 | Given a `.gitignore` and `.ignoremetoo` file: 35 | ```sh 36 | $ cat .gitignore 37 | 38 | vendor/ 39 | *.swp 40 | *~ 41 | 42 | $ cat .ignoremetoo 43 | 44 | file.txt 45 | *.png 46 | ``` 47 | 48 | The following command will create `destination.zip` file by ignoring all patterns mentioned in `.gitignore` and `.ignoremetoo`. 49 | ```sh 50 | $ pack --ig .gitignore --ig .ignoremetoo /path/to/file-to-compress /path/to/destination.zip 51 | ``` 52 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 12 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | # Override automatic language detection by changing the below list 21 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 22 | language: ['go'] 23 | # Learn more... 24 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | with: 30 | # We must fetch at least the immediate parents so that if this is 31 | # a pull request then we can checkout the head. 32 | fetch-depth: 2 33 | 34 | # If this run was triggered by a pull request event, then checkout 35 | # the head of the pull request instead of the merge commit. 36 | - run: git checkout HEAD^2 37 | if: ${{ github.event_name == 'pull_request' }} 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v1 42 | with: 43 | languages: ${{ matrix.language }} 44 | 45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 46 | # If this step fails, then you should remove it and run the build manually (see below) 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v1 49 | 50 | # ℹ️ Command-line programs to run using the OS shell. 51 | # 📚 https://git.io/JvXDl 52 | 53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 54 | # and modify them (or add more) to build your code if your project 55 | # uses a compiled language 56 | 57 | #- run: | 58 | # make bootstrap 59 | # make release 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v1 63 | -------------------------------------------------------------------------------- /pack_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_files(t *testing.T) { 11 | t.Parallel() 12 | 13 | accept, err := files("./testdata/zipme", []string{"./testdata/zipme/.ignore", "./testdata/zipme/.ignore1"}) 14 | 15 | assert.Nil(t, err) 16 | assert.Equal(t, 9, len(accept)) 17 | } 18 | 19 | func Test_ignore(t *testing.T) { 20 | t.Parallel() 21 | 22 | avoid := ignore([]string{"./testdata/zipme/.ignore", "./testdata/zipme/.ignore1", "./testdata/unknown"}) 23 | keys := []string{"*.png", "*.pdf", "folder2"} 24 | 25 | assert.Equal(t, 3, len(avoid)) 26 | 27 | for _, k := range keys { 28 | _, ok := avoid[k] 29 | assert.True(t, ok) 30 | } 31 | } 32 | 33 | func Test_isHidden(t *testing.T) { 34 | t.Parallel() 35 | 36 | cases := []struct { 37 | file string 38 | expected bool 39 | }{ 40 | {".hidden", true}, 41 | {"regular", false}, 42 | {"nothidden.", false}, 43 | {"nor.mal", false}, 44 | {"..hidden", true}, 45 | {".", true}, 46 | {"..", true}, 47 | {"", true}, 48 | } 49 | 50 | for _, tc := range cases { 51 | assert.Equal(t, tc.expected, isHidden(tc.file)) 52 | } 53 | } 54 | 55 | func Test_parsePath(t *testing.T) { 56 | t.Parallel() 57 | 58 | homeDir, err := os.UserHomeDir() 59 | assert.Nil(t, err) 60 | 61 | cases := []struct { 62 | file, expected string 63 | }{ 64 | {"/some/path/to/file.ext", "/some/path/to/file.ext"}, 65 | {"~/Desktop", homeDir + "/Desktop"}, 66 | {"/some/path/./to/../file.ext", "/some/path/file.ext"}, 67 | {"some/path/./with/dot.ext", "some/path/with/dot.ext"}, 68 | } 69 | 70 | for _, tc := range cases { 71 | assert.Equal(t, tc.expected, parsePath(tc.file)) 72 | } 73 | } 74 | 75 | func Test_isDir(t *testing.T) { 76 | t.Parallel() 77 | 78 | assert.True(t, isDir("./testdata")) 79 | assert.False(t, isDir("./testdata/empty.txt")) 80 | } 81 | 82 | func Test_createRootDir(t *testing.T) { 83 | t.Parallel() 84 | 85 | tmp := "/tmp/pack" 86 | 87 | path, err := createRootDir("./testdata/empty.txt", tmp) 88 | 89 | assert.Nil(t, err) 90 | assert.Equal(t, tmp+"/empty", path) 91 | assert.True(t, isDir(path)) 92 | 93 | _ = os.RemoveAll("/tmp/pack") 94 | } 95 | 96 | func Test_dupe(t *testing.T) { 97 | t.Parallel() 98 | 99 | src, dest, file := "./testdata", "/tmp/pack-dupe", "/empty.txt" 100 | 101 | err := dupe(src, dest) 102 | assert.Nil(t, err) 103 | assert.True(t, isDir(dest)) 104 | 105 | err = dupe(src+file, dest+file) 106 | assert.Nil(t, err) 107 | 108 | _, err = os.Stat(dest + file) 109 | assert.False(t, isDir(dest+file)) 110 | assert.True(t, !os.IsNotExist(err)) 111 | 112 | _ = os.RemoveAll("/tmp/pack-dupe") 113 | } 114 | -------------------------------------------------------------------------------- /pack.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/git-lfs/wildmatch" 13 | "github.com/mholt/archiver/v3" 14 | "github.com/urfave/cli/v2" 15 | ) 16 | 17 | type list map[string]struct{} 18 | 19 | var ignoreList cli.StringSlice 20 | 21 | func main() { 22 | app := &cli.App{ 23 | Name: "pack", 24 | Usage: "Pack create archives while ignoring any hidden or unnecessary files and folders", 25 | ArgsUsage: " ", 26 | Flags: []cli.Flag{ 27 | &cli.StringSliceFlag{ 28 | Name: "ignore", 29 | Aliases: []string{"ig"}, 30 | Usage: "Ignore list from given files", 31 | Destination: &ignoreList, 32 | }, 33 | }, 34 | Action: pack, 35 | } 36 | 37 | if err := app.Run(os.Args); err != nil && err != io.EOF { 38 | panic(err) 39 | } 40 | } 41 | 42 | func pack(c *cli.Context) error { 43 | if c.NArg() != 2 { 44 | return cli.ShowAppHelp(c) 45 | } 46 | 47 | src, dest := c.Args().Get(0), c.Args().Get(1) 48 | if src == "." || src == "./" { 49 | cwd, err := os.Getwd() 50 | if err != nil { 51 | return err 52 | } 53 | src = cwd 54 | } 55 | 56 | accept, err := files(src, ignoreList.Value()) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | tmp, err := ioutil.TempDir("", "pack") 62 | if err != nil { 63 | return err 64 | } 65 | defer os.RemoveAll(tmp) 66 | 67 | rootDir := accept[0] 68 | basePath, err := createRootDir(dest, tmp) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | for _, v := range accept { 74 | if v == rootDir { 75 | continue 76 | } 77 | 78 | newPath := basePath + strings.Replace(v, rootDir, "", 1) 79 | 80 | if err := dupe(v, newPath); err != nil { 81 | return err 82 | } 83 | } 84 | 85 | return archiver.Archive([]string{basePath}, dest) 86 | } 87 | 88 | func files(path string, ign []string) ([]string, error) { 89 | var accept []string 90 | 91 | avoid := ignore(ign) 92 | err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { 93 | if err != nil { 94 | return err 95 | } 96 | 97 | path = filepath.Clean(path) 98 | 99 | if isHidden(path) { 100 | if isDir(path) { 101 | return filepath.SkipDir 102 | } 103 | return nil 104 | } 105 | 106 | for pattern := range avoid { 107 | wm := wildmatch.NewWildmatch(pattern, wildmatch.Basename) 108 | 109 | if wm.Match(path) { 110 | if isDir(path) { 111 | return filepath.SkipDir 112 | } 113 | return nil 114 | } 115 | } 116 | 117 | accept = append(accept, path) 118 | 119 | return nil 120 | }) 121 | 122 | return accept, err 123 | } 124 | 125 | func ignore(files []string) list { 126 | var avoid = make(list) 127 | 128 | for _, file := range files { 129 | path := parsePath(file) 130 | 131 | func() { 132 | f, err := os.Open(path) 133 | if err != nil { 134 | return 135 | } 136 | defer f.Close() 137 | 138 | scanner := bufio.NewScanner(f) 139 | for scanner.Scan() { 140 | avoid[filepath.Clean(scanner.Text())] = struct{}{} 141 | } 142 | }() 143 | } 144 | 145 | return avoid 146 | } 147 | 148 | func isHidden(path string) bool { 149 | if len(path) == 0 { 150 | return true 151 | } 152 | 153 | base := filepath.Base(path) 154 | 155 | return len(base) > 0 && base[0] == '.' 156 | } 157 | 158 | func parsePath(path string) string { 159 | homeDir, err := os.UserHomeDir() 160 | if err != nil { 161 | return filepath.Clean(path) 162 | } 163 | return filepath.Clean(strings.ReplaceAll(path, "~", homeDir)) 164 | } 165 | 166 | func isDir(path string) bool { 167 | fi, err := os.Stat(path) 168 | if err != nil { 169 | return false 170 | } 171 | return fi.Mode().IsDir() 172 | } 173 | 174 | func createRootDir(dest, path string) (string, error) { 175 | name, ext := filepath.Base(dest), filepath.Ext(dest) 176 | 177 | basePath := path + "/" + strings.Replace(name, ext, "", 1) 178 | 179 | return basePath, exec.Command("mkdir", "-p", basePath).Run() 180 | } 181 | 182 | func dupe(src, dest string) error { 183 | if isDir(src) { 184 | return exec.Command("mkdir", "-p", dest).Run() 185 | } 186 | return exec.Command("cp", "-rf", src, dest).Run() 187 | } 188 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 2 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 3 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 4 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 5 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 6 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 11 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 12 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 13 | github.com/git-lfs/wildmatch v1.0.4 h1:Mj6LPnNZ6QSHLAAPDCH596pu6A/Z1xVm2Vk0+s3CtkY= 14 | github.com/git-lfs/wildmatch v1.0.4/go.mod h1:SdHAGnApDpnFYQ0vAxbniWR0sn7yLJ3QXo9RRfhn2ew= 15 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 16 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 17 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 18 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 19 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 20 | github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 21 | github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= 22 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 23 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 24 | github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= 25 | github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 26 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 27 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 28 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 29 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 30 | github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= 31 | github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= 32 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 33 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 34 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 35 | github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= 36 | github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 37 | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 38 | github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= 39 | github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 40 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 41 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 42 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 43 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 44 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 45 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 46 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 47 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 48 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 49 | github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg= 50 | github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 51 | github.com/urfave/cli/v2 v2.11.1 h1:UKK6SP7fV3eKOefbS87iT9YHefv7iB/53ih6e+GNAsE= 52 | github.com/urfave/cli/v2 v2.11.1/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= 53 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 54 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 55 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 56 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 57 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 58 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 59 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 60 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 61 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 62 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 63 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 64 | --------------------------------------------------------------------------------