Added lint to the CI. formatted all code accordingly
Some checks failed
CI / check for spelling errors (pull_request) Successful in 21s
CI / code quality (lint/tests) (pull_request) Failing after 18s
CI / make sure build does not fail (pull_request) Has been skipped
CI / notify-fail (pull_request) Successful in 14s

Signed-off-by: Sagi Dayan <sagidayan@gmail.com>
This commit is contained in:
Sagi Dayan 2024-12-16 20:22:58 +02:00
parent 7ae4c4869e
commit 0de49e6cdb
Signed by: sagi
GPG key ID: FAB96BFC63B46458
48 changed files with 317 additions and 148 deletions

View file

@ -1,4 +1,4 @@
name: ci/ci
name: CI
on:
push:
@ -11,7 +11,7 @@ permissions:
jobs:
codespell:
name: Check for spelling errors
name: check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -25,14 +25,33 @@ jobs:
check_hidden: true
skip: ./.git
compile:
name: Make sure build does not fail
codequality:
name: code quality (lint/tests)
runs-on: ubuntu-latest
needs:
- codespell
if: ${{ success() }}
steps:
- uses: https://code.forgejo.org/actions/checkout@v4
- name: checkout
uses: actions/checkout@v4
- name: install dependencies
run: make dep
- name: download golangci-lint
run: wget https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh
- name: install golangci-lint
run: sh ./install.sh v1.62.2
- name: lint
run: ./bin/golangci-lint run
compile:
name: make sure build does not fail
runs-on: ubuntu-latest
needs:
- codespell
- codequality
if: ${{ success() }}
steps:
- uses: actions/checkout@v4
name: checkout
- name: install alsa devel
run: apt update && apt install libasound2-dev -y
@ -52,6 +71,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- codespell
- codequality
- compile
if: ${{ failure() }}
env:
@ -62,7 +82,7 @@ jobs:
run: |-
curl "${SERVER_URL}?token=${TOKEN}" \
-F "title='CI failed'" \
-F "message='Something failed at ${GITHUB_REPOSITORY} for user ${GITHUB_ACTOR}'" \
-F "message='Something failed at ${GITHUB_REPOSITORY}/${GITHUB_REF} for user ${GITHUB_ACTOR}'" \
-F "priority=7" &> /dev/null

12
.golangci.yml Normal file
View file

@ -0,0 +1,12 @@
linters:
enable-all: true
disable:
- wrapcheck
- exhaustruct
- varnamelen
- gochecknoglobals
- depguard
- gochecknoinits
- forbidigo
- revive
- gosec

View file

@ -8,7 +8,7 @@ GO_BUILD_LD_FLAGS=-ldflags="-s -w -X 'git.dayanhub.com/sagi/envoid/internal/vari
build:
GOARCH=amd64 GOOS=linux go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-linux main.go
GOARCH=amd64 GOOS=darwin go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-darwin main.go
GOARCH=amd64 GOOS=windows go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-windows main.go
#GOARCH=amd64 GOOS=windows go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-windows main.go
.PHONY: clean
clean:

View file

@ -37,12 +37,15 @@ var rmEnvCmd = &cobra.Command{
return err
}
defer ds.Close()
err = ds.RemoveEnv(*envFlags.rmEnvName)
if err != nil {
return err
}
project.RemoveEnv(*envFlags.rmEnvName)
configuration.Save()
return nil
},
}
@ -91,6 +94,7 @@ var addEnvCmd = &cobra.Command{
return err
}
configuration.Save()
return nil
},
}
@ -119,21 +123,27 @@ var lsEnvCmd = &cobra.Command{
func init() {
// add
envFlags.addEnvName = addEnvCmd.Flags().StringP("environment", "e", "", "environment name")
err := addEnvCmd.MarkFlagRequired("environment")
if err != nil {
panic(err)
}
envFlags.baseEnvName = addEnvCmd.Flags().StringP("base-environment", "b", "", "base environment name (copy data from base)")
envFlags.baseEnvName = addEnvCmd.Flags().StringP("base-environment",
"b", "", "base environment name (copy data from base)")
err = addEnvCmd.RegisterFlagCompletionFunc("base-environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)
}
//rm
// rm
envFlags.rmEnvName = rmEnvCmd.Flags().StringP("environment", "e", "", "environment name")
err = rmEnvCmd.MarkFlagRequired("environment")
if err != nil {
panic(err)
}
err = rmEnvCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)

View file

@ -1,10 +1,10 @@
package cmd
import (
"errors"
"fmt"
"os"
"errors"
"git.dayanhub.com/sagi/envoid/internal/datastore"
intErrors "git.dayanhub.com/sagi/envoid/internal/errors"
"github.com/spf13/cobra"
@ -68,12 +68,14 @@ var getCmd = &cobra.Command{
}
}
fmt.Println(envVar.Value)
return nil
},
}
func init() {
getFlags.envName = getCmd.Flags().StringP("environment", "e", "", "environment name")
err := getCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)

