Compare commits

...

12 Commits

Author SHA1 Message Date
DataHearth 1241858027
chore(doc): update changelog 2022-05-23 13:19:37 +02:00
DataHearth fab9533da1
fix(stderr): add stderr to brew command output 2022-05-23 13:08:43 +02:00
DataHearth 1cfd84b8cf
fix(rm): remove dir even if not empty 2022-05-23 09:35:35 +02:00
DataHearth f526bf1f5d
refactor(cli): separate functions from CLI for lisibility 2022-05-23 09:09:59 +02:00
DataHearth 3af07f159e
chore(doc): update readme 2022-04-07 21:49:56 +02:00
DataHearth 638e0ff3a2
refactor(logging): drop pterm 2022-04-07 21:44:21 +02:00
DataHearth 35d8210ca9
fix(git): use git binary for "git add" 2022-04-07 21:43:50 +02:00
DataHearth 584fe09fe2
feat(index): add indexing system 2022-04-07 21:43:26 +02:00
DataHearth a85165e8fe fix(git): deleted files are not pushed 2022-03-01 19:52:02 +01:00
DataHearth 94b2838f7e fix(git): add error handling and repo URL from config 2022-03-01 12:31:35 +01:00
DataHearth 71526ec28d feat(cli): add git push option with message 2022-02-28 23:41:21 +01:00
DataHearth 759604ebda refactor(archi): reduce base code to one struct 2022-02-28 22:36:26 +01:00
16 changed files with 630 additions and 453 deletions

View File

@ -2,13 +2,17 @@ storage:
# Where will be the repository folder located ? [DEFAULT: MacOS($TMPDIR/config-mapper) | Linux(/tmp/config-mapper)]
location: /path/to/folder
git:
# * by default, if ssh dict is set with its keys filled, I'll try to clone with SSH
# username used for commit author
name: USERNAME
# email used for commit author
email: EMAIL
repository: git@github.com:DataHearth/my-config.git
basic-auth:
username: USERNAME
# * NOTE: if you're having trouble with error "authentication required", you should maybe use a token access
# * In some cases, it's due to 2FA authentication enabled on the git hosting provided
password: TOKEN
# * by default, if ssh dict is set with its keys filled, I'll try to clone with SSH
ssh:
# path can be relative and can contain environment variables
private-key: /path/to/private/key

View File

@ -3,4 +3,6 @@
.gitignore
LICENSE
README.md
demo
CHANGELOG.md
.chglog
build

4
.gitignore vendored
View File

@ -1,3 +1,3 @@
.config-mapper.yml
demo
build
build
.DS_Store

View File

@ -3,8 +3,38 @@
## [Unreleased]
<a name="0.1.0"></a>
## 0.1.0 - 2022-02-27
<a name="v0.2.0"></a>
## [v0.2.0] - 2022-05-23
### Bug Fixes
- **config:** fix config path check
- **copy:** use io.Copy instead of custom copy
- **git:** use git binary for "git add"
- **git:** deleted files are not pushed
- **git:** add error handling and repo URL from config
- **rm:** remove dir even if not empty
- **stderr:** add stderr to brew command output
### Code Refactoring
- **archi:** reduce base code to one struct
- **cli:** separate functions from CLI for lisibility
- **config:** unmarshal configuration instead of raw read
- **logging:** drop pterm
### Features
- **cli:** add configuration-file persistant flag
- **cli:** add git push option with message
- **cli:** add save and load features
- **cli:** add init sub-command
- **cli:** add copy folder
- **cli:** add save command
- **cli:** implement pkgs installation
- **config:** update git configuration
- **config:** add yaml tags for yaml.v3
- **index:** add indexing system
<a name="v0.1.0"></a>
## v0.1.0 - 2022-02-27
### Bug Fixes
- **config:** fix config path check
- **copy:** use io.Copy instead of custom copy
@ -23,4 +53,5 @@
- **config:** add yaml tags for yaml.v3
[Unreleased]: https://github.com/DataHearth/config-mapper/compare/0.1.0...HEAD
[Unreleased]: https://github.com/DataHearth/config-mapper/compare/v0.2.0...HEAD
[v0.2.0]: https://github.com/DataHearth/config-mapper/compare/v0.1.0...v0.2.0

View File

@ -107,12 +107,12 @@ The same ignore flags are used in the `save` command.
## TO-DO
- [] load configuration though SSH
- [] save configuration though SSH
- [ ] load configuration though SSH
- [ ] save configuration though SSH
- add more storage options
- [] smb storage
- [] nfs storage
- [] zip
- [ ] smb storage
- [ ] nfs storage
- [ ] zip
## Known issues

