Compare commits

..

4 commits

Author SHA1 Message Date
29e31c5b21
Added lint to the CI. formatted all code accordingly
All checks were successful
CI / check for spelling errors (pull_request) Successful in 21s
CI / code quality (lint/tests) (pull_request) Successful in 2m6s
CI / make sure build does not fail (pull_request) Successful in 2m22s
CI / notify-fail (pull_request) Has been skipped
CI / check for spelling errors (push) Successful in 21s
CI / code quality (lint/tests) (push) Successful in 2m4s
CI / make sure build does not fail (push) Successful in 2m24s
CI / notify-fail (push) Has been skipped
Signed-off-by: Sagi Dayan <sagidayan@gmail.com>
2024-12-17 13:10:42 +02:00
7ae4c4869e
CI changes: merged some flows into one with multiple jobs
All checks were successful
ci/ci / Check for spelling errors (pull_request) Successful in 21s
ci/ci / Make sure build does not fail (pull_request) Successful in 2m48s
ci/ci / notify-fail (pull_request) Has been skipped
ci/ci / Check for spelling errors (push) Successful in 22s
ci/ci / Make sure build does not fail (push) Successful in 2m51s
ci/ci / notify-fail (push) Has been skipped
Signed-off-by: Sagi Dayan <sagidayan@gmail.com>
2024-12-16 18:07:31 +02:00
a16af482de Update module golang.org/x/sync to v0.10.0
All checks were successful
build bin / Make sure build does not fail (pull_request) Successful in 2m54s
Codespell / Check for spelling errors (pull_request) Successful in 22s
build bin / Make sure build does not fail (push) Successful in 2m55s
Codespell / Check for spelling errors (push) Successful in 21s
2024-12-15 16:18:39 +00:00
fd263d8280 Update module golang.org/x/crypto to v0.31.0
All checks were successful
build bin / Make sure build does not fail (pull_request) Successful in 2m52s
Codespell / Check for spelling errors (pull_request) Successful in 22s
build bin / Make sure build does not fail (push) Successful in 3m21s
Codespell / Check for spelling errors (push) Successful in 22s
2024-12-15 15:59:57 +00:00
51 changed files with 390 additions and 202 deletions

View file

@ -1,31 +0,0 @@
name: build bin
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
build:
name: Make sure build does not fail
runs-on: ubuntu-latest
steps:
- uses: https://code.forgejo.org/actions/checkout@v4
name: checkout
- name: install alsa devel
run: apt update && apt install libasound2-dev -y
- uses: https://code.forgejo.org/actions/setup-go@v5
name: install go
with:
go-version-file: './go.mod'
- run: go version
name: Go version
- run: make dep
name: install dependencies
- run: make build
name: build app

93
.forgejo/workflows/ci.yml Normal file
View file

@ -0,0 +1,93 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
codespell:
name: check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Annotate locations with typos
uses: "https://github.com/codespell-project/codespell-problem-matcher@v1"
- name: Codespell
uses: "https://github.com/codespell-project/actions-codespell@v2"
with:
check_filenames: true
check_hidden: true
skip: ./.git
codequality:
name: code quality (lint/tests)
runs-on: ubuntu-latest
needs:
- codespell
if: ${{ success() }}
steps:
- name: checkout
uses: actions/checkout@v4
- uses: https://code.forgejo.org/actions/setup-go@v5
name: install go
with:
go-version-file: './go.mod'
- 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
- uses: https://code.forgejo.org/actions/setup-go@v5
name: install go
with:
go-version-file: './go.mod'
- run: go version
name: Go version
- run: make dep
name: install dependencies
- run: make build
name: build app
notify-fail:
name: notify-fail
runs-on: ubuntu-latest
needs:
- codespell
- codequality
- compile
if: ${{ failure() }}
env:
SERVER_URL: ${{ secrets.GOTIFY_SERVER_URL }}
TOKEN: ${{ secrets.GOTIFY_TOKEN }}
steps:
- name: gotify
run: |-
curl "${SERVER_URL}?token=${TOKEN}" \
-F "title='CI failed'" \
-F "message='Something failed at ${GITHUB_REPOSITORY}/${GITHUB_REF} for user ${GITHUB_ACTOR}'" \
-F "priority=7" &> /dev/null

View file

@ -1,27 +0,0 @@
name: Codespell
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Annotate locations with typos
uses: "https://github.com/codespell-project/codespell-problem-matcher@v1"
- name: Codespell
uses: "https://github.com/codespell-project/actions-codespell@v2"
with:
check_filenames: true
check_hidden: true
skip: ./.git

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: build:
GOARCH=amd64 GOOS=linux go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-linux main.go 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=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 .PHONY: clean
clean: clean:

