start refactoring

This commit is contained in:
DataHearth 2024-01-08 19:08:41 +01:00
parent 2d07590dbf
commit 01932bdfc7
No known key found for this signature in database
GPG Key ID: E88FD356ACC5F3C4
20 changed files with 256 additions and 1190 deletions

View File

@ -1,12 +1,20 @@
# OPTIONAL: where your configuration is stored locally [default: $HOME/.config/config-mapper]
path: /path/to/folder
logging:
log-level: info # OPTIONAL: [default: info] available: debug, info, warn, error, fatal
time: true # OPTIONAL: [default: false] display time in logs
time-format: 02/01/2006 15:04:05 # OPTIONAL: [default: 02/01/2006 15:04:05] format of time displayed in logs
file: /path/to/logfile # OPTIONAL: [default: no file logging] path to log file
disable-console: false # OPTIONAL: [default: false] should logs be printed in the terminal
# ! REQUIRED: id used to identify your configuration folder (should be unique).
# * NOTE: you can generate a UUIDv4 using "config-mapper gen-uuid --write".
# * It will generate a UUIDv4 and write it to this file.
# * If you initialize your configuration folder using "config-mapper init",
# * this field automatically gets filled with a brand new UUIDv4.
uuid: 2893211a-fac2-42a6-8f9d-9a8f692a8f04
# ? OPTIONAL: where your configuration is stored locally.
# * DEFAULT: $HOME/.local/state/config-mapper
path: $HOME/.local/state/config-mapper
# ? OPTIONAL: log level
# * DEFAULT: info
# * OPTIONS: debug, info, warn, error
log-level: info
# ! REQUIRED: list of storage provider to use. At least one is required.
# * OPTIONS: git, (TODO: AWS, GCP, AZURE)
storage:
git:
pull: true # OPTIONAL: [default: false] should git repository be pulled before any operation
@ -29,51 +37,6 @@ storage:
- private-key: /path/to/private/key
passphrase: PASSPHRASE
# REQUIRED: list of items to save in configuration repository
# ! REQUIRED: list of paths
items:
- name: .zshrc # OPTIONAL: [default: name of last path segment in "(universal|darwin|windows|linux).local" key]
# REQUIRED (if any of "darwin|windows|linux" keys aren't set): universal location for all operating systems
universal:
# REQUIRED: path to local file
local: /path/to/file
# REQUIRED: path to remote file (absolute path where "/" is the root of the repository)
remote: /remote/path
# REQUIRED (if "universal" key is not set): location for darwin operating system
darwin:
# REQUIRED: path to local file
local: /path/to/file
# REQUIRED: path to remote file (absolute path where "/" is the root of the repository)
remote: /remote/path
# REQUIRED (if "universal" key is not set): location for windows operating system
windows:
# REQUIRED: path to local file
local: /path/to/file
# REQUIRED: path to remote file (absolute path where "/" is the root of the repository)
remote: /remote/path
# REQUIRED (if "universal" key is not set): location for linux operating system
linux:
# REQUIRED: path to local file
local: /path/to/file
# REQUIRED: path to remote file (absolute path where "/" is the root of the repository)
remote: /remote/path
package-managers:
# available: brew, pip (pip check also for pip3), cargo, apt, npm, go
installation-order:
- brew
brew:
- bat
- hexyl
- fd
- hyperfine
- diskus
- jq
- k9s
- go
- starship
- exa
- httpie
- neovim
- nmap
- pinentry
- zsh
- $HOME/.config/nvim

10
.gitignore vendored
View File

@ -1,6 +1,6 @@
.env
.DS_STORE
build
/.env
/.DS_STORE
/build
/dist
/.config-mapper.yml
dist/
.config-mapper.yml
config/

View File