View file

@ -39,8 +39,9 @@ var importCmd = &cobra.Command{
}
envs = []*types.Environment{e}
}
if len(args) != 1 {
return fmt.Errorf("Needs a file to parse")
return errors.NewInvalidCommandError("Missing a file to parse")
}
file, err := os.Open(args[0])
@ -70,6 +71,7 @@ var importCmd = &cobra.Command{
func init() {
importFlags.envName = importCmd.Flags().StringP("environment", "e", "", "environments name")
err := importCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)

View file

@ -18,6 +18,7 @@ var initCmd = &cobra.Command{
_, err := configuration.GetProject(workingDir)
if err == nil {
fmt.Printf("Project already exists. please remove if you wish to override\n")
return nil
}
ds, err := datastore.NewDataStore()
@ -30,7 +31,7 @@ var initCmd = &cobra.Command{
var pass string
var envNames []string
//check if we are importing an existing file
// check if we are importing an existing file
if ds.DoesFileExists() {
fmt.Printf("Project was not found locally. But found %s file in path.\n", variables.DBFileName)
fmt.Printf("Importing Project for %s\n", workingDir)
@ -71,6 +72,7 @@ var initCmd = &cobra.Command{
}
configuration.Save()
fmt.Println("✅ Done")
return nil
},
}

View file

@ -44,13 +44,13 @@ var printenvCmd = &cobra.Command{
}
env = e
}
datastore, err := datastore.NewDataStore()
ds, err := datastore.NewDataStore()
if err != nil {
fmt.Printf("Error: %e", err)
}
defer datastore.Close()
vars, err := datastore.GetAll(env.Name)
defer ds.Close()
vars, err := ds.GetAll(env.Name)
if err != nil {
return err
}
@ -68,12 +68,14 @@ var printenvCmd = &cobra.Command{
fmt.Println("###")
}
}
return nil
},
}
func init() {
printenvFlags.envName = printenvCmd.Flags().StringP("environment", "e", "", "environments name")
err := printenvCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)

View file

@ -25,9 +25,11 @@ var lsProjectCmd = &cobra.Command{
for _, proj := range configuration.Projects {
fmt.Printf("%s\t%s\n", proj.Name, proj.Path)
}
return nil
},
}
var rmProjectCmd = &cobra.Command{
Use: "rm",
Short: "remove a project definition. the `.envoid` file will not be removed",
@ -36,22 +38,27 @@ var rmProjectCmd = &cobra.Command{
if err != nil {
return err
}
configuration.RemoveProject(project)
configuration.Save()
return nil
},
}
func init() {
projectFlags.projectPath = rmProjectCmd.Flags().StringP("project-path", "p", "", "project path to remove")
err := rmProjectCmd.MarkFlagRequired("project-path")
if err != nil {
panic(err)
}
err = rmProjectCmd.RegisterFlagCompletionFunc("project-path", validProjectPathComplete)
if err != nil {
panic(err)
}
projectCmd.AddCommand(lsProjectCmd)
projectCmd.AddCommand(rmProjectCmd)
}

View file