View File

@ -1,12 +1,13 @@
package cmd
import (
"fmt"
"log"
"os"
"strconv"
"time"
mapper "github.com/datahearth/config-mapper/internal"
"github.com/pterm/pterm"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@ -28,89 +29,21 @@ var initCmd = &cobra.Command{
Short: "Initialize your configuration folder",
Long: `Initialize will retrieve your configuration folder from the source location and
copy it into the destination field`,
Run: func(cmd *cobra.Command, args []string) {
var config mapper.Configuration
if err := viper.Unmarshal(&config); err != nil {
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to decode configuration: %v\n", err)))
os.Exit(1)
}
logger.Println("initializing config-mapper folder from configuration...")
if _, err := mapper.OpenGitRepo(config.Storage.Git, config.Storage.Location); err != nil {
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to initialize folder: %v\n", err)))
os.Exit(1)
}
logger.Printf("repository initialized at \"%v\"\n", viper.GetString("storage.location"))
},
Run: initCommand,
}
var loadCmd = &cobra.Command{
Use: "load",
Short: "Load your configurations onto your system",
Long: `Load your files, folders and package managers deps configurations onto your new
onto your new system based on your configuration file`,
Run: func(cmd *cobra.Command, args []string) {
var config mapper.Configuration
if err := viper.Unmarshal(&config); err != nil {
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to decode configuration: %v\n", err)))
os.Exit(1)
}
if !viper.GetBool("load-disable-files") {
if err := mapper.LoadFiles(config.Files, config.Storage.Location); err != nil {
errLogger.Printf(pterm.Red(err))
os.Exit(1)
}
}
if !viper.GetBool("load-disable-folders") {
if err := mapper.LoadFolders(config.Folders, config.Storage.Location); err != nil {
errLogger.Printf(pterm.Red(err))
os.Exit(1)
}
}
if !viper.GetBool("load-disable-pkgs") {
if err := mapper.LoadPkgs(config.PackageManagers); err != nil {
errLogger.Printf(pterm.Red(err))
os.Exit(1)
}
}
},
Run: load,
}
var saveCmd = &cobra.Command{
Use: "save",
Short: "save your configurations onto your saved location",
Long: `Save your files, folders and package managers deps configurations onto your
saved location based on your configuration file`,
Run: func(cmd *cobra.Command, args []string) {
var config mapper.Configuration
if err := viper.Unmarshal(&config); err != nil {
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to decode configuration: %v\n", err)))
os.Exit(1)
}
if !viper.GetBool("save-disable-files") {
if err := mapper.SaveFiles(config.Files, config.Storage.Location); err != nil {
errLogger.Printf(pterm.Red(err))
os.Exit(1)
}
}
if !viper.GetBool("save-disable-folders") {
if err := mapper.SaveFolders(config.Folders, config.Storage.Location); err != nil {
errLogger.Printf(pterm.Red(err))
os.Exit(1)
}
}
if !viper.GetBool("save-disable-pkgs") {
if err := mapper.SavePkgs(config); err != nil {
errLogger.Printf(pterm.Red(err))
os.Exit(1)
}
}
},
Run: save,
}
func init() {
@ -132,9 +65,15 @@ func init() {
saveCmd.PersistentFlags().Bool("disable-files", false, "files will be ignored")
saveCmd.PersistentFlags().Bool("disable-folders", false, "folders will be ignored")
saveCmd.PersistentFlags().Bool("disable-pkgs", false, "package managers will be ignored")
saveCmd.Flags().BoolP("push", "p", false, "new configurations will be committed and pushed")
saveCmd.Flags().StringP("message", "m", strconv.FormatInt(time.Now().Unix(), 10), "combined with --push to set a commit message")
saveCmd.Flags().Bool("disable-index", false, "configuration index will not be updated")
viper.BindPFlag("save-disable-files", saveCmd.PersistentFlags().Lookup("disable-files"))
viper.BindPFlag("save-disable-folders", saveCmd.PersistentFlags().Lookup("disable-folders"))
viper.BindPFlag("save-disable-pkgs", saveCmd.PersistentFlags().Lookup("disable-pkgs"))
viper.BindPFlag("push", saveCmd.Flags().Lookup("push"))
viper.BindPFlag("disable-index-update", saveCmd.Flags().Lookup("disable-index"))
viper.BindPFlag("message", saveCmd.Flags().Lookup("message"))
}
func Execute() {
@ -143,3 +82,113 @@ func Execute() {
os.Exit(1)
}
}
func save(cmd *cobra.Command, args []string) {
var c mapper.Configuration
if err := viper.Unmarshal(&c); err != nil {
mapper.PrintError("failed to decode configuration: %v\n", err)
os.Exit(1)
}
indexer, err := mapper.NewIndexer(c.Storage.Path)
if err != nil {
mapper.PrintError("failed to open the indexer: %v\n", err)
os.Exit(1)
}
r, err := mapper.NewRepository(c.Storage.Git, c.Storage.Path)
if err != nil {
mapper.PrintError("failed to open repository at %s: %v\n", c.Storage.Path, err)
os.Exit(1)
}
el := mapper.NewItemsActions(nil, c.Storage.Path, r, indexer)
if !viper.GetBool("save-disable-files") {
el.AddItems(c.Files)
}
if !viper.GetBool("save-disable-folders") {
el.AddItems(c.Folders)
}
el.Action("save")
if !viper.GetBool("save-disable-pkgs") {
if err := mapper.SavePkgs(c); err != nil {
mapper.PrintError(err.Error())
os.Exit(1)
}
}
if err := el.CleanUp(indexer.RemovedLines()); err != nil {
mapper.PrintError("failed to clean repository: %v\n", err)
os.Exit(1)
}
if viper.GetBool("push") {
color.Blue("# Pushing items")
if err := r.PushChanges(viper.GetString("message"), indexer.Lines(), indexer.RemovedLines()); err != nil {
mapper.PrintError("failed to push changes to repository: %v\n", err)
os.Exit(1)
}
color.Green("Items pushed")
}
}
func load(cmd *cobra.Command, args []string) {
var c mapper.Configuration
if err := viper.Unmarshal(&c); err != nil {
mapper.PrintError("failed to decode configuration: %v\n", err)
os.Exit(1)
}
i, err := mapper.NewIndexer(c.Storage.Path)
if err != nil {
mapper.PrintError("failed to open the indexer: %v\n", err)
os.Exit(1)
}
r, err := mapper.NewRepository(c.Storage.Git, c.Storage.Path)
if err != nil {
mapper.PrintError("failed to open repository at %s: %v\n", c.Storage.Path, err)
os.Exit(1)
}
el := mapper.NewItemsActions(nil, c.Storage.Path, r, i)
if !viper.GetBool("load-disable-files") {
el.AddItems(c.Files)
}
if !viper.GetBool("load-disable-folders") {
el.AddItems(c.Folders)
}
el.Action("load")
if !viper.GetBool("load-disable-pkgs") {
if err := mapper.LoadPkgs(c.PackageManagers); err != nil {
mapper.PrintError(err.Error())
os.Exit(1)
}
}
}
func initCommand(cmd *cobra.Command, args []string) {
var config mapper.Configuration
if err := viper.Unmarshal(&config); err != nil {
mapper.PrintError("failed to decode configuration: %v\n", err)
os.Exit(1)
}
logger.Println("initializing config-mapper folder from configuration...")
if _, err := mapper.NewRepository(config.Storage.Git, config.Storage.Path); err != nil {
mapper.PrintError("failed to initialize folder: %v\n", err)
os.Exit(1)
}
logger.Printf("repository initialized at \"%v\"\n", viper.GetString("storage.location"))
}

9
go.mod
View File

@ -4,7 +4,6 @@ go 1.17
require (
github.com/go-git/go-git/v5 v5.4.2
github.com/pterm/pterm v0.12.37
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
@ -14,23 +13,22 @@ require (
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/atomicgo/cursor v0.0.1 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/color v1.13.0
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/gookit/color v1.4.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
@ -38,7 +36,6 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect

31
go.sum
View File

@ -49,12 +49,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k=
github.com/MarvinJWendt/testza v0.2.12 h1:/PRp/BF+27t2ZxynTiqj0nyND5PbOtfJS0SuTuxmgeg=
github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
@ -77,8 +71,6 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@ -126,6 +118,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
@ -225,8 +218,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
@ -281,8 +272,6 @@ github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgy
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -303,15 +292,15 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
@ -356,15 +345,6 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU=
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.37 h1:QGOyuaDUmY3yTbP0k6i0uPNqNHA9YofEBQDy0tIyKTA=
github.com/pterm/pterm v0.12.37/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -406,8 +386,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
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=
@ -614,14 +592,11 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -6,7 +6,6 @@ import (
"path"
"strings"
"github.com/pterm/pterm"
"github.com/spf13/viper"
)
@ -23,14 +22,16 @@ type ItemLocation struct {
}
type Storage struct {
Location string `mapstructure:"location" yaml:"location"`
Git Git `mapstructure:"git" yaml:"git"`
Path string `mapstructure:"location" yaml:"location"`
Git Git `mapstructure:"git" yaml:"git"`
}
type Git struct {
Repository string `mapstructure:"repository" yaml:"repository"`
BasicAuth BasicAuth `mapstructure:"basic-auth" yaml:"basic-auth"`
SSH Ssh `mapstructure:"ssh" yaml:"ssh"`
URL string `mapstructure:"repository" yaml:"repository"`
Name string `mapstructure:"name" yaml:"name"`
Email string `mapstructure:"email" yaml:"email"`
BasicAuth BasicAuth `mapstructure:"basic-auth" yaml:"basic-auth"`
SSH Ssh `mapstructure:"ssh" yaml:"ssh"`
}
type BasicAuth struct {
@ -80,9 +81,9 @@ func InitConfig() {
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
errLogger.Println(pterm.Red(err))
PrintError(err.Error())
} else {
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to read config: %v\n", err)))
PrintError("failed to read config: %v\n", err)
}
os.Exit(1)

View File

@ -1,99 +0,0 @@
package mapper
import (
"errors"
"fmt"
"os"
"path"
"github.com/pterm/pterm"
)
var (
ErrCopy = errors.New("failed to copy some files")
ErrUnsupportedOS = errors.New("unsupported OS. Please, contact the maintainer")
)
func LoadFiles(files []ItemLocation, location string) error {
pterm.DefaultSection.Println("Save files into saved location")
haveErr := false
p, _ := pterm.DefaultProgressbar.WithTotal(len(files)).Start()
for _, f := range files {
src, dst, err := configPaths(f, location)
if err != nil {
if err == ErrUnsupportedOS {
return err
}
pterm.Error.Println(fmt.Sprintf("failed to destination resolve path \"%s\": %v", f.Linux, err))
haveErr = true
continue
}
if err := os.MkdirAll(path.Dir(dst), 0755); err != nil {
pterm.Error.Printfln(fmt.Sprintf("failed to create directory architecture for destination path \"%s\": %v", dst, err))
haveErr = true
continue
}
p.UpdateTitle(fmt.Sprintf("copying %s", src))
if err := copyFile(src, dst); err != nil {
pterm.Error.Println(fmt.Sprintf("failed to load file from \"%s\" to \"%s\": %v", src, dst, err))
haveErr = true
continue
}
pterm.Success.Println(fmt.Sprintf("%s copied", src))
p.Increment()
}
p.Stop()
if haveErr {
return ErrCopy
}
return nil
}
func SaveFiles(files []ItemLocation, location string) error {
haveErr := false
pterm.DefaultSection.Println("Save files into saved location")
p, _ := pterm.DefaultProgressbar.WithTotal(len(files)).Start()
for _, f := range files {
dst, src, err := configPaths(f, location)
if err != nil {
if err == ErrUnsupportedOS {
return err
}
pterm.Error.Println(fmt.Sprintf("failed to destination resolve path \"%s\": %v", f.Linux, err))
haveErr = true
continue
}
p.UpdateTitle(fmt.Sprintf("copying \"%s\"", src))
if err := os.MkdirAll(path.Dir(dst), 0755); err != nil {
pterm.Error.Printfln(fmt.Sprintf("failed to create directory architecture for destination path \"%s\": %v", dst, err))
haveErr = true
continue
}
if err := copyFile(src, dst); err != nil {
pterm.Error.Println(fmt.Sprintf("failed to load file from \"%s\" to \"%s\": %v", src, dst, err))
haveErr = true
continue
}
pterm.Success.Println(fmt.Sprintf("\"%s\" copied", src))
p.Increment()
}
p.Stop()
if haveErr {
return ErrCopy
}
return nil
}

View File

@ -1,150 +0,0 @@
package mapper
import (
"errors"
"fmt"
"os"
"github.com/pterm/pterm"
)
var ErrFolderCopy = errors.New("failed to copy some folders")
func LoadFolders(folders []ItemLocation, location string) error {
haveErr := false
pterm.DefaultSection.Println("Load folders into saved location")
p, _ := pterm.DefaultProgressbar.WithTotal(len(folders)).Start()
for _, f := range folders {
src, dst, err := configPaths(f, location)
if err != nil {
if err == ErrUnsupportedOS {
return err
}
pterm.Error.Println(fmt.Sprintf("failed to destination resolve path \"%s\": %v", f.Linux, err))
haveErr = true
continue
}
s, err := os.Stat(src)
if err != nil {
pterm.Error.Println(fmt.Sprintf("failed to check if source path is a folder \"%s\": %v", src, err))
haveErr = true
continue
}
if !s.IsDir() {
pterm.Error.Println(fmt.Sprintf("source path is a file \"%s\"", src))
haveErr = true
continue
}
p.UpdateTitle(fmt.Sprintf("copying folder \"%s\"", src))
if err := os.MkdirAll(dst, 0755); err != nil {
pterm.Error.Printfln(fmt.Sprintf("failed to create directory architecture for destination path \"%s\": %v", dst, err))
haveErr = true
continue
}
if err := copyFolder(src, dst); err != nil {
pterm.Error.Println(fmt.Sprintf("failed to load folder from \"%s\" to \"%s\": %v", src, dst, err))
haveErr = true
continue
}
pterm.Success.Println(fmt.Sprintf("\"%s\" copied", src))
p.Increment()
}
p.Stop()
if haveErr {
return ErrCopy
}
return nil
}
func SaveFolders(folders []ItemLocation, location string) error {
haveErr := false
pterm.DefaultSection.Println("Save folders into saved location")
p, _ := pterm.DefaultProgressbar.WithTotal(len(folders)).Start()
for _, f := range folders {
dst, src, err := configPaths(f, location)
if err != nil {
if err == ErrUnsupportedOS {
return err
}
pterm.Error.Println(fmt.Sprintf("failed to destination resolve path \"%s\": %v", f.Linux, err))
haveErr = true
continue
}
s, err := os.Stat(src)
if err != nil {
pterm.Error.Println(fmt.Sprintf("failed to check if source path is a folder \"%s\": %v", src, err))
haveErr = true
continue
}
if !s.IsDir() {
pterm.Error.Println(fmt.Sprintf("source path is a file \"%s\"", src))
haveErr = true
continue
}
p.UpdateTitle(fmt.Sprintf("copying folder \"%s\"", src))
if err := os.MkdirAll(dst, 0755); err != nil {
pterm.Error.Printfln(fmt.Sprintf("failed to create directory architecture for destination path \"%s\": %v", dst, err))
haveErr = true
continue
}
if err := copyFolder(src, dst); err != nil {
pterm.Error.Println(fmt.Sprintf("failed to save folder from \"%s\" to \"%s\": %v", src, dst, err))
haveErr = true
continue
}
pterm.Success.Println(fmt.Sprintf("\"%s\" copied", src))
p.Increment()
}
p.Stop()
if haveErr {
return ErrCopy
}
return nil
}
func copyFolder(src, dst string) error {
var haveErr bool
items, err := os.ReadDir(src)
if err != nil {
return err
}
for _, i := range items {
itemName := i.Name()
srcItem := fmt.Sprintf("%s/%s", src, itemName)
dstItem := fmt.Sprintf("%s/%s", dst, itemName)
if i.IsDir() {
os.Mkdir(dstItem, i.Type().Perm())
copyFolder(srcItem, dstItem)
continue
}
if err := copyFile(srcItem, dstItem); err != nil {
haveErr = true
continue
}
}
if haveErr {
return ErrFolderCopy
}
return nil
}

View File

@ -3,8 +3,11 @@ package mapper
import (
"errors"
"os"
"os/exec"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
@ -15,56 +18,150 @@ var (
ErrInvalidEnv = errors.New("found invalid environment variable in path")
)
func OpenGitRepo(c Git, l string) (*git.Repository, error) {
s, err := os.Stat(l)
type RepositoryActions interface {
PushChanges(msg string, newLines, removedLines []string) error
GetWorktree() (*git.Worktree, error)
GetAuthor() *object.Signature
openRepository() error
}
type Repository struct {
auth transport.AuthMethod
repository *git.Repository
repoPath string
author author
url string
}
type author struct {
name string
email string
}
func NewRepository(config Git, repoPath string) (RepositoryActions, error) {
var auth transport.AuthMethod
if config.URL == "" {
return nil, errors.New("a repository URI is needed (either using GIT protocol or HTTPS)")
}
repoPath, err := absolutePath(repoPath)
if err != nil {
if os.IsNotExist(err) {
var auth transport.AuthMethod
if c.SSH.Passphrase != "" && c.SSH.PrivateKey != "" {
privateKey, err := absolutePath(c.SSH.PrivateKey)
if err != nil {
return nil, err
}
if _, err := os.Stat(privateKey); err != nil {
return nil, err
}
auth, err = ssh.NewPublicKeysFromFile("git", privateKey, c.SSH.Passphrase)
if err != nil {
return nil, err
}
} else {
auth = &http.BasicAuth{
Username: c.BasicAuth.Username,
Password: c.BasicAuth.Password,
}
}
repo, err := git.PlainClone(l, false, &git.CloneOptions{
URL: c.Repository,
Progress: os.Stdout,
Auth: auth,
})
if err != nil {
return nil, err
}
return repo, nil
}
return nil, err
}
if !s.IsDir() {
return nil, ErrDirIsFile
if config.SSH.Passphrase != "" && config.SSH.PrivateKey != "" {
privateKey, err := absolutePath(config.SSH.PrivateKey)
if err != nil {
return nil, err
}
if _, err := os.Stat(privateKey); err != nil {
return nil, err
}
auth, err = ssh.NewPublicKeysFromFile("git", privateKey, config.SSH.Passphrase)
if err != nil {
return nil, err
}
} else {
auth = &http.BasicAuth{
Username: config.BasicAuth.Username,
Password: config.BasicAuth.Password,
}
}
repo, err := git.PlainOpen(l)
if err != nil {
repo := &Repository{
auth: auth,
repository: nil,
repoPath: repoPath,
url: config.URL,
author: author{
name: config.Name,
email: config.Email,
},
}
if err := repo.openRepository(); err != nil {
return nil, err
}
return repo, nil
}
func (r *Repository) openRepository() error {
s, err := os.Stat(r.repoPath)
if err != nil {
if os.IsNotExist(err) {
repo, err := git.PlainClone(r.repoPath, false, &git.CloneOptions{
URL: r.url,
Progress: os.Stdout,
Auth: r.auth,
})
if err != nil {
return err
}
r.repository = repo
return nil
}
return err
}
if !s.IsDir() {
return ErrDirIsFile
}
repo, err := git.PlainOpen(r.repoPath)
if err != nil {
return err
}
w, err := repo.Worktree()
if err != nil {
return err
}
err = w.Pull(&git.PullOptions{
Auth: r.auth,
})
if err != nil && err != git.NoErrAlreadyUpToDate {
return err
}
r.repository = repo
return nil
}
func (r *Repository) PushChanges(msg string, newLines, removedLines []string) error {
w, err := r.repository.Worktree()
if err != nil {
return err
}
// TODO: investigated why w.AddWithOptions doesn't add removed files and sometimes .index
cmd := exec.Command("git", "add", ".")
cmd.Dir = r.repoPath
if err := cmd.Run(); err != nil {
return err
}
if _, err := w.Commit(msg, &git.CommitOptions{
Author: r.GetAuthor(),
}); err != nil {
return err
}
return r.repository.Push(&git.PushOptions{})
}
func (r *Repository) GetWorktree() (*git.Worktree, error) {
return r.repository.Worktree()
}
func (r *Repository) GetAuthor() *object.Signature {
return &object.Signature{
Name: r.author.name,
Email: r.author.email,
When: time.Now(),
}
}

107
internal/index.go Normal file
View File

@ -0,0 +1,107 @@
package mapper
import (
"fmt"
"io/fs"
"os"
"strings"
)
type Index struct {
lines []string
path string
perms fs.FileMode
repoPath string
removedLines []string
}
type Indexer interface {
Write(newLines []string) error
filter(configLines []string) map[string]bool
RemovedLines() []string
Lines() []string
}
func NewIndexer(repoPath string) (Indexer, error) {
perms := fs.FileMode(0755)
indexPath, err := absolutePath(fmt.Sprintf("%s/.index", repoPath))
if err != nil {
return nil, err
}
var l []string
s, err := os.Stat(indexPath)
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
l = []string{}
} else {
perms = s.Mode()
b, err := os.ReadFile(indexPath)
if err != nil {
return nil, err
}
l = strings.Split(string(b), "\n")
}
return &Index{
lines: l,
path: indexPath,
perms: perms,
repoPath: repoPath,
removedLines: []string{},
}, nil
}
func (i *Index) RemovedLines() []string {
return i.removedLines
}
func (i *Index) Lines() []string {
return i.lines
}
// filter removes lines that are no more used in configuration from the index
func (i *Index) filter(newLines []string) map[string]bool {
removedLines := []string{}
foundLines := map[string]bool{}
for _, nl := range newLines {
foundLines[nl] = true
}
for _, ml := range i.lines {
if _, ok := foundLines[ml]; !ok {
removedLines = append(removedLines, ml)
}
}
i.removedLines = removedLines
return foundLines
}
// Write add lines stored in memory the .index file
func (i *Index) Write(newLines []string) error {
lines := i.filter(newLines)
linesNumber := len(lines)
var data []byte
index := 0
i.lines = []string{}
for path := range lines {
if index+1 == linesNumber {
data = append(data, []byte(fmt.Sprint(path))...)
} else {
data = append(data, []byte(fmt.Sprintln(path))...)
}
index += 1
i.lines = append(i.lines, path)
}
os.WriteFile(i.path, data, i.perms)
return nil
}

137
internal/items.go Normal file
View File

@ -0,0 +1,137 @@
package mapper
import (
"fmt"
"io/fs"
"os"
"path"
"strings"
"github.com/fatih/color"
"github.com/spf13/viper"
)
type Items struct {
locations []ItemLocation
storage string
repository RepositoryActions
indexer Indexer
}
type ItemsActions interface {
Action(action string)
AddItems(items []ItemLocation)
CleanUp(removedLines []string) error
}
func NewItemsActions(items []ItemLocation, storage string, repository RepositoryActions, indexer Indexer) ItemsActions {
if items == nil {
items = []ItemLocation{}
}
return &Items{
locations: items,
storage: storage,
repository: repository,
indexer: indexer,
}
}
func (e *Items) Action(a string) {
color.Blue("# %s", a)
newLines := []string{}
for i, l := range e.locations {
var src, dst string
storagePath, systemPath, err := configPaths(l, e.storage)
if err != nil {
PrintError("[%d] failed to resolve item paths \"%v\": %v", i, l, err)
continue
}
if a == "save" {
src = systemPath
dst = storagePath
} else {
src = storagePath
dst = systemPath
}
if err := os.MkdirAll(path.Dir(dst), 0755); err != nil {
PrintError("[%d] failed to create directory architecture for destination path \"%s\": %v", i, path.Dir(dst), err)
continue
}
s, err := os.Stat(src)
if err != nil {
PrintError("[%d] failed to check if source path is a folder \"%s\": %v", i, src, err)
continue
}
if s.IsDir() {
dstPerms := fs.FileMode(0755)
s, err := os.Stat(dst)
if err != nil {
if !os.IsNotExist(err) {
PrintError("[%d] failed to check if destination folder \"%s\" exists: %v", i, dst, err)
continue
}
} else {
dstPerms = s.Mode()
}
if err := os.Mkdir(dst, dstPerms); err != nil {
if !os.IsExist(err) {
PrintError("[%d] failed to create destination folder \"%s\": %v", i, dst, err)
continue
}
}
if err := copyFolder(src, dst); err != nil {
PrintError("[%d] failed to %s folder from \"%s\" to \"%s\": %v", i, a, src, dst, err)
continue
}
} else {
if err := copyFile(src, dst); err != nil {
PrintError("[%d] failed to %s file from \"%s\" to \"%s\": %v", i, a, src, dst, err)
continue
}
}
if a == "save" {
p, err := absolutePath(e.storage)
if err != nil {
PrintError("[%d] failed resolve absolute path from configuration storage: %v", i, err)
continue
}
newLines = append(newLines, strings.ReplaceAll(dst, p+"/", ""))
}
color.Green("[%d] %s copied", i, src)
}
if a == "save" && !viper.GetBool("disable-index-update") {
if err := e.indexer.Write(newLines); err != nil {
PrintError(err.Error())
os.Exit(1)
}
}
}
func (e *Items) AddItems(items []ItemLocation) {
e.locations = append(e.locations, items...)
}
func (e *Items) CleanUp(removedLines []string) error {
for _, l := range removedLines {
path, err := absolutePath(fmt.Sprintf("%s/%s", e.storage, l))
if err != nil {
return err
}
if err := os.RemoveAll(path); err != nil {
return fmt.Errorf("failed to remove item %s: %v", l, err)
}
}
return nil
}

View File

@ -8,7 +8,7 @@ import (
"os/exec"
"strings"
"github.com/pterm/pterm"
"github.com/fatih/color"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
@ -22,18 +22,18 @@ var (
)
func LoadPkgs(c PkgManagers) error {
pterm.DefaultSection.Println("Load folders into saved location")
color.Blue("# Load folders into saved location")
for _, pkg := range c.InstallationOrder {
switch pkg {
case "homebrew":
if err := installBrewPkgs(c.Homebrew); err != nil {
errLogger.Println(pterm.Red(err))
PrintError(err.Error())
return ErrFailedInstallation
}
case "apt":
if err := installAptPkgs(c.Aptitude); err != nil {
errLogger.Println(pterm.Red(err))
PrintError(err.Error())
return ErrFailedInstallation
}
}
@ -43,13 +43,13 @@ func LoadPkgs(c PkgManagers) error {
}
func SavePkgs(cfg Configuration) error {
pterm.DefaultSection.Println("Save user installed packages")
color.Blue("# Save user installed packages")
for _, pkg := range cfg.PackageManagers.InstallationOrder {
switch pkg {
case "homebrew":
if err := SaveBrewPkgs(cfg); err != nil {
errLogger.Println(pterm.Red(err))
PrintError(err.Error())
return ErrFailedSaving
}
case "apt":
@ -65,7 +65,7 @@ func SaveBrewPkgs(cfg Configuration) error {
return err
}
introSpinner, _ := pterm.DefaultSpinner.WithShowTimer(true).WithRemoveWhenDone(false).Start("Installing homebrew packages")
color.Blue("## Saving Homebrew packages")
o, err := exec.Command("brew", "leaves", "--installed-on-request").Output()
if err != nil {
@ -84,9 +84,7 @@ func SaveBrewPkgs(cfg Configuration) error {
return err
}
introSpinner.Stop()
introSpinner.Success("Packages intalled succesfully")
color.Green("Packages saved succesfully !")
return nil
}
@ -96,22 +94,21 @@ func installBrewPkgs(pkgs []string) error {
}
if len(pkgs) == 0 {
pterm.Println(pterm.Blue("homebrew: nothing to do"))
fmt.Println("homebrew: nothing to do")
return nil
}
cmd := exec.Command("brew", "install")
cmd.Args = append(cmd.Args, pkgs...)
introSpinner, _ := pterm.DefaultSpinner.WithShowTimer(true).WithRemoveWhenDone(false).Start("Installing homebrew packages")
color.Blue("## Installing Homebrew packages")
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
introSpinner.Stop()
introSpinner.SuccessPrinter.PrintOnErrorf("brew command failed", err)
PrintError("brew command failed: %v", err)
return err
}
introSpinner.Stop()
introSpinner.Success("Packages intalled succesfully")
color.Green("Packages intalled succesfully !")
return nil
}
@ -122,34 +119,21 @@ func installAptPkgs(pkgs []string) error {
}
if len(pkgs) == 0 {
pterm.Println(pterm.Blue("aptitude: nothing to do"))
fmt.Println("aptitude: nothing to do")
return nil
}
cmd := exec.Command("sudo", "apt-get", "install")
cmd.Args = append(cmd.Args, pkgs...)
introSpinner, _ := pterm.DefaultSpinner.WithShowTimer(true).WithRemoveWhenDone(false).Start("Installing aptitude packages")
color.Blue("## Installing aptitude packages")
chErr := make(chan error)
defer close(chErr)
go func(chErr chan error) {
if err := cmd.Run(); err != nil {
chErr <- err
return
}
chErr <- nil
}(chErr)
err := <-chErr
introSpinner.Stop()
if err != nil {
if err := cmd.Run(); err != nil {
PrintError("aptitude command failed: %v", err)
return err
}
introSpinner.Success("Packages intalled succesfully")
color.Green("Packages intalled succesfully !")
return nil
}

View File

@ -1,12 +1,15 @@
package mapper
import (
"errors"
"fmt"
"io"
"os"
"path"
"runtime"
"strings"
"github.com/fatih/color"
)
func absolutePath(p string) (string, error) {
@ -99,8 +102,47 @@ func configPaths(f ItemLocation, location string) (string, string, error) {
return "", "", err
}
default:
return "", "", ErrUnsupportedOS
return "", "", errors.New("unsupported OS. Please, contact the maintainer")
}
return src, dst, nil
}
func copyFolder(src, dst string) error {
items, err := os.ReadDir(src)
if err != nil {
return err
}
for _, i := range items {
itemName := i.Name()
srcItem := fmt.Sprintf("%s/%s", src, itemName)
dstItem := fmt.Sprintf("%s/%s", dst, itemName)
if i.IsDir() {
info, err := i.Info()
if err != nil {
return err
}
if err := os.MkdirAll(dstItem, info.Mode()); err != nil {
return err
}
if err := copyFolder(srcItem, dstItem); err != nil {
return err
}
continue
}
if err := copyFile(srcItem, dstItem); err != nil {
return err
}
}
return nil
}
func PrintError(err string, values ...interface{}) {
color.Error.Write([]byte(color.RedString(err+"\n", values...)))
}