@ -1,6 +1,9 @@
version: 1
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
@ -8,40 +11,19 @@ builds:
- linux
- windows
- darwin
goarch:
- "386"
- amd64
- arm
- arm64
archives:
- format: tar.gz
files:
- LICENSE
- README.md
- .config-mapper.yml.template
- CHANGELOG.md
name_template: >-
{{ .ProjectName }}_
{{ .Version }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
gitea_urls:
api: https://gitea.antoine-langlois.net/api/v1
download: https://gitea.antoine-langlois.net
release:
gitea:
owner: DataHearth
name: config-mapper
mode: append
checksum:
name_template: "checksums.txt"
snapshot:
name_template: "{{ incpatch .Version }}-next"
force_token: gitea
{{- if .Arm }}v{{ .Arm }}{{ end }}
format_overrides:
- goos: windows
format: zip

View File

@ -1,5 +0,0 @@
{
"files.associations": {
".config-mapper.yml.template": "yaml"
}
}

54
go.mod
View File

@ -1,53 +1,13 @@
module gitea.antoine-langlois.net/datahearth/config-mapper
go 1.21
go 1.21.5
require github.com/urfave/cli/v2 v2.25.7
require (
github.com/briandowns/spinner v1.23.0
github.com/go-git/go-git/v5 v5.4.2
github.com/sirupsen/logrus v1.9.3
github.com/spf13/viper v1.10.0
github.com/urfave/cli/v2 v2.25.7
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/google/uuid v1.5.0
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gopkg.in/yaml.v3 v3.0.1
)
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/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/color v1.13.0 // indirect
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/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.12 // 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-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.18 // 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/russross/blackfriday/v2 v2.1.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
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // 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.11.0 // indirect
golang.org/x/term v0.1.0 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

162
go.sum
View File

@ -1,171 +1,13 @@
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=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
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/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A=
github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
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=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
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/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

44
internal/config/main.go Normal file
View File

@ -0,0 +1,44 @@
package config
import (
"io"
"os"
cmLog "gitea.antoine-langlois.net/datahearth/config-mapper/internal/logger"
"github.com/google/uuid"
"gopkg.in/yaml.v3"
)
type Definition struct {
UUID uuid.UUID `yaml:"uuid"`
Path string `yaml:"path,omitempty"`
LogLevel string `yaml:"log-level,omitempty"`
Storage Storage `yaml:"storage"`
Items []string `yaml:"items"`
}
func Load(logger *cmLog.Logger) (*Definition, error) {
env := "$HOME/.config/config-mapper.yml"
if v := os.Getenv("CFG_MAPPER_CONFIG_PATH"); v != "" {
env = v
}
logger.Debug("Loading config from %s", env)
f, err := os.Open(env)
if err != nil {
return nil, err
}
defer f.Close()
cfg := Definition{}
d, err := io.ReadAll(f)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(d, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}

View File

@ -0,0 +1,15 @@
package config
type Storage struct {
Git interface{} `yaml:"git"`
AWS interface{}
GCP interface{}
}
type Git struct {
Repository string `yaml:"repository"`
Authentication struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"authentication"`
}

View File

@ -1,64 +0,0 @@
package internal
type Configuration struct {
Path string `yaml:"path"`
Storage Storage `yaml:"storage"`
Items []Item `yaml:"items"`
PackageManagers PkgManagers `yaml:"package-managers"`
Logging Logging `yaml:"logging"`
}
type Logging struct {
Level string `yaml:"log-level,omitempty"`
EnableTime bool `yaml:"time,omitempty"`
TimeFormat string `yaml:"time-format,omitempty"`
File string `yaml:"file,omitempty"`
DisableConsole bool `yaml:"disable-console"`
}
type Item struct {
Name string `yaml:"name"`
Universal ItemLocation `yaml:"universal"`
Darwin ItemLocation `yaml:"darwin"`
Linux ItemLocation `yaml:"linux"`
Windows ItemLocation `yaml:"windows"`
}
type ItemLocation struct {
Local string `yaml:"local"`
Remote string `yaml:"remote"`
}
type Storage struct {
Git Git `yaml:"git"`
}
type Git struct {
Repository string `yaml:"repository"`
Name string `yaml:"name"`
Email string `yaml:"email"`
BasicAuth BasicAuth `yaml:"basic"`
SshAuth []SshAuth `yaml:"ssh"`
Pull bool `yaml:"pull"`
}
type BasicAuth struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
}
type SshAuth struct {
PrivateKey string `yaml:"private-key"`
Passphrase string `yaml:"passphrase"`
}
type PkgManagers struct {
InstallationOrder []string `yaml:"installation-order"`
Brew []string `yaml:"brew"`
Apt []string `yaml:"apt"`
Cargo []string `yaml:"cargo"`
Pip []string `yaml:"pip"`
Npm []string `yaml:"npm"`
Go []string `yaml:"go"`
Nala []string `yaml:"nala"`
}

View File

@ -1,261 +0,0 @@
package internal
import (
"errors"
"fmt"
"os"
"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"
"github.com/sirupsen/logrus"
)
type Repository struct {
availableAuth []transport.AuthMethod
auth transport.AuthMethod
repository *git.Repository
repoPath string
url string
author *object.Signature
}
// NewRepository creates a new repository struct by either cloning or opening the repository
func NewRepository(config Git, repoPath string, clone bool) (*Repository, error) {
var auth []transport.AuthMethod = nil
if config.Repository == "" {
return nil, errors.New("a repository URI is needed (either using GIT protocol or HTTPS)")
}
repoPath, err := ResolvePath(repoPath)
if err != nil {
return nil, err
}
for i, c := range config.SshAuth {
sshAuth, err := getSSHAuthMethod(c)
if err != nil {
fmt.Printf("failed to create SSH authentication method for configuration n°%d: %v\n", i, err)
continue
}
if auth == nil {
auth = []transport.AuthMethod{}
}
auth = append(auth, sshAuth)
}
if len(auth) == 0 {
auth = append(auth, &http.BasicAuth{
Username: config.BasicAuth.Username,
Password: config.BasicAuth.Password,
})
}
repo := &Repository{
availableAuth: auth,
auth: nil,
repository: nil,
repoPath: repoPath,
url: config.Repository,
author: &object.Signature{
Name: config.Name,
Email: config.Email,
},
}
if clone {
if err := repo.cloneRepository(); err != nil {
return nil, err
}
} else {
if err := repo.openRepository(); err != nil {
return nil, err
}
}
return repo, nil
}
// openRepository opens the repository at the given path
func (r *Repository) openRepository() error {
repo, err := git.PlainOpen(r.repoPath)
if err != nil {
return err
}
w, err := repo.Worktree()
if err != nil {
return err
}
if r.availableAuth != nil {
pulled := false
for _, auth := range r.availableAuth {
err := w.Pull(&git.PullOptions{
Auth: auth,
})
if err != nil {
if err == git.NoErrAlreadyUpToDate {
pulled = true
break
} else if checkAuthErr(err) {
logrus.WithField("auth", auth.String()).Warn("failed to authenticate. Trying next auth if exists")
continue
}
return err
}
pulled = true
r.auth = auth
break
}
if !pulled {
return fmt.Errorf("authentication failed for git repository")
}
} else {
if err := w.Pull(&git.PullOptions{}); err != nil && err != git.NoErrAlreadyUpToDate {
return err
}
}
r.repository = repo
return nil
}
// PushChanges pushes changes to the remote repository
func (r *Repository) PushChanges(msg string, newLines, removedLines []string) error {
w, err := r.repository.Worktree()
if err != nil {
return err
}
status, err := w.Status()
if err != nil {
return err
}
for file := range status {
if _, err := w.Add(file); err != nil {
return err
}
}
author := r.author
author.When = time.Now()
if _, err := w.Commit(msg, &git.CommitOptions{
Author: author,
}); err != nil {
return err
}
return r.repository.Push(&git.PushOptions{
Auth: r.auth,
})
}
// FetchChanges fetches changes from the remote repository. If changes are fetched,
// the function returns true, otherwise false.
func (r *Repository) FetchChanges() (bool, error) {
if err := r.repository.Fetch(&git.FetchOptions{
Auth: r.auth,
}); err != nil {
if err == git.NoErrAlreadyUpToDate {
return false, nil
}
return false, err
}
return true, nil
}
// cloneRepository clones the repository into the given path
func (r *Repository) cloneRepository() error {
var repo *git.Repository
var err error
if r.availableAuth != nil {
for _, auth := range r.availableAuth {
repo, err = git.PlainClone(r.repoPath, false, &git.CloneOptions{
URL: r.url,
Auth: auth,
})
if err != nil {
if checkAuthErr(err) {
logrus.WithField("auth", auth.String()).Warn("failed to authenticate. Trying next auth if exists")
continue
}
return err
}
r.auth = auth
break
}
if repo == nil {
return fmt.Errorf("authentication failed for git repository")
}
} else {
repo, err = git.PlainClone(r.repoPath, false, &git.CloneOptions{
URL: r.url,
})
if err != nil {
return err
}
}
r.repository = repo
return nil
}
// Pull pulls changes from the remote repository
func (r *Repository) Pull() error {
w, err := r.repository.Worktree()
if err != nil {
return err
}
return w.Pull(&git.PullOptions{
Auth: r.auth,
})
}
// getSSHAuthMethod returns an authentication method for SSH
func getSSHAuthMethod(config SshAuth) (transport.AuthMethod, error) {
if config.PrivateKey == "" {
return nil, errors.New("\"private-key\" field is empty")
}
privateKey, err := ResolvePath(config.PrivateKey)
if err != nil {
return nil, err
}
s, err := os.Stat(privateKey)
if err != nil {
return nil, err
}
if s.IsDir() {
return nil, errors.New("private key is a directory")
}
auth, err := ssh.NewPublicKeysFromFile("git", privateKey, config.Passphrase)
if err != nil {
return nil, err
}
return auth, nil
}
// checkAuthErr checks if the error is an authentication error
func checkAuthErr(err error) bool {
return err == transport.ErrAuthorizationFailed || err == transport.ErrAuthenticationRequired
}

View File

@ -1 +0,0 @@
package internal

View File

@ -1,89 +0,0 @@
package internal
import (
"fmt"
"time"
"github.com/sirupsen/logrus"
)
const (
red = 31
green = 32
yellow = 33
blue = 36
gray = 37
)
const (
PanicLevel logrus.Level = iota
FatalLevel
ErrorLevel
WarnLevel
InfoLevel
DebugLevel
TraceLevel
)
type LoggerFormatter struct {
TimeFormat string
}
// Format gather all the data from the log entry and format it to return the final log message
func (f *LoggerFormatter) Format(e *logrus.Entry) ([]byte, error) {
msg := f.printColored(e)
if len(e.Data) != 0 {
var fields string
for k, v := range e.Data {
if fields == "" {
fields = fmt.Sprintf("%s=%v", k, v)
} else {
fields = fmt.Sprintf("%s %s=%v", fields, k, v)
}
}
msg = fmt.Sprintf("%s %s %s\n", msg, e.Message, fields)
} else {
msg = fmt.Sprintf("%s %s\n", msg, e.Message)
}
if f.TimeFormat != "" {
msg = fmt.Sprintf("[%s] %s", time.Now().Format(f.TimeFormat), msg)
}
return []byte(msg), nil
}
// printColored returns the prefix for log message with correct log level color and name
func (f *LoggerFormatter) printColored(e *logrus.Entry) string {
var levelColor int
var levelText string
switch e.Level {
case TraceLevel:
levelText = "TRACE:"
levelColor = gray
case DebugLevel:
levelText = "DEBUG:"
levelColor = blue
case InfoLevel:
levelText = "INFO:"
levelColor = green
case WarnLevel:
levelText = "WARNING:"
levelColor = yellow
case ErrorLevel:
levelText = "ERROR:"
levelColor = red
case FatalLevel:
levelText = "FATAL:"
levelColor = red
case PanicLevel:
levelText = "PANIC:"
levelColor = red
default:
levelColor = blue
}
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", levelColor, levelText)
}

95
internal/logger/main.go Normal file
View File

@ -0,0 +1,95 @@
package logger
import (
"fmt"
"strings"
)
// ClassicLogFormat is the format used to print user logs.
//
// Format: "LEVEL MESSAGE"
const ClassicLogFormat = "%s %s"
type Level uint8
const (
Error Level = iota
Warn
Info
Debug
)
var (
Reset = "\033[0m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
)
func (l Level) String() string {
switch l {
case Error:
return fmt.Sprintf("%s%s%s", Red, "ERROR", Reset)
case Warn:
return fmt.Sprintf("%s%s%s", Yellow, "WARN", Reset)
case Info:
return fmt.Sprintf("%s%s%s", Green, "INFO", Reset)
case Debug:
return fmt.Sprintf("%s%s%s", Blue, "DEBUG", Reset)
default:
panic("invalid logger level")
}
}
func LevelFromString(lvl string) Level {
switch strings.ToUpper(lvl) {
case "ERROR":
return Error
case "WARN":
return Warn
case "INFO":
return Info
case "DEBUG":
return Debug
default:
panic("invalid logger level")
}
}
type Logger struct {
Lvl Level
}
// New instanciates a new Logger. It takes a Level as argument.
func New(level Level) *Logger {
return &Logger{
Lvl: level,
}
}
// Error logs an error message.
func (l Logger) Error(msg string, args ...interface{}) {
l.log(Error, msg, args...)
}
// Warn logs a warning message.
func (l Logger) Warn(msg string, args ...interface{}) {
l.log(Warn, msg, args...)
}
// Info logs an info message.
func (l Logger) Info(msg string, args ...interface{}) {
l.log(Info, msg, args...)
}
// Debug logs a debug message.
func (l Logger) Debug(msg string, args ...interface{}) {
l.log(Debug, msg, args...)
}
func (l Logger) log(level Level, msg string, args ...interface{}) {
if l.Lvl >= level {
fmt.Printf(ClassicLogFormat, l.Lvl, fmt.Sprintf(msg, args...))
}
}

View File

@ -1,160 +0,0 @@
package internal
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"time"
"github.com/briandowns/spinner"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
// InstallPackages install all packages from the configuration file by installation order
func InstallPackages(c PkgManagers) error {
pkgManagers := map[string]bool{}
for _, pkgManager := range viper.GetStringSlice("exclude-pkg-managers") {
pkgManagers[pkgManager] = true
}
for _, pkgManager := range c.InstallationOrder {
log := logrus.WithField("package manager", pkgManager)
log.Infoln("Installing packages")
if _, ok := pkgManagers[pkgManager]; ok {
log.Infoln("Skipping...")
continue
}
var pkgs []string
switch pkgManager {
case "brew":
pkgs = c.Brew
case "apt":
pkgs = c.Apt
case "cargo":
pkgs = c.Cargo
case "npm":
pkgs = c.Npm
case "pip":
pkgs = c.Pip
case "go":
pkgs = c.Go
case "nala":
pkgs = c.Nala
default:
log.Error("Package manager not supported")
continue
}
if _, err := exec.LookPath(pkgManager); err != nil {
// * pip might not be available on the system but pip3 is
if pkgManager == "pip" {
if _, err := exec.LookPath("pip3"); err != nil {
log.Error("\"pip\" & \"pip3\" are not available on your system")
continue
}
pkgManager = "pip3"
} else {
log.Error("Package manager not available. Please, install it first")
continue
}
}
// * for some reason, apt binary is available on darwin. Exclude it to avoid errors
if pkgManager == "apt" && runtime.GOOS == "darwin" {
log.Error("\"apt\" is not available on Darwin")
continue
}
if len(pkgs) == 0 {
log.Infoln("Nothing to do")
continue
}
v := viper.GetBool("verbose")
commands := []*exec.Cmd{}
// * package managers requiring sudo permission
if pkgManager == "apt" || pkgManager == "nala" {
commands = append(commands, buildDefaultCommand([]string{"sudo", pkgManager, "install", "-y"}, pkgs, v))
} else if pkgManager == "cargo" {
commands = buildCargoCommand(pkgs, v)
} else {
commands = append(commands, buildDefaultCommand([]string{pkgManager, "install"}, pkgs, v))
}
for i, cmd := range commands {
s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
s.Suffix = "Installing..."
if !v {
s.Start()
}
if err := cmd.Run(); err != nil {
if v {
log.Error(err)
} else {
msg := fmt.Sprintf(" %s", cmd.Args)
if i == len(commands)-1 {
msg = fmt.Sprintf("%s\n", msg)
}
s.FinalMSG = fmt.Sprintf("\x1b[31mFailed: %s\x1b[0m", msg)
s.Stop()
}
continue
}
if !v {
msg := fmt.Sprintf(" %s", cmd.Args)
if i == len(commands)-1 {
msg = fmt.Sprintf("%s\n", msg)
}
s.FinalMSG = fmt.Sprintf("\x1b[32mInstalled: %s\x1b[0m", msg)
s.Stop()
}
}
}
return nil
}
func buildCargoCommand(packages []string, verbose bool) []*exec.Cmd {
commands := []*exec.Cmd{}
cmd := exec.Command("cargo", "install")
for _, pkg := range packages {
if strings.Contains(pkg, " ") {
customCmd := exec.Command("cargo", "install")
customCmd.Args = append(cmd.Args, strings.Split(pkg, " ")...)
if verbose {
customCmd.Stderr = os.Stderr
customCmd.Stdout = os.Stdout
}
commands = append(commands, customCmd)
} else {
cmd.Args = append(cmd.Args, pkg)
}
}
if verbose {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
}
if len(cmd.Args) > 2 {
commands = append(commands, cmd)
}
return commands
}
func buildDefaultCommand(command, packages []string, verbose bool) *exec.Cmd {
cmd := exec.Command(command[0], command[1:]...)
cmd.Args = append(cmd.Args, packages...)
if verbose {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
}
return cmd
}

View File

@ -0,0 +1,23 @@
package git
import "gitea.antoine-langlois.net/datahearth/config-mapper/internal/providers"
type Git struct {
rootDir string
remoteDir string
}
func New(rootDir string, remoteDir string) providers.Provider {
return Git{
rootDir: rootDir,
remoteDir: remoteDir,
}
}
func (g Git) Upload() error {
return nil
}
func (g Git) Download() error {
return nil
}

View File

@ -0,0 +1,6 @@
package providers
type Provider interface {
Upload() error
Download() error
}

View File

@ -1 +0,0 @@
package internal

View File

@ -1,23 +0,0 @@
package internal
import (
"os"
"os/user"
"path/filepath"
"strings"
)
// ResolvePath resolves the path using environment variables and "~"
func ResolvePath(path string) (string, error) {
path = os.ExpandEnv(path)
if strings.Contains(path, "~") {
usr, err := user.Current()
if err != nil {
return "", err
}
path = filepath.Join(usr.HomeDir, strings.Replace(path, "~", "", 1))
}
return path, nil
}

View File

@ -1,11 +1,5 @@
set dotenv-load
set shell := ["zsh", "-uc"]
latest-tag := `git describe --tags --abbrev=0`
default:
@just --list
publish version: (bump-files-version version)
git-chglog --next-tag {{version}} --output CHANGELOG.md
git add CHANGELOG.md cmd/cli.go && git commit -m "chore(changelog): release {{version}}"

320
main.go
View File

@ -1,304 +1,50 @@
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"gitea.antoine-langlois.net/datahearth/config-mapper/internal"
"github.com/sirupsen/logrus"
cmConfig "gitea.antoine-langlois.net/datahearth/config-mapper/internal/config"
cmLog "gitea.antoine-langlois.net/datahearth/config-mapper/internal/logger"
"github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"
)
var (
configuration internal.Configuration
logFormatter = new(internal.LoggerFormatter)
app = &cli.App{
Version: "v0.6.2",
Name: "config-mapper",
Description: "Manage your systems configuration",
UsageText: `config-mapper aims to help you manage your configurations between systems
with a single configuration file.`,
Authors: []*cli.Author{
{
Name: "Antoine Langlois",
Email: "antoine.l@antoine-langlois.net",
},
var app = &cli.App{
Version: "v1.0.0-alpha",
Name: "config-mapper",
Description: "Manage your systems configurations",
UsageText: `config-mapper aims to help you manage your configurations between systems
with a single configuration file.`,
Authors: []*cli.Author{
{
Name: "Antoine Langlois",
Email: "antoine.l@antoine-langlois.net",
},
Suggest: true,
EnableBashCompletion: true,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "location of configuration file",
Value: "$HOME/.config-mapper.yml",
Action: func(ctx *cli.Context, s string) error {
path, err := internal.ResolvePath(s)
if err != nil {
return err
}
stat, err := os.Stat(path)
if err != nil {
return err
}
},
Suggest: true,
EnableBashCompletion: true,
Flags: []cli.Flag{},
Before: func(ctx *cli.Context) error {
var err error
config, err = cmConfig.Load(logger)
if err != nil {
return err
}
if stat.IsDir() {
return fmt.Errorf("--config must be a file. Found directory")
}
if config.LogLevel != "" {
logger.Lvl = cmLog.LevelFromString(config.LogLevel)
}
if !slices.Contains([]string{".yml", ".yaml"}, filepath.Ext(path)) {
return fmt.Errorf("--config must be a \".yml|yaml\" file")
}
return nil
},
},
},
Commands: []*cli.Command{
{
Name: "init",
Aliases: []string{"i"},
Usage: "Initialize your configuration folder",
UsageText: `Initialize will retrieve your configuration folder from the source location and
copy it into the destination field`,
Action: initCommand,
Before: beforeAction,
},
{
Name: "load",
Aliases: []string{"l"},
Usage: "Load your configurations onto your system",
UsageText: `Load your items and package managers dependencies onto your new
system based on your configuration file`,
Action: loadCommand,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "items",
Usage: "Items will not be loaded",
Aliases: []string{"i"},
Category: "Exclude",
},
&cli.BoolFlag{
Name: "packages",
Usage: "Packages will be installed",
Aliases: []string{"p"},
},
&cli.StringSliceFlag{
Name: "package-managers",
Usage: "Exclude packages from specified package managers to be installed",
Category: "Exclude",
Aliases: []string{"P"},
Action: func(ctx *cli.Context, s []string) error {
if !ctx.Bool("packages") {
return fmt.Errorf("--packages is required to exclude package managers")
}
return nil
},
},
},
Before: beforeAction,
},
{
Name: "save",
Aliases: []string{"s"},
Usage: "save your configurations onto your saved location",
UsageText: `Save your items onto your saved location based on your configuration file`,
Action: saveCommand,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "push",
Usage: "Push changes to remote repository",
Aliases: []string{"p"},
},
&cli.StringFlag{
Name: "message",
Usage: "Commit message when pushing repository",
Aliases: []string{"m"},
Action: func(ctx *cli.Context, s string) error {
if !ctx.Bool("push") {
return fmt.Errorf("--push option is required to set a message")
}
if len(strings.Trim(s, " ")) == 0 {
return fmt.Errorf("message must contain at least one character")
}
return nil
},
},
},
Before: beforeAction,
},
{
Name: "install",
Usage: "Install additional tools",
UsageText: `Install additional tools like package managers (brew, nala),
programming language (golang, rust, python)`,
ArgsUsage: "TOOLS",
Action: installCommand,
},
{
Name: "check",
Aliases: []string{"c"},
Usage: "Check if remote has updates",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "pull",
Usage: "Pull updates if any",
},
},
Action: checkCommand,
Before: beforeAction,
},
},
}
)
func init() {
logrus.SetFormatter(logFormatter)
return nil
},
Commands: []*cli.Command{},
}
var (
logger = cmLog.New(cmLog.Info)
config *cmConfig.Definition
)
func main() {
if err := app.Run(os.Args); err != nil {
logrus.Fatalln(err)
logger.Error(err.Error())
}
}
func initCommand(ctx *cli.Context) error {
if _, err := os.Stat(configuration.Path); err == nil {
logrus.Warnf("configuration folder already exists at %s", configuration.Path)
return nil
}
logrus.WithField("path", configuration.Path).Infoln("initializing configuration folder...")
if _, err := internal.NewRepository(configuration.Storage.Git, configuration.Path, true); err != nil {
return err
}
logrus.Infoln("Everything is ready to go!")
return nil
}
func saveCommand(ctx *cli.Context) error {
repo, err := internal.NewRepository(configuration.Storage.Git, configuration.Path, false)
if err != nil {
return err
}
if configuration.Storage.Git.Pull {
if err := repo.Pull(); err != nil {
return err
}
logrus.Debugln("Changes pulled")
}
return nil
}
func loadCommand(ctx *cli.Context) error {
return nil
}
func installCommand(ctx *cli.Context) error {
return nil
}
func checkCommand(ctx *cli.Context) error {
repo, err := internal.NewRepository(configuration.Storage.Git, configuration.Path, false)
if err != nil {
return err
}
changes, err := repo.FetchChanges()
if err != nil {
return err
}
if changes {
logrus.Infoln("Configuration has changed upstream")
if ctx.Bool("pull") {
logrus.Infoln("Pulling changes...")
if err := repo.Pull(); err != nil {
return err
}
}
} else {
logrus.Infoln("Configuration is up-to-date")
}
return nil
}
func beforeAction(ctx *cli.Context) error {
configPath, err := internal.ResolvePath(ctx.String("config"))
if err != nil {
return err
}
f, err := os.ReadFile(configPath)
if err != nil {
return err
}
if err := yaml.Unmarshal(f, &configuration); err != nil {
return err
}
if configuration.Logging.Level != "" {
lvl, err := logrus.ParseLevel(configuration.Logging.Level)
if err != nil {
return err
}
logrus.SetLevel(lvl)
}
if configuration.Logging.File != "" {
loggingFile := configuration.Logging.File
s, err := os.Stat(filepath.Dir(loggingFile))
if err != nil {
if !os.IsNotExist(err) {
return err
}
if err := os.MkdirAll(filepath.Dir(loggingFile), 0755); err != nil {
return err
}
}
if !s.IsDir() {
return fmt.Errorf("parent path segment of \"log-file\" field is a file")
}
f, err := os.OpenFile(configuration.Logging.File, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
if err != nil {
return err
}
var out io.Writer
if configuration.Logging.DisableConsole {
out = f
} else {
out = io.MultiWriter(os.Stdout, f)
}
logrus.SetOutput(out)
}
if configuration.Logging.EnableTime {
format := "02/01/2006 15:04:05"
if configuration.Logging.TimeFormat != "" {
format = configuration.Logging.TimeFormat
}
logFormatter.TimeFormat = format
}
return nil
}