View file

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

View file

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

View file

@ -39,8 +39,9 @@ var importCmd = &cobra.Command{
} }
envs = []*types.Environment{e} envs = []*types.Environment{e}
} }
if len(args) != 1 { 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]) file, err := os.Open(args[0])
@ -70,6 +71,7 @@ var importCmd = &cobra.Command{
func init() { func init() {
importFlags.envName = importCmd.Flags().StringP("environment", "e", "", "environments name") importFlags.envName = importCmd.Flags().StringP("environment", "e", "", "environments name")
err := importCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete) err := importCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil { if err != nil {
panic(err) panic(err)

View file

@ -18,6 +18,7 @@ var initCmd = &cobra.Command{
_, err := configuration.GetProject(workingDir) _, err := configuration.GetProject(workingDir)
if err == nil { if err == nil {
fmt.Printf("Project already exists. please remove if you wish to override\n") fmt.Printf("Project already exists. please remove if you wish to override\n")
return nil return nil
} }
ds, err := datastore.NewDataStore() ds, err := datastore.NewDataStore()
@ -71,6 +72,7 @@ var initCmd = &cobra.Command{
} }
configuration.Save() configuration.Save()
fmt.Println("✅ Done") fmt.Println("✅ Done")
return nil return nil
}, },
} }

View file

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

View file

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

View file

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

View file

@ -5,22 +5,26 @@ import (
"os" "os"
"git.dayanhub.com/sagi/envoid/internal/config" "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/types"
"git.dayanhub.com/sagi/envoid/internal/variables" "git.dayanhub.com/sagi/envoid/internal/variables"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/cobra/doc" "github.com/spf13/cobra/doc"
) )
var configuration *config.Config = nil var (
var workingDir string configuration *config.Config = nil
var project *types.Project = nil workingDir string
project *types.Project = nil
)
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "envoid [command]", Use: "envoid [command]",
Short: "envoid is an easy to use .env manager for personal (non production) use", 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. 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`, .env variables`,
Version: fmt.Sprintf("%s commit %s", variables.Version, variables.Commit), Version: fmt.Sprintf("%s commit %s", variables.Version, variables.Commit),
} }
@ -33,6 +37,7 @@ var docCmd = &cobra.Command{
if err != nil { if err != nil {
return err return err
} }
return nil return nil
}, },
} }
@ -46,8 +51,9 @@ func Execute() {
func checkAmbiguousEnv(envName string) error { func checkAmbiguousEnv(envName string) error {
if len(envName) == 0 && len(project.Environments) > 1 { 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 return nil
} }
@ -56,25 +62,34 @@ func initProject() error {
if err != nil { if err != nil {
return err return err
} }
project = p project = p
return nil 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{} envs := []string{}
if err := initProject(); err == nil { if err := initProject(); err == nil {
for _, e := range project.Environments { for _, e := range project.Environments {
envs = append(envs, e.Name) envs = append(envs, e.Name)
} }
} }
return envs, cobra.ShellCompDirectiveDefault 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{} paths := []string{}
for _, p := range configuration.Projects { for _, p := range configuration.Projects {
paths = append(paths, fmt.Sprintf("%s\t%s", p.Path, p.Name)) paths = append(paths, fmt.Sprintf("%s\t%s", p.Path, p.Name))
} }
return paths, cobra.ShellCompDirectiveDefault return paths, cobra.ShellCompDirectiveDefault
} }
@ -90,10 +105,12 @@ func init() {
rootCmd.AddCommand(projectCmd) rootCmd.AddCommand(projectCmd)
configuration = config.GetConfig() configuration = config.GetConfig()
pwd, err := os.Getwd() pwd, err := os.Getwd()
if err != nil { if err != nil {
fmt.Printf("[error] %s. please run 'ssecret init'\n", err.Error()) fmt.Printf("[error] %s. please run 'ssecret init'\n", err.Error())
os.Exit(1) os.Exit(1)
} }
workingDir = pwd workingDir = pwd
} }

View file

@ -21,13 +21,15 @@ var setCmd = &cobra.Command{
Use: "set [flags] <key> <value>", Use: "set [flags] <key> <value>",
Short: "sets a variable in environment(s)", Short: "sets a variable in environment(s)",
Long: "", Long: "",
Args: func(cmd *cobra.Command, args []string) error { Args: func(_ *cobra.Command, args []string) error {
if len(args) != 2 { expectedArgs := 2
if len(args) != expectedArgs {
return errors.NewInvalidCommandError("expected 2 args. <key> <value>") return errors.NewInvalidCommandError("expected 2 args. <key> <value>")
} }
return nil return nil
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
err := initProject() err := initProject()
if err != nil { if err != nil {
return err return err
@ -57,6 +59,7 @@ var setCmd = &cobra.Command{
if err != nil { if err != nil {
fmt.Printf("Error: %s\n", err.Error()) fmt.Printf("Error: %s\n", err.Error())
} }
return nil return nil
}, },
} }
@ -65,13 +68,15 @@ var setEncryptCmd = &cobra.Command{
Use: "encrypt <key>", Use: "encrypt <key>",
Short: "encrypts an existing variable in environment(s)", Short: "encrypts an existing variable in environment(s)",
Long: "", Long: "",
Args: func(cmd *cobra.Command, args []string) error { Args: func(_ *cobra.Command, args []string) error {
if len(args) != 1 { expectedArgs := 1
if len(args) != expectedArgs {
return errors.NewInvalidCommandError("expected 1 args. <key>") return errors.NewInvalidCommandError("expected 1 args. <key>")
} }
return nil return nil
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
err := initProject() err := initProject()
if err != nil { if err != nil {
return err return err
@ -117,9 +122,11 @@ var setEncryptCmd = &cobra.Command{
func init() { func init() {
setFlags.envName = setCmd.PersistentFlags().StringP("environment", "e", "", "environments name") setFlags.envName = setCmd.PersistentFlags().StringP("environment", "e", "", "environments name")
setFlags.encrypt = setCmd.Flags().BoolP("secret", "s", false, "value is a secret. encrypt this value") setFlags.encrypt = setCmd.Flags().BoolP("secret", "s", false, "value is a secret. encrypt this value")
err := setCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete) err := setCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
if err != nil { if err != nil {
panic(err) panic(err)
} }
setCmd.AddCommand(setEncryptCmd) 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 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 .env variables
### Options ### 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 rm](envoid_rm.md) - removes a variable from environment(s)
* [envoid set](envoid_set.md) - sets a variable in 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 powershell](envoid_completion_powershell.md) - Generate the autocompletion script for powershell
* [envoid completion zsh](envoid_completion_zsh.md) - Generate the autocompletion script for zsh * [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 * [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 * [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 * [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 * [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 ls](envoid_env_ls.md) - list all environments in this project
* [envoid env rm](envoid_env_rm.md) - removes an environment * [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 * [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 * [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 * [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 * [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 * [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 * [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 * [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 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 * [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 * [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 * [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 * [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](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) * [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) * [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

4
go.mod
View file

@ -7,8 +7,8 @@ require (
github.com/glebarez/go-sqlite v1.22.0 github.com/glebarez/go-sqlite v1.22.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.29.0 golang.org/x/crypto v0.31.0
golang.org/x/sync v0.9.0 golang.org/x/sync v0.10.0
golang.org/x/term v0.27.0 golang.org/x/term v0.27.0
) )

12
go.sum
View file

@ -24,17 +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/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= 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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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.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 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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.ToLower(s)
snake = strings.ReplaceAll(snake, " ", "_") snake = strings.ReplaceAll(snake, " ", "_")
snake = strings.ReplaceAll(snake, "\t", "_") snake = strings.ReplaceAll(snake, "\t", "_")
return snake return snake
} }

View file

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

View file

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

View file

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

View file

@ -1,13 +1,11 @@
package errors package errors
import "fmt"
type EnvironmentExistsError struct { type EnvironmentExistsError struct {
name string name string
} }
func (e *EnvironmentExistsError) Error() 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 { func NewEnvironmentExistsError(name string) *EnvironmentExistsError {

View file

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

View file

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

View file

@ -1,13 +1,11 @@
package errors package errors
import "fmt"
type NoKeyFoundError struct { type NoKeyFoundError struct {
key string key string
} }
func (e *NoKeyFoundError) Error() 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 { func NewNoKeyFoundError(key string) *NoKeyFoundError {

View file

@ -1,13 +1,11 @@
package errors package errors
import "fmt"
type NoConfigFoundError struct { type NoConfigFoundError struct {
path string path string
} }
func (e *NoConfigFoundError) Error() 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 { 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 package errors
import "fmt"
type ProjectEmptyError struct { type ProjectEmptyError struct {
name string name string
} }
func (e *ProjectEmptyError) Error() 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 { func NewProjectEmptyError(name string) *ProjectEmptyError {

View file

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

View file

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

View file

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

View file

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