Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Lint

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
lint:
name: Lint
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.21.13"

- name: Verify dependencies
run: go mod verify

- name: Build
run: go build -v ./...

- name: Run go vet
run: go vet ./...

- name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest

- name: Run staticcheck
run: staticcheck ./...

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
args: --timeout=5m

- name: Check formatting
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
echo "The following files are not formatted:"
gofmt -s -l .
exit 1
fi

- name: Check go mod tidy
run: |
go mod tidy
if [ -n "$(git status --porcelain)" ]; then
echo "go mod tidy resulted in changes"
git diff
exit 1
fi
68 changes: 68 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
run:
timeout: 5m
issues-exit-code: 1
tests: true
modules-download-mode: readonly

output:
formats:
- format: colored-line-number

linters-settings:
depguard:
rules:
main:
files:
- $all
allow:
- $gostd
- github.com/luigimorel/gogen
- github.com/urfave/cli/v2
gofmt:
simplify: true
goimports:
local-prefixes: github.com/luigimorel/gogen
misspell:
locale: US

linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
- misspell
- goconst
- gosec
- unconvert
- dupl
- gocritic
- gocyclo
- whitespace
- bodyclose
- depguard
- dogsled

disable:
- funlen
- gochecknoglobals
- gocognit
- godot
- godox
- nestif

issues:
exclude-rules:
- path: _test\.go
linters:
- gosec
- dupl
- linters:
- lll
source: "^//go:generate "
max-issues-per-linter: 0
max-same-issues: 0
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test-coverage:
go test -cover ./...

fmt:
go fmt ./...
go fmt ./... && goimports -w . && gofmt -s -w .

lint:
golangci-lint run
Expand Down
23 changes: 15 additions & 8 deletions cmd/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ import (
"os/exec"
"strings"

"github.com/luigimorel/gogen/internal"
"github.com/urfave/cli/v2"

"github.com/luigimorel/gogen/internal"
)

// Runtime constants
const (
node = "node"
bun = "bun"
)
Comment on lines +13 to 17
Copy link

Copilot AI Sep 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constants are duplicated across multiple files (internal/create_frontend.go and cmd/frontend.go). Consider moving all shared constants to a common package to avoid duplication and maintain consistency.

Suggested change
// Runtime constants
const (
node = "node"
bun = "bun"
)
// Runtime constants are now defined in the internal package as internal.Node and internal.Bun

Copilot uses AI. Check for mistakes.

