diff --git a/.travis.yml b/.travis.yml index ae55811..a5f26c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - "1.11.x" + - "1.16.x" before_script: - go get -u github.com/mitchellh/gox diff --git a/cmd/ignore.go b/cmd/ignore.go new file mode 100644 index 0000000..10720ca --- /dev/null +++ b/cmd/ignore.go @@ -0,0 +1,133 @@ +// Copyright © 2016 Dropbox, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + + "github.com/pkg/xattr" + + "github.com/spf13/cobra" + + gitignore "github.com/sabhiram/go-gitignore" +) + +const ( + dropboxXattr string = "com.dropbox.attrs" + dropboxIgnoredXattr string = "com.dropbox.ignored" + ignoreFilesToShow int = 7 +) + +func getGitIgnorePaths(root, ignoreFilePath string) ([]string, error) { + gi, err := gitignore.CompileIgnoreFile(ignoreFilePath) + if err != nil { + return nil, err + } + + var targetedFiles []string + filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if gi.MatchesPath(path) && hasDropboxAttr(path) { + targetedFiles = append(targetedFiles, path) + } + return nil + }) + + return targetedFiles, nil +} + +func hasDropboxAttr(path string) bool { + if _, err := os.Stat(path); err == nil { + if _, err := xattr.Get(path, dropboxXattr); err == nil { + return true + } + } + return false +} + +func ignoreToggle(cmd *cobra.Command, args []string) (err error) { + + isGitignore, _ := cmd.Flags().GetBool("gitignore") + root := "." + + var targetedFiles []string + + if isGitignore { + if f, err := os.Stat(args[0]); err == nil && !f.IsDir() { + targetedFiles, err = getGitIgnorePaths(root, args[0]) + if err != nil { + return errors.New("the given file must be a .gitignore style file") + } + } else { + return errors.New("the given file must be an existing .gitignore style file") + } + } else { + if _, err := os.Stat(args[0]); err == nil && hasDropboxAttr(args[0]) { + targetedFiles = append(targetedFiles, args[0]) + } + } + + if len(targetedFiles) == 0 { + fmt.Println("No files found...") + return + } + + fmt.Println("Toggling ignore state on the following file(s):") + for idx, path := range targetedFiles { + if idx >= ignoreFilesToShow { + break + } + fmt.Println("\t- ", path) + } + if len(targetedFiles) >= ignoreFilesToShow { + fmt.Printf("And %d more...\n", len(targetedFiles)-ignoreFilesToShow) + } + + toggled := 0 + for _, path := range targetedFiles { + if _, err := xattr.Get(path, dropboxIgnoredXattr); err == nil { + if err := xattr.Remove(path, dropboxIgnoredXattr); err == nil { + toggled++ + } else { + panic(err) + } + } else { + if err := xattr.Set(path, dropboxIgnoredXattr, []byte{1}); err == nil { + toggled++ + } else { + panic(err) + } + } + } + + return +} + +// ignoreToggleCmd represents the ignoreToggle command +var ignoreToggleCmd = &cobra.Command{ + Use: "toggle-ignore [flags] ", + Short: "Ignore a file from Dropbox Sync", + Long: "Fully ignore a local file from syncing with Dropbox or a set of files defined in a gitignore style file", + Args: cobra.ExactArgs(1), + RunE: ignoreToggle, +} + +func init() { + RootCmd.AddCommand(ignoreToggleCmd) + ignoreToggleCmd.Flags().BoolP("gitignore", "g", false, "Toggle ignored files based on the contents of a .gitignore style file") +} diff --git a/go.mod b/go.mod index 7632f53..e8029c9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/dropbox/dbxcli -go 1.11 +go 1.16 require ( github.com/dropbox/dropbox-sdk-go-unofficial v1.0.1-0.20210112084502-47d7ce03959a @@ -9,6 +9,8 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e + github.com/pkg/xattr v0.4.3 // indirect + github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f // indirect github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85 github.com/spf13/pflag v1.0.3 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect diff --git a/go.sum b/go.sum index 9ce6096..c9545a8 100644 --- a/go.sum +++ b/go.sum @@ -114,15 +114,20 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e h1:Qa6dnn8DlasdXRnacluu8HzPts0S1I9zvvUPDbBnXFI= github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e/go.mod h1:waEya8ee1Ro/lgxpVhkJI4BVASzkm3UZqkx/cFJiYHM= +github.com/pkg/xattr v0.4.3 h1:5Jx4GCg5ABtqWZH8WLzeI4fOtM1HyX4RBawuCoua1es= +github.com/pkg/xattr v0.4.3/go.mod h1:sBD3RAqlr8Q+RC3FutZcikpT8nyDrIEEBw2J744gVWs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f h1:8P2MkG70G76gnZBOPGwmMIgwBb/rESQuwsJ7K8ds4NE= +github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85 h1:RghwryY75x76zKqO9v7NF+9lcmfW1/RNZBfqK4LSCKE= github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -239,6 +244,8 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -371,6 +378,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=