@ -55,6 +55,7 @@ var rmCmd = &cobra.Command{
func init() {
rmFlags.envName = rmCmd.Flags().StringP("environment", "e", "", "environments name")
err := rmCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)

View file

@ -5,22 +5,26 @@ import (
"os"
"git.dayanhub.com/sagi/envoid/internal/config"
"git.dayanhub.com/sagi/envoid/internal/errors"
"git.dayanhub.com/sagi/envoid/internal/types"
"git.dayanhub.com/sagi/envoid/internal/variables"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
var configuration *config.Config = nil
var workingDir string
var project *types.Project = nil
var (
configuration *config.Config = nil
workingDir string
project *types.Project = nil
)
var rootCmd = &cobra.Command{
Use: "envoid [command]",
Short: "envoid is an easy to use .env manager for personal (non production) use",
Long: `envoid is an easy to use .env manager for personal (non production) use.
envoid works offline and creates different encrypted environments for each project. It's mainly used to store,encrypt (with a passphrase) and share
envoid works offline and creates different encrypted environments for each project.
It's mainly used to store,encrypt (with a passphrase) and share
.env variables`,
Version: fmt.Sprintf("%s commit %s", variables.Version, variables.Commit),
}
@ -33,6 +37,7 @@ var docCmd = &cobra.Command{
if err != nil {
return err
}
return nil
},
}
@ -46,8 +51,9 @@ func Execute() {
func checkAmbiguousEnv(envName string) error {
if len(envName) == 0 && len(project.Environments) > 1 {
return fmt.Errorf("You have more than 1 environment. please provide environment name")
return errors.NewNoEnvProvidedError()
}
return nil
}
@ -56,25 +62,34 @@ func initProject() error {
if err != nil {
return err
}
project = p
return nil
}
func validEnvironmentNamesComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
func validEnvironmentNamesComplete(cmd *cobra.Command, args []string,
toComplete string,
) ([]string, cobra.ShellCompDirective) {
envs := []string{}
if err := initProject(); err == nil {
for _, e := range project.Environments {
envs = append(envs, e.Name)
}
}
return envs, cobra.ShellCompDirectiveDefault
}
func validProjectPathComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
func validProjectPathComplete(cmd *cobra.Command, args []string,
toComplete string,
) ([]string, cobra.ShellCompDirective) {
paths := []string{}
for _, p := range configuration.Projects {
paths = append(paths, fmt.Sprintf("%s\t%s", p.Path, p.Name))
}
return paths, cobra.ShellCompDirectiveDefault
}
@ -90,10 +105,12 @@ func init() {
rootCmd.AddCommand(projectCmd)
configuration = config.GetConfig()
pwd, err := os.Getwd()
if err != nil {
fmt.Printf("[error] %s. please run 'ssecret init'\n", err.Error())
os.Exit(1)
}
workingDir = pwd
}

View file

@ -21,13 +21,15 @@ var setCmd = &cobra.Command{
Use: "set [flags] <key> <value>",
Short: "sets a variable in environment(s)",
Long: "",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
Args: func(_ *cobra.Command, args []string) error {
expectedArgs := 2
if len(args) != expectedArgs {
return errors.NewInvalidCommandError("expected 2 args. <key> <value>")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
err := initProject()
if err != nil {
return err
@ -57,6 +59,7 @@ var setCmd = &cobra.Command{
if err != nil {
fmt.Printf("Error: %s\n", err.Error())
}
return nil
},
}
@ -65,13 +68,15 @@ var setEncryptCmd = &cobra.Command{
Use: "encrypt <key>",
Short: "encrypts an existing variable in environment(s)",
Long: "",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
Args: func(_ *cobra.Command, args []string) error {
expectedArgs := 1
if len(args) != expectedArgs {
return errors.NewInvalidCommandError("expected 1 args. <key>")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
err := initProject()
if err != nil {
return err
@ -117,9 +122,11 @@ var setEncryptCmd = &cobra.Command{
func init() {
setFlags.envName = setCmd.PersistentFlags().StringP("environment", "e", "", "environments name")
setFlags.encrypt = setCmd.Flags().BoolP("secret", "s", false, "value is a secret. encrypt this value")
err := setCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil {
panic(err)
}
setCmd.AddCommand(setEncryptCmd)
}

View file

@ -6,7 +6,8 @@ envoid is an easy to use .env manager for personal (non production) use
envoid is an easy to use .env manager for personal (non production) use.
envoid works offline and creates different encrypted environments for each project. It's mainly used to store,encrypt (with a passphrase) and share
envoid works offline and creates different encrypted environments for each project.
It's mainly used to store,encrypt (with a passphrase) and share
.env variables
### Options
@ -27,4 +28,4 @@ envoid works offline and creates different encrypted environments for each proje
* [envoid rm](envoid_rm.md) - removes a variable from environment(s)
* [envoid set](envoid_set.md) - sets a variable in environment(s)
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -22,4 +22,4 @@ See each sub-command's help for details on how to use the generated script.
* [envoid completion powershell](envoid_completion_powershell.md) - Generate the autocompletion script for powershell
* [envoid completion zsh](envoid_completion_zsh.md) - Generate the autocompletion script for zsh
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -41,4 +41,4 @@ envoid completion bash
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -32,4 +32,4 @@ envoid completion fish [flags]
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -29,4 +29,4 @@ envoid completion powershell [flags]
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -43,4 +43,4 @@ envoid completion zsh [flags]
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -15,4 +15,4 @@ manage environments
* [envoid env ls](envoid_env_ls.md) - list all environments in this project
* [envoid env rm](envoid_env_rm.md) - removes an environment
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -18,4 +18,4 @@ envoid env add [flags]
* [envoid env](envoid_env.md) - manage environments
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -16,4 +16,4 @@ envoid env ls [flags]
* [envoid env](envoid_env.md) - manage environments
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -17,4 +17,4 @@ envoid env rm [flags]
* [envoid env](envoid_env.md) - manage environments
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -17,4 +17,4 @@ envoid get <key> [flags]
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -21,4 +21,4 @@ envoid import <file> [flags]
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -16,4 +16,4 @@ envoid init [flags]
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -17,4 +17,4 @@ envoid printenv [flags]
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -14,4 +14,4 @@ manage project
* [envoid project ls](envoid_project_ls.md) - list all projects for this user
* [envoid project rm](envoid_project_rm.md) - remove a project definition. the `.envoid` file will not be removed
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -16,4 +16,4 @@ envoid project ls [flags]
* [envoid project](envoid_project.md) - manage project
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -17,4 +17,4 @@ envoid project rm [flags]
* [envoid project](envoid_project.md) - manage project
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -17,4 +17,4 @@ envoid rm <key> [flags]
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -19,4 +19,4 @@ envoid set [flags] <key> <value>
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
* [envoid set encrypt](envoid_set_encrypt.md) - encrypts an existing variable in environment(s)
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

View file

@ -22,4 +22,4 @@ envoid set encrypt <key> [flags]
* [envoid set](envoid_set.md) - sets a variable in environment(s)
###### Auto generated by spf13/cobra on 12-Dec-2024
###### Auto generated by spf13/cobra on 16-Dec-2024

8
go.sum
View file

@ -24,21 +24,13 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

View file

@ -6,5 +6,6 @@ func StrToSnakeCase(s string) string {
snake := strings.ToLower(s)
snake = strings.ReplaceAll(snake, " ", "_")
snake = strings.ReplaceAll(snake, "\t", "_")
return snake
}

View file

@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/fs"
"os"
"path"
@ -14,33 +15,40 @@ import (
type Config struct {
PWD string `json:"-"`
Projects []*types.Project `json:"projects" default:"[]"`
Projects []*types.Project `default:"[]" json:"projects"`
}
var configPath string
var configStruct *Config
func (c *Config) GetProject(projectPath string) (*types.Project, error) {
var p *types.Project
for _, project := range configStruct.Projects {
if project.Path == projectPath {
p = project
break
}
}
if p == nil {
return nil, errors.NewProjectNotFoundError(projectPath)
}
return p, nil
}
func (c *Config) RemoveProject(project *types.Project) {
projects := []*types.Project{}
for _, p := range c.Projects {
if p.Name != project.Name && p.Path != project.Path {
projects = append(projects, p)
}
}
c.Projects = projects
}
@ -54,7 +62,9 @@ func (c *Config) NewProject(name string, path string, password string) (*types.P
Environments: []*types.Environment{},
}
configStruct.Projects = append(configStruct.Projects, p)
SaveConfig()
return p, nil
}
@ -79,46 +89,53 @@ func init() {
fmt.Printf("[ERROR] Failed to fetch user config directory. %e\n", err)
os.Exit(1)
}
if _, err := os.Stat(configDir); os.IsNotExist(err) {
err := os.MkdirAll(configDir, 0700)
var folderPermissions fs.FileMode = 0o700
err := os.MkdirAll(configDir, folderPermissions)
if err != nil {
panic(err)
}
}
var configFile *os.File
if _, err := os.Stat(configPath); os.IsNotExist(err) {
configFile, err = os.Create(configPath)
defer func() {
err = configFile.Close()
if err != nil {
panic(err)
}
}()
if err != nil {
fmt.Printf("[ERROR] Failed to create config file @ %s. %e\n", configPath, err)
os.Exit(1)
}
defer configFile.Close()
}
configStruct, err = loadConfig()
if err != nil {
fmt.Printf("[ERROR] Failed to load config file @ %s. %e\n", configPath, err)
os.Exit(1)
configFile.Close()
panic("")
}
}
func loadConfig() (*Config, error) {
c := &Config{}
err := defaults.Set(c)
if err != nil {
panic(err)
}
file, err := os.ReadFile(configPath)
if err != nil {
return nil, err
}
if len(file) == 0 {
return c, nil
}
if err = json.Unmarshal(file, c); err != nil {
return nil, err
}
@ -127,7 +144,9 @@ func loadConfig() (*Config, error) {
if err != nil {
return nil, err
}
c.PWD = pwd
return c, nil
}
@ -137,7 +156,10 @@ func SaveConfig() {
fmt.Printf("[ERROR] Failed to convert config to json. %e\n", err)
os.Exit(1)
}
err = os.WriteFile(configPath, yml, 0600)
var filePermissions fs.FileMode = 0o600
err = os.WriteFile(configPath, yml, filePermissions)
if err != nil {
fmt.Printf("[ERROR] Failed to save config file @ %s. %e\n", configPath, err)
os.Exit(1)

View file

@ -18,84 +18,98 @@ import (
"golang.org/x/sync/errgroup"
)
type datastore struct {
type Datastore struct {
db *db
}
func NewDataStore() (*datastore, error) {
func NewDataStore() (*Datastore, error) {
db, err := newDB()
if err != nil {
return nil, err
}
return &datastore{
return &Datastore{
db: db,
}, nil
}
func (d *datastore) CreateEnv(name string) error {
table_name := envNameToTableName(name)
return d.db.createTableIfNotExists(table_name)
func (d *Datastore) CreateEnv(name string) error {
tableName := envNameToTableName(name)
return d.db.createTableIfNotExists(tableName)
}
func (d *datastore) DoesFileExists() bool {
func (d *Datastore) DoesFileExists() bool {
pwd := config.GetConfig().PWD
filePath := fmt.Sprintf("%s/%s", pwd, variables.DBFileName)
if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) {
return false
}
return true
}
func (d *datastore) ListEnvironments() ([]string, error) {
func (d *Datastore) ListEnvironments() ([]string, error) {
return d.db.listTables()
}
func (d *datastore) CreateEnvOffExsisting(new_env string, base_env string) error {
table_name_new := envNameToTableName(new_env)
table_name_base := envNameToTableName(base_env)
err := d.CreateEnv(new_env)
func (d *Datastore) CreateEnvOffExsisting(newEnv string, baseEnv string) error {
tableNameName := envNameToTableName(newEnv)
tableNameBase := envNameToTableName(baseEnv)
err := d.CreateEnv(newEnv)
if err != nil {
return err
}
err = d.db.copyContentFromTo(table_name_base, table_name_new)
err = d.db.copyContentFromTo(tableNameBase, tableNameName)
if err != nil {
return err
}
return nil
}
func (d *datastore) Close() error {
func (d *Datastore) Close() error {
return d.db.close()
}
func (d *datastore) SetValue(key string, value string, encrypted *bool, envs []*types.Environment) error {
func (d *Datastore) SetValue(key string, value string, encrypted *bool, envs []*types.Environment) error {
if encrypted == nil {
encrypted = common.BoolP(false)
}
if *encrypted {
v, err := enc(value)
if err != nil {
return err
}
value = *v
}
for _, env := range envs {
table_name := envNameToTableName(env.Name)
if err := d.db.setVar(table_name, key, value, *encrypted); err != nil {
tableName := envNameToTableName(env.Name)
if err := d.db.setVar(tableName, key, value, *encrypted); err != nil {
return err
}
}
return nil
}
func (d *datastore) GetAll(envName string) ([]*types.EnvVar, error) {
table_name := envNameToTableName(envName)
vars, err := d.db.getAll(table_name)
func (d *Datastore) GetAll(envName string) ([]*types.EnvVar, error) {
tableName := envNameToTableName(envName)
vars, err := d.db.getAll(tableName)
if err != nil {
return vars, err
}
g := new(errgroup.Group)
for _, v := range vars {
g.Go(func() error {
if v.Encrypted {
@ -103,42 +117,50 @@ func (d *datastore) GetAll(envName string) ([]*types.EnvVar, error) {
return &intErrors.InvalidPasswordError{}
}
}
return nil
})
}
if err := g.Wait(); err != nil {
return vars, err
}
sort.SliceStable(vars, func(i, j int) bool {
return vars[i].Key < vars[j].Key
})
return vars, nil
}
func (d *datastore) RemoveVar(key string, envs []*types.Environment) {
func (d *Datastore) RemoveVar(key string, envs []*types.Environment) {
for _, env := range envs {
table_name := envNameToTableName(env.Name)
d.db.rmVar(table_name, key)
tableName := envNameToTableName(env.Name)
d.db.rmVar(tableName, key)
}
}
func (d *datastore) GetVar(envName string, key string) (*types.EnvVar, error) {
table_name := envNameToTableName(envName)
v, err := d.db.getVar(table_name, key)
func (d *Datastore) GetVar(envName string, key string) (*types.EnvVar, error) {
tableName := envNameToTableName(envName)
v, err := d.db.getVar(tableName, key)
if err != nil {
return v, err
}
if v.Encrypted {
if v.Value, err = dec(v.Value); err != nil {
return v, &intErrors.InvalidPasswordError{}
}
}
return v, err
}
func (d *datastore) RemoveEnv(envName string) error {
table_name := envNameToTableName(envName)
return d.db.deleteTable(table_name)
func (d *Datastore) RemoveEnv(envName string) error {
tableName := envNameToTableName(envName)
return d.db.deleteTable(tableName)
}
func enc(s string) (*string, error) {
@ -146,6 +168,7 @@ func enc(s string) (*string, error) {
proj, _ := conf.GetProject(conf.PWD)
key, salt, err := deriveKey([]byte(proj.Password), nil)
data := []byte(s)
if err != nil {
return nil, err
}
@ -179,6 +202,7 @@ func dec(s string) (string, error) {
conf := config.GetConfig()
proj, _ := conf.GetProject(conf.PWD)
key, _, err := deriveKey([]byte(proj.Password), salt)
if err != nil {
return "", err
@ -207,14 +231,19 @@ func dec(s string) (string, error) {
}
func deriveKey(password, salt []byte) ([]byte, []byte, error) {
saltSize := 32
if salt == nil {
salt = make([]byte, 32)
salt = make([]byte, saltSize)
if _, err := rand.Read(salt); err != nil {
return nil, nil, err
}
}
key, err := scrypt.Key(password, salt, 1048576, 8, 1, 32)
N := 1048576
r := 8
p := 1
key, err := scrypt.Key(password, salt, N, r, p, saltSize)
if err != nil {
return nil, nil, err
}

View file

@ -1,9 +1,8 @@
package datastore
import (
"fmt"
"database/sql"
"fmt"
"git.dayanhub.com/sagi/envoid/internal/common"
"git.dayanhub.com/sagi/envoid/internal/errors"
@ -20,6 +19,7 @@ func newDB() (*db, error) {
con, err := sql.Open("sqlite", variables.DBFileName)
if err != nil {
fmt.Printf("%v\n", err)
return nil, err
}
@ -32,72 +32,91 @@ func (d *db) close() error {
return d.con.Close()
}
func (d *db) createTableIfNotExists(table_name string) error {
_, err := d.con.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL, encrypted BOOL NOT NULL);", table_name))
func (d *db) createTableIfNotExists(tableName string) error {
q := "CREATE TABLE IF NOT EXISTS " + tableName +
" (key TEXT PRIMARY KEY NOT NULL,value BLOB NOT NULL,encrypted BOOL NOT NULL);"
_, err := d.con.Exec(q)
if err != nil {
return err
}
return nil
}
func (d *db) listTables() ([]string, error) {
tables := []string{}
q := "SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%' AND name LIKE ?"
rows, err := d.con.Query(q, fmt.Sprintf("%s_%%", variables.DBTablePrefix))
if err != nil {
rows, err := d.con.Query(q, variables.DBTablePrefix+"_%%")
if err != nil || rows.Err() != nil {
return tables, err
}
defer rows.Close()
for rows.Next() {
var table string
if err := rows.Scan(&table); err != nil {
return tables, err
}
tables = append(tables, tableNameToEnvName(table))
}
return tables, nil
}
func (d *db) copyContentFromTo(table_name_target string, table_name_dest string) error {
_, err := d.con.Exec(fmt.Sprintf("INSERT INTO %s (key, value, encrypted) SELECT * FROM %s", table_name_dest, table_name_target))
func (d *db) copyContentFromTo(tableNameTarget string, tableNameDest string) error {
_, err := d.con.Exec(
fmt.Sprintf("INSERT INTO %s (key, value, encrypted) SELECT * FROM %s",
tableNameDest, tableNameTarget),
)
if err != nil {
return err
}
return nil
}
func (d *db) deleteTable(table_name string) error {
_, err := d.con.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", table_name))
func (d *db) deleteTable(tableName string) error {
_, err := d.con.Exec("DROP TABLE IF EXISTS " + tableName)
return err
}
func (d *db) getVar(table_name string, key string) (*types.EnvVar, error) {
q := fmt.Sprintf("SELECT * FROM %s WHERE key=?", table_name)
func (d *db) getVar(tableName string, key string) (*types.EnvVar, error) {
q := "SELECT * FROM " + tableName + " WHERE key=?"
row := d.con.QueryRow(q, key)
envVar := &types.EnvVar{}
err := row.Scan(&envVar.Key, &envVar.Value, &envVar.Encrypted)
if err != nil {
return envVar, errors.NewNoKeyFoundError(key)
}
return envVar, nil
}
func (d *db) getAll(table_name string) ([]*types.EnvVar, error) {
func (d *db) getAll(tableName string) ([]*types.EnvVar, error) {
entries := []*types.EnvVar{}
rows, err := d.con.Query(fmt.Sprintf("SELECT * FROM %s", table_name))
if err != nil {
rows, err := d.con.Query("SELECT * FROM " + tableName)
if err != nil || rows.Err() != nil {
return entries, err
}
defer rows.Close()
for rows.Next() {
key := ""
value := ""
enc := common.BoolP(false)
err := rows.Scan(&key, &value, enc)
if err != nil {
return entries, err
}
e := &types.EnvVar{
Key: key,
Value: value,
@ -105,27 +124,33 @@ func (d *db) getAll(table_name string) ([]*types.EnvVar, error) {
}
entries = append(entries, e)
}
return entries, nil
return entries, nil
}
func (d *db) setVar(table_name string, key string, value string, encrypted bool) error {
q := fmt.Sprintf("INSERT INTO %s (key, value, encrypted) values ( ? , ? , ? ) ON CONFLICT (key) DO UPDATE SET value = ?, encrypted = ?", table_name)
func (d *db) setVar(tableName string, key string, value string, encrypted bool) error {
q := "INSERT INTO " +
tableName +
" (key,value,encrypted) values (?,?,?) ON CONFLICT (key) DO UPDATE SET value=?, encrypted=?"
_, err := d.con.Exec(q, key, value, encrypted, value, encrypted)
if err != nil {
fmt.Println(err)
return errors.NewSecretExsistsError(key)
}
return nil
}
func (d *db) rmVar(table_name string, key string) {
q := fmt.Sprintf("DELETE FROM %s WHERE key = ?", table_name)
func (d *db) rmVar(tableName string, key string) {
q := "DELETE FROM " + tableName + " WHERE key = ?"
_, _ = d.con.Exec(q, key)
}
func envNameToTableName(envName string) string {
envSanke := common.StrToSnakeCase(envName)
return fmt.Sprintf("%s_%s", variables.DBTablePrefix, envSanke)
}

View file

@ -1,13 +1,11 @@
package errors
import "fmt"
type EnvironmentExistsError struct {
name string
}
func (e *EnvironmentExistsError) Error() string {
return fmt.Sprintf("environment %s already exists", e.name)
return "environment " + e.name + " already exists"
}
func NewEnvironmentExistsError(name string) *EnvironmentExistsError {

View file

@ -1,13 +1,11 @@
package errors
import "fmt"
type InvalidCommandError struct {
msg string
}
func (e *InvalidCommandError) Error() string {
return fmt.Sprintf("invalid command. %v", e.msg)
return "invalid command. " + e.msg
}
func NewInvalidCommandError(msg string) *InvalidCommandError {

View file

@ -1,7 +1,6 @@
package errors
type InvalidPasswordError struct {
}
type InvalidPasswordError struct{}
func (e *InvalidPasswordError) Error() string {
return "invalid password. is your environment set correctly?"

View file

@ -1,13 +1,11 @@
package errors
import "fmt"
type NoKeyFoundError struct {
key string
}
func (e *NoKeyFoundError) Error() string {
return fmt.Sprintf("key %s not found", e.key)
return "key " + e.key + " not found"
}
func NewNoKeyFoundError(key string) *NoKeyFoundError {

View file

@ -1,13 +1,11 @@
package errors
import "fmt"
type NoConfigFoundError struct {
path string
}
func (e *NoConfigFoundError) Error() string {
return fmt.Sprintf("no config file found in %s", e.path)
return "no config file found in " + e.path
}
func NewNoConfigFoundError(path string) *NoConfigFoundError {

View file

@ -0,0 +1,11 @@
package errors
type NoEnvProvidedError struct{}
func (e *NoEnvProvidedError) Error() string {
return "You have more than 1 environment. please provide environment name"
}
func NewNoEnvProvidedError() *NoEnvProvidedError {
return &NoEnvProvidedError{}
}

View file

@ -1,13 +1,11 @@
package errors
import "fmt"
type ProjectEmptyError struct {
name string
}
func (e *ProjectEmptyError) Error() string {
return fmt.Sprintf("Project %s does not have any environments.", e.name)
return "Project " + e.name + " does not have any environments."
}
func NewProjectEmptyError(name string) *ProjectEmptyError {

View file

@ -9,15 +9,20 @@ import (
)
func PasswordPrompt(label string) string {
var s string
var str string
for {
fmt.Fprint(os.Stderr, label+" ")
b, _ := term.ReadPassword(int(syscall.Stdin))
s = string(b)
if s != "" {
b, _ := term.ReadPassword(syscall.Stdin)
str = string(b)
if str != "" {
break
}
}
fmt.Println()
return s
return str
}

View file

@ -8,14 +8,18 @@ import (
)
func StringPrompt(label string) string {
var s string
var str string
r := bufio.NewReader(os.Stdin)
for {
fmt.Fprint(os.Stderr, label+" ")
s, _ = r.ReadString('\n')
if s != "" {
str, _ = r.ReadString('\n')
if str != "" {
break
}
}
return strings.TrimSpace(s)
return strings.TrimSpace(str)
}

View file

@ -12,11 +12,12 @@ type Project struct {
Path string `json:"path"`
Name string `json:"name"`
Password string `json:"password"`
Environments []*Environment `json:"envorinments" default:"[]"`
Environments []*Environment `default:"[]" json:"envorinments"`
}
func (p *Project) GetEnv(name string) (*Environment, error) {
var e *Environment
for _, env := range p.Environments {
if env.Name == name {
passByte, err := base64.RawStdEncoding.DecodeString(p.Password)
@ -24,32 +25,39 @@ func (p *Project) GetEnv(name string) (*Environment, error) {
fmt.Printf("[ERROR] Failed to decode project password @ %s. %e\n", p.Path, err)
os.Exit(1)
}
e = &Environment{
Name: env.Name,
Password: string(passByte),
}
}
}
if e == nil {
return nil, errors.NewEnvironmentNotFoundError(p.Path, name)
}
return e, nil
}
func (p *Project) NewEnv(name string) error {
e := &Environment{
Name: name,
}
p.Environments = append(p.Environments, e)
return nil
}
func (p *Project) RemoveEnv(name string) {
environments := []*Environment{}
for _, env := range p.Environments {
if env.Name != name {
environments = append(environments, env)
}
}
p.Environments = environments
}

View file

@ -1,6 +1,6 @@
package variables
var (
Commit string = "HEAD"
Version string = "development"
Commit = "HEAD"
Version = "development"
)