type FrontendManager struct {
Expand Down Expand Up @@ -113,14 +120,14 @@ func (fm *FrontendManager) execute() error {

func (fm *FrontendManager) validateSetup() error {
switch fm.Runtime {
case "node":
case node:
if !fm.commandExists("node") {
return fmt.Errorf("node.js is required but not installed. Please install Node.js from https://nodejs.org/")
}
if !fm.commandExists("npm") {
return fmt.Errorf("npm is required but not installed. Please install npm")
}
case "bun":
case bun:
if !fm.commandExists("bun") {
return fmt.Errorf("bun is required but not installed. Please install Bun from https://bun.sh/")
}
Expand All @@ -130,13 +137,13 @@ func (fm *FrontendManager) validateSetup() error {

switch fm.FrameworkType {
case "angular":
if fm.Runtime == "node" && !fm.commandExists("ng") {
if fm.Runtime == node && !fm.commandExists("ng") {
fmt.Println("Angular CLI not found. Installing @angular/cli globally...")
var cmd *exec.Cmd
switch fm.Runtime {
case "node":
case node:
cmd = exec.Command("npm", "install", "-g", "@angular/cli")
case "bun":
case bun:
cmd = exec.Command("bun", "add", "-g", "@angular/cli")
}

Expand Down Expand Up @@ -167,9 +174,9 @@ func (fm *FrontendManager) printInstructions() {

var devCommand string
switch fm.Runtime {
case "node":
case node:
devCommand = "npm run dev"
case "bun":
case bun:
devCommand = "bun run dev"
default:
devCommand = "npm run dev"
Expand Down
37 changes: 26 additions & 11 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
Expand All @@ -13,6 +14,10 @@ import (
"github.com/urfave/cli/v2"
)

const (
PlatformWindows = "windows"
)

type Installer struct {
Method string
Force bool
Expand Down Expand Up @@ -86,7 +91,7 @@ func (i *Installer) autoInstall() error {
return i.nixInstall()
}
return i.binaryInstall()
case "windows":
case PlatformWindows:
return i.binaryInstall()
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
Expand Down Expand Up @@ -114,7 +119,7 @@ func (i *Installer) binaryInstall() error {
return fmt.Errorf("failed to download binary: %w", err)
}

if runtime.GOOS != "windows" {
if runtime.GOOS != PlatformWindows {
if err := os.Chmod(binPath, 0755); err != nil {
return fmt.Errorf("failed to make binary executable: %w", err)
}
Expand Down Expand Up @@ -156,7 +161,7 @@ func (i *Installer) fileExists(path string) bool {

func (i *Installer) getBinaryInstallDir() string {
switch runtime.GOOS {
case "windows":
case PlatformWindows:
// Use %USERPROFILE%\AppData\Local\gogen
home, _ := os.UserHomeDir()
return filepath.Join(home, "AppData", "Local", "gogen")
Expand All @@ -168,7 +173,7 @@ func (i *Installer) getBinaryInstallDir() string {
}

func (i *Installer) getBinaryName() string {
if runtime.GOOS == "windows" {
if runtime.GOOS == PlatformWindows {
return "gogen.exe"
}
return "gogen"
Expand All @@ -184,15 +189,24 @@ func (i *Installer) getDownloadURL() string {

version := "latest"
filename := fmt.Sprintf("gogen_%s_%s", os, arch)
if runtime.GOOS == "windows" {
if runtime.GOOS == PlatformWindows {
filename += ".exe"
}

return fmt.Sprintf("https://github.com/luigimorel/gogen/releases/%s/download/%s", version, filename)
}

func (i *Installer) downloadFile(url, filepath string) error {
resp, err := http.Get(url)
func (i *Installer) downloadFile(downloadURL, filepath string) error {
u, err := url.Parse(downloadURL)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
}

if !strings.HasPrefix(u.String(), "https://github.com/luigimorel/gogen/releases/") {
return fmt.Errorf("invalid download URL: %s", u.String())
}

resp, err := http.Get(u.String())
if err != nil {
return err
}
Expand All @@ -214,7 +228,7 @@ func (i *Installer) downloadFile(url, filepath string) error {

func (i *Installer) printPathInstructions(binDir string) {
switch runtime.GOOS {
case "windows":
case PlatformWindows:
fmt.Println("\nTo add gogen to your PATH:")
fmt.Println("1. Press Win + R, type 'sysdm.cpl', and press Enter")
fmt.Println("2. Click 'Environment Variables'")
Expand All @@ -224,13 +238,14 @@ func (i *Installer) printPathInstructions(binDir string) {
fmt.Println("6. Restart your terminal and run 'gogen --help'")
default:
shell := os.Getenv("SHELL")
if strings.Contains(shell, "fish") {
switch {
case strings.Contains(shell, "fish"):
fmt.Printf("\nTo add gogen to your PATH, run:\n")
fmt.Printf("fish_add_path %s\n", binDir)
} else if strings.Contains(shell, "zsh") {
case strings.Contains(shell, "zsh"):
fmt.Printf("\nTo add gogen to your PATH, add this to your ~/.zshrc:\n")
fmt.Printf("export PATH=\"%s:$PATH\"\n", binDir)
} else {
default:
fmt.Printf("\nTo add gogen to your PATH, add this to your ~/.bashrc or ~/.profile:\n")
fmt.Printf("export PATH=\"%s:$PATH\"\n", binDir)
}
Expand Down
24 changes: 17 additions & 7 deletions cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import (
"os"
"os/exec"

"github.com/luigimorel/gogen/internal"
"github.com/urfave/cli/v2"

"github.com/luigimorel/gogen/internal"
)

// Template constants
const (
TemplateWeb = "web"
)

type ProjectCreator struct {
Expand Down Expand Up @@ -114,7 +120,7 @@ This command will create a new directory, initialize a Go module, and create a n

// Check if runtime was explicitly set by user
runtimeExplicitlySet := c.IsSet("runtime")
if runtimeExplicitlySet && template != "web" {
if runtimeExplicitlySet && template != TemplateWeb {
return fmt.Errorf("runtime flag is only applicable when template is 'web'")
}

Expand Down Expand Up @@ -163,7 +169,7 @@ func (pc *ProjectCreator) execute() error {
}

func (pc *ProjectCreator) validate() error {
if pc.FrontendFramework != "" && pc.Template != "web" {
if pc.FrontendFramework != "" && pc.Template != TemplateWeb {
return fmt.Errorf("frontend flag is only applicable when template is 'web'")
}

Expand Down Expand Up @@ -201,7 +207,7 @@ func (pc *ProjectCreator) ChangeToProjectDirectory() (string, func(), error) {
}

func (pc *ProjectCreator) initializeGoModule() error {
if pc.Template != "web" {
if pc.Template != TemplateWeb {
moduleName := pc.ModuleName
if moduleName == "" {
moduleName = pc.Name
Expand All @@ -222,7 +228,7 @@ func (pc *ProjectCreator) createProjectFiles() error {
switch pc.Template {
case "cli":
return pg.CreateCLIProject(pc.Name, pc.ModuleName)
case "web":
case TemplateWeb:
return pg.CreateWebProject(pc.Name, pc.ModuleName, pc.Router, pc.FrontendFramework, pc.Runtime, pc.UseTypeScript, pc.UseTailwind)
case "api":
return pg.CreateAPIProject(pc.Name, pc.ModuleName, pc.Router)
Expand All @@ -235,7 +241,11 @@ func (pc *ProjectCreator) createEditorLLMRules() error {
if err := os.Chdir(".."); err != nil {
return fmt.Errorf("failed to change to project root directory: %w", err)
}
defer os.Chdir(pc.DirName)
defer func() {
if err := os.Chdir(pc.DirName); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to change back to %s directory: %v\n", pc.DirName, err)
}
}()

llmTemplate := internal.NewLLMTemplate()
return llmTemplate.CreateTemplate(pc.Editor, pc.FrontendFramework, pc.Runtime, pc.Router)
Expand All @@ -245,7 +255,7 @@ func (pc *ProjectCreator) printNextSteps() {
fmt.Println("\nNext steps:")
fmt.Printf(" cd %s\n", pc.Name)

if pc.Template == "web" {
if pc.Template == TemplateWeb {
fmt.Println(" cd api")
fmt.Println(" go run main.go")
if pc.FrontendFramework != "" {
Expand Down
Loading