diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bc3e14016..f8200e596 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,8 +14,12 @@ jobs: - uses: actions/setup-go@v4 with: go-version: 'stable' - - name: Lint 🎨 + cache: false + - name: Lint uses: golangci/golangci-lint-action@v3 with: + skip-cache: true + skip-pkg-cache: true + skip-build-cache: true version: latest args: --timeout=30m ./... diff --git a/auth/console/jwt_secret_command_test.go b/auth/console/jwt_secret_command_test.go new file mode 100644 index 000000000..33ab17070 --- /dev/null +++ b/auth/console/jwt_secret_command_test.go @@ -0,0 +1,61 @@ +package console + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + configmock "github.com/goravel/framework/contracts/config/mocks" + consolemocks "github.com/goravel/framework/contracts/console/mocks" + "github.com/goravel/framework/support" + "github.com/goravel/framework/support/file" +) + +func TestJwtSecretCommand(t *testing.T) { + mockConfig := &configmock.Config{} + mockConfig.On("GetString", "jwt.secret").Return("").Twice() + + jwtSecretCommand := NewJwtSecretCommand(mockConfig) + mockContext := &consolemocks.Context{} + + assert.False(t, file.Exists(".env")) + err := file.Create(".env", "JWT_SECRET=\n") + assert.Nil(t, err) + + assert.Nil(t, jwtSecretCommand.Handle(mockContext)) + + assert.True(t, file.Exists(".env")) + env, err := os.ReadFile(".env") + assert.Nil(t, err) + assert.True(t, len(env) > 10) + assert.Nil(t, file.Remove(".env")) + + mockConfig.AssertExpectations(t) +} + +func TestJwtSecretCommandWithCustomEnvFile(t *testing.T) { + support.EnvPath = "config.conf" + + mockConfig := &configmock.Config{} + mockConfig.On("GetString", "jwt.secret").Return("").Twice() + + jwtSecretCommand := NewJwtSecretCommand(mockConfig) + mockContext := &consolemocks.Context{} + + assert.False(t, file.Exists("config.conf")) + err := file.Create("config.conf", "JWT_SECRET=\n") + assert.Nil(t, err) + + assert.Nil(t, jwtSecretCommand.Handle(mockContext)) + + assert.True(t, file.Exists("config.conf")) + env, err := os.ReadFile("config.conf") + assert.Nil(t, err) + assert.True(t, len(env) > 10) + assert.Nil(t, file.Remove("config.conf")) + + support.EnvPath = ".env" + + mockConfig.AssertExpectations(t) +} diff --git a/auth/console/policy_make_command_test.go b/auth/console/policy_make_command_test.go index 4b5c780b5..b379b8559 100644 --- a/auth/console/policy_make_command_test.go +++ b/auth/console/policy_make_command_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestEventMakeCommand(t *testing.T) { +func TestPolicyMakeCommand(t *testing.T) { policyMakeCommand := &PolicyMakeCommand{} mockContext := &consolemocks.Context{} mockContext.On("Argument", 0).Return("").Once() diff --git a/config/application.go b/config/application.go index b7159a5f5..e472d4544 100644 --- a/config/application.go +++ b/config/application.go @@ -20,8 +20,8 @@ type Application struct { func NewApplication(envPath string) *Application { if !file.Exists(envPath) { - color.Redln("Please create .env and initialize it first.") - color.Warnln("Run command: \ncp .env.example .env && go run . artisan key:generate") + color.Redln("Please create " + envPath + " and initialize it first.") + color.Warnln("Example command: \ncp .env.example .env && go run . artisan key:generate") os.Exit(0) } @@ -42,13 +42,13 @@ func NewApplication(envPath string) *Application { if support.Env != support.EnvArtisan { if appKey == nil { color.Redln("Please initialize APP_KEY first.") - color.Warnln("Run command: \ngo run . artisan key:generate") + color.Warnln("Example command: \ngo run . artisan key:generate") os.Exit(0) } if len(appKey.(string)) != 32 { color.Redln("Invalid APP_KEY, please reset it.") - color.Warnln("Run command: \ngo run . artisan key:generate") + color.Warnln("Example command: \ngo run . artisan key:generate") os.Exit(0) } } diff --git a/console/application.go b/console/application.go index f82027cf4..833fc4789 100644 --- a/console/application.go +++ b/console/application.go @@ -42,36 +42,43 @@ func (c *Application) Register(commands []console.Command) { } } -//Call Run an Artisan console command by name. +// Call Run an Artisan console command by name. func (c *Application) Call(command string) { c.Run(append([]string{os.Args[0], "artisan"}, strings.Split(command, " ")...), false) } -//CallAndExit Run an Artisan console command by name and exit. +// CallAndExit Run an Artisan console command by name and exit. func (c *Application) CallAndExit(command string) { c.Run(append([]string{os.Args[0], "artisan"}, strings.Split(command, " ")...), true) } -//Run a command. Args come from os.Args. +// Run a command. Args come from os.Args. func (c *Application) Run(args []string, exitIfArtisan bool) { - if len(args) >= 2 { - if args[1] == "artisan" { - if len(args) == 2 { - args = append(args, "--help") - } + artisanIndex := -1 + for i, arg := range args { + if arg == "artisan" { + artisanIndex = i + break + } + } - if args[2] != "-V" && args[2] != "--version" { - cliArgs := append([]string{args[0]}, args[2:]...) - if err := c.instance.Run(cliArgs); err != nil { - panic(err.Error()) - } + if artisanIndex != -1 { + // Add --help if no command argument is provided. + if artisanIndex+1 == len(args) { + args = append(args, "--help") + } + + if args[artisanIndex+1] != "-V" && args[artisanIndex+1] != "--version" { + cliArgs := append([]string{args[0]}, args[artisanIndex+1:]...) + if err := c.instance.Run(cliArgs); err != nil { + panic(err.Error()) } + } - printResult(args[2]) + printResult(args[artisanIndex+1]) - if exitIfArtisan { - os.Exit(0) - } + if exitIfArtisan { + os.Exit(0) } } } diff --git a/console/console/key_generate_command_test.go b/console/console/key_generate_command_test.go index 1858f4fae..f8f609499 100644 --- a/console/console/key_generate_command_test.go +++ b/console/console/key_generate_command_test.go @@ -8,6 +8,7 @@ import ( configmock "github.com/goravel/framework/contracts/config/mocks" consolemocks "github.com/goravel/framework/contracts/console/mocks" + "github.com/goravel/framework/support" "github.com/goravel/framework/support/file" ) @@ -53,3 +54,50 @@ func TestKeyGenerateCommand(t *testing.T) { mockConfig.AssertExpectations(t) } + +func TestKeyGenerateCommandWithCustomEnvFile(t *testing.T) { + support.EnvPath = "config.conf" + + mockConfig := &configmock.Config{} + mockConfig.On("GetString", "app.env").Return("local").Twice() + mockConfig.On("GetString", "app.key").Return("12345").Once() + + keyGenerateCommand := NewKeyGenerateCommand(mockConfig) + mockContext := &consolemocks.Context{} + + assert.False(t, file.Exists("config.conf")) + + assert.Nil(t, keyGenerateCommand.Handle(mockContext)) + + err := file.Create("config.conf", "APP_KEY=12345\n") + assert.Nil(t, err) + + assert.Nil(t, keyGenerateCommand.Handle(mockContext)) + assert.True(t, file.Exists("config.conf")) + env, err := os.ReadFile("config.conf") + assert.Nil(t, err) + assert.True(t, len(env) > 10) + + mockConfig.On("GetString", "app.env").Return("production").Once() + + reader, writer, err := os.Pipe() + assert.Nil(t, err) + originalStdin := os.Stdin + defer func() { os.Stdin = originalStdin }() + os.Stdin = reader + go func() { + defer writer.Close() + _, err = writer.Write([]byte("no\n")) + assert.Nil(t, err) + }() + + assert.Nil(t, keyGenerateCommand.Handle(mockContext)) + env, err = os.ReadFile("config.conf") + assert.Nil(t, err) + assert.True(t, len(env) > 10) + assert.Nil(t, file.Remove("config.conf")) + + support.EnvPath = ".env" + + mockConfig.AssertExpectations(t) +} diff --git a/crypt/aes.go b/crypt/aes.go index 0cc121526..334127528 100644 --- a/crypt/aes.go +++ b/crypt/aes.go @@ -7,12 +7,12 @@ import ( "encoding/base64" "errors" "io" - "os" "github.com/bytedance/sonic" "github.com/gookit/color" "github.com/goravel/framework/contracts/config" + "github.com/goravel/framework/support" ) type AES struct { @@ -23,15 +23,14 @@ type AES struct { func NewAES(config config.Config) *AES { key := config.GetString("app.key") - // Don't use AES in artisan key:generate command - args := os.Args - if len(args) >= 3 && args[1] == "artisan" && args[2] == "key:generate" { + // Don't use AES in artisan when the key is empty. + if support.Env == support.EnvArtisan && len(key) == 0 { return nil } // check key length before using it if len(key) != 16 && len(key) != 24 && len(key) != 32 { - color.Redln("[Crypt] Empty or invalid APP_KEY, please reset it.\nRun command:\ngo run . artisan key:generate") + color.Redln("[Crypt] Empty or invalid APP_KEY, please reset it.\nExample command:\ngo run . artisan key:generate") return nil } keyBytes := []byte(key) diff --git a/foundation/application.go b/foundation/application.go index 31e96805e..c162d31d5 100644 --- a/foundation/application.go +++ b/foundation/application.go @@ -175,8 +175,11 @@ func setEnv() { support.Env = support.EnvTest } if len(args) >= 2 { - if args[1] == "artisan" { - support.Env = support.EnvArtisan + for _, arg := range args[1:] { + if arg == "artisan" { + support.Env = support.EnvArtisan + break + } } } diff --git a/route/gin.go b/route/gin.go index 12ee4ae20..9bcf869cf 100644 --- a/route/gin.go +++ b/route/gin.go @@ -12,6 +12,7 @@ import ( httpcontract "github.com/goravel/framework/contracts/http" "github.com/goravel/framework/contracts/route" goravelhttp "github.com/goravel/framework/http" + "github.com/goravel/framework/support" ) type Gin struct { @@ -120,7 +121,7 @@ func (r *Gin) ServeHTTP(writer http.ResponseWriter, request *http.Request) { } func (r *Gin) outputRoutes() { - if r.config.GetBool("app.debug") && !runningInConsole() { + if r.config.GetBool("app.debug") && support.Env != support.EnvArtisan { for _, item := range r.instance.Routes() { fmt.Printf("%-10s %s\n", item.Method, colonToBracket(item.Path)) } diff --git a/route/utils.go b/route/utils.go index 827e80f78..1dc97185c 100644 --- a/route/utils.go +++ b/route/utils.go @@ -2,7 +2,6 @@ package route import ( "fmt" - "os" "regexp" "strings" "time" @@ -99,9 +98,3 @@ func mergeSlashForPath(path string) string { return strings.ReplaceAll(path, "//", "/") } - -func runningInConsole() bool { - args := os.Args - - return len(args) >= 2 && args[1] == "artisan" -} diff --git a/support/file/file.go b/support/file/file.go index 3d8ea3c5f..69bda1096 100644 --- a/support/file/file.go +++ b/support/file/file.go @@ -50,10 +50,7 @@ func Create(file string, content string) error { func Exists(file string) bool { _, err := os.Stat(file) - if err != nil { - return os.IsExist(err) - } - return true + return err == nil } // Extension Supported types: https://github.com/gabriel-vasile/mimetype/blob/master/supported_mimes.md