Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
itpey committed Apr 28, 2024
1 parent 89d580a commit 7c0db26
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 87 deletions.
2 changes: 1 addition & 1 deletion app/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func createProject(projectName string, templateName string) error {
}

// Copy project from template directory to project directory
if err := copyTemplate(templatePath, projectPath, true); err != nil {
if err := copyTemplate(templatePath, projectPath); err != nil {
return fmt.Errorf(color.RedString("Error: copying project: %v", err))
}

Expand Down
4 changes: 1 addition & 3 deletions app/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ _ __/ _ / _ /_/ // /_/ /
With figo, you can quickly scaffold out the directory structure, configuration files, and necessary boilerplate code for your Go applications.`
appCopyright = "Apache-2.0 license\nFor more information, visit the GitHub repository: https://github.com/itpey/figo"
defaultTemplatesRepoURL = "https://github.com/itpey/figo-templates"

metaDataDirectoryname = "figo"
metadataFileName = "figo.json"
metaDataDirectoryname = "figo"
)

var (
Expand Down
44 changes: 42 additions & 2 deletions app/doctor.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
// Copyright 2024 itpey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package app

import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"

"github.com/fatih/color"
)
Expand Down Expand Up @@ -30,10 +47,33 @@ func runDoctor() error {
// Check if both Git and Go are available
if err == nil {
fmt.Println(color.GreenString("All required tools are installed and accessible."))
fmt.Print(color.YellowString("Run 'figo help' for usage instructions."))
// Check if template directory is empty
templates, err := listTemplates()
if err != nil {
return err
}

if len(templates) == 0 {
fmt.Println(color.YellowString("Warning: No templates found in the templates directory."))

// Prompt user to download default templates
fmt.Println("Do you want to download default templates? (y/n): ")
reader := bufio.NewReader(os.Stdin)
answer, _ := reader.ReadString('\n')
answer = strings.TrimSpace(answer)
if answer == "y" || answer == "Y" {
err := downloadTemplates(defaultTemplatesRepoURL)
if err != nil {
return err
}
} else {
fmt.Println(color.YellowString("No templates downloaded."))
}
}
fmt.Println(color.YellowString("Run 'figo help' for usage instructions."))
} else {
fmt.Println(color.RedString("Error: system environment check completed with errors."))
fmt.Print(color.YellowString("Please ensure that Git and Go are installed and available in your PATH."))
fmt.Println(color.YellowString("Please ensure that Git and Go are installed and available in your PATH."))
}

return nil
Expand Down
133 changes: 53 additions & 80 deletions app/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package app

import (
"encoding/json"
"fmt"
"io"
"os"
Expand All @@ -25,39 +24,23 @@ import (
"github.com/fatih/color"
)

type TemplateMetadata struct {
Description string `json:"description"`
Author string `json:"author"`
}

func loadTemplateMetadata(templateDir string) (*TemplateMetadata, error) {
metadataFile := filepath.Join(templateDir, metadataFileName)

// Check if metadata file exists
if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf(color.RedString("Error: checking metadata file: %v", err))
}

// Read metadata file
data, err := os.ReadFile(metadataFile)
func extractAllTemplates(sourceDir string) error {
repoName, err := getRepositoryName(sourceDir)
if err != nil {
return nil, fmt.Errorf(color.RedString("Error: reading metadata file: %v", err))
fmt.Printf(color.RedString("Error: getting repository name for template '%s': %v\n"), sourceDir, err)
}

// Unmarshal metadata JSON
var metadata TemplateMetadata
if err := json.Unmarshal(data, &metadata); err != nil {
return nil, fmt.Errorf(color.RedString("Error: decoding metadata file: %v", err))
if isGoModule(sourceDir) {
destPath := filepath.Join(templatesDirectory, repoName)
if err := copyTemplate(sourceDir, destPath); err != nil {
fmt.Printf(color.RedString("Error:Error copying template '%s': %v\n"), repoName, err)
}
fmt.Printf("Template '%s' extracted successfully\n", repoName)
}

return &metadata, nil
}
func extractAllTemplates(sourceDir string) error {
files, err := os.ReadDir(sourceDir)
if err != nil {
return fmt.Errorf(color.RedString("Error: reading repository directory: %v", err))
return fmt.Errorf(color.RedString("Error: reading repository directory: %v"), err)
}

for _, file := range files {
Expand All @@ -69,119 +52,111 @@ func extractAllTemplates(sourceDir string) error {
continue
}

// Read template metadata
metaData, err := loadTemplateMetadata(templateDir)
if err != nil {
continue
}

// Skip if metadata is not available
if metaData == nil {
continue
}
// Check if the directory contains a Go module
if isGoModule(templateDir) {
// Get the base directory name (to use as template name)
templateName := fmt.Sprintf("%s/%s", repoName, file.Name())

// Check if the template already exists locally
destPath := filepath.Join(templatesDirectory, file.Name())
if _, err := os.Stat(destPath); err == nil {
fmt.Print(color.YellowString("template '%s' already exists, updating to the latest version\n", file.Name()))
if err := os.RemoveAll(destPath); err != nil {
fmt.Print(color.RedString("Error: removing existing template '%s': %v\n", file.Name(), err))
// Copy template directory to local templates directory
destPath := filepath.Join(templatesDirectory, templateName)
if err := copyTemplate(templateDir, destPath); err != nil {
fmt.Printf(color.RedString("Error: copying template '%s': %v\n"), templateName, err)
continue
}
}

// Copy template directory to local templates directory
if err := copyTemplate(templateDir, destPath, false); err != nil {
fmt.Print(color.RedString("Error: copying template '%s': %v\n", file.Name(), err))
continue
fmt.Printf("Template '%s' extracted successfully\n", templateName)
}

fmt.Print(color.GreenString("Template '%s' extracted successfully\n", file.Name()))
}
}

return nil
}

func copyTemplate(src, dest string, excludeMetadata bool) error {
func copyTemplate(src, dest string) error {
// Create destination directory if it doesn't exist
if err := os.MkdirAll(dest, 0755); err != nil {
return err
return fmt.Errorf(color.RedString("Error: failed to create destination directory: %v"), err)
}

// Traverse source directory
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
return fmt.Errorf(color.RedString("Error: accessing path %q: %v"), path, err)
}

// Get relative path within the source directory
relPath, err := filepath.Rel(src, path)
if err != nil {
return fmt.Errorf(color.RedString("Error: getting relative path for %q: %v"), path, err)
}

// Construct destination path
destPath := filepath.Join(dest, relPath)
// Check if the directory should be skipped
if info.IsDir() {
if shouldSkipDir(info.Name()) {
return filepath.SkipDir
}
// Create directory in destination
destPath := filepath.Join(dest, path[len(src):]) // Get relative path
if err := os.MkdirAll(destPath, info.Mode()); err != nil {
return err
return fmt.Errorf(color.RedString("Error: failed to create directory %q: %v"), destPath, err)
}
} else {
// Skip copying certain files
if shouldSkipFile(info.Name()) {
return nil
}

if excludeMetadata && info.Name() == metadataFileName {
return nil
}

// Copy file to destination
destPath := filepath.Join(dest, path[len(src):]) // Get relative path
srcFile, err := os.Open(path)
if err != nil {
return err
return fmt.Errorf(color.RedString("Error: failed to open source file %q: %v"), path, err)
}
defer srcFile.Close()

destFile, err := os.Create(destPath)
if err != nil {
return err
return fmt.Errorf(color.RedString("Error: failed to create destination file %q: %v"), destPath, err)
}
defer destFile.Close()

if _, err := io.Copy(destFile, srcFile); err != nil {
return err
return fmt.Errorf(color.RedString("Error: failed to copy file %q to %q: %v"), path, destPath, err)
}

// Preserve file mode
if err := os.Chmod(destPath, info.Mode()); err != nil {
return err
return fmt.Errorf(color.RedString("Error: failed to set file mode for %q: %v"), destPath, err)
}
}

return nil
})

if err != nil {
return fmt.Errorf(color.RedString("Error: copying template: %v"), err)
}

return nil
}

func printTemplateOptions(templates []string, currentIndex int) {
for i, tmpl := range templates {
templatePath := filepath.Join(templatesDirectory, tmpl)
if metadata, err := loadTemplateMetadata(templatePath); err == nil {
if i == currentIndex {
fmt.Print(color.GreenString("-> %s: %s | %s\n", tmpl, metadata.Description, metadata.Author))
} else {
fmt.Printf("%s: %s | %s\n", tmpl, metadata.Description, metadata.Author)
}

if i == currentIndex {
fmt.Print(color.GreenString("-> %s\n", tmpl))
} else {
fmt.Printf("%s\n", tmpl)
}

}

}

func downloadTemplates(url string) error {

if url == "" {
fmt.Print(color.RedString("No URL specified, using default repository: %s\n", defaultTemplatesRepoURL))
fmt.Print(color.YellowString("Warning: No URL specified, using default repository: %s\n", defaultTemplatesRepoURL))
url = defaultTemplatesRepoURL
}

fmt.Print(color.YellowString("downloading templates from repository: %s\n", url))
fmt.Print(color.YellowString("Downloading templates from repository: %s ...\n", url))

repoDir := repoDirectory
if err := gitClone(url, repoDir); err != nil {
Expand All @@ -193,8 +168,6 @@ func downloadTemplates(url string) error {
return fmt.Errorf(color.RedString("Error: extracting templates: %v", err))
}

fmt.Println(color.GreenString("Templates downloaded successfully"))

return nil
}

Expand Down
40 changes: 39 additions & 1 deletion app/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package app

import (
"encoding/json"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -56,7 +57,7 @@ func runCommand(command string, args []string, projectPath string, description s
return fmt.Errorf(color.RedString("Error: running %s: %v\n%s", description, err, output))
}

fmt.Printf(color.GreenString("%s finished successfully.\n\n"), description)
fmt.Printf(color.GreenString("%s finished successfully.\n"), description)
return nil
}

Expand Down Expand Up @@ -134,3 +135,40 @@ func clearConsole() {
cmd.Run()
}
}

func isGoModule(dir string) bool {
goModFile := filepath.Join(dir, "go.mod")

// Use os.Stat to check the existence of the go.mod file
if _, err := os.Stat(goModFile); err != nil {
if os.IsNotExist(err) {
return false // go.mod file does not exist
}
// Handle other errors (e.g., permission denied)
return false
}

return true // go.mod file exists
}

// getRepositoryName retrieves the repository name from the Go module
func getRepositoryName(dir string) (string, error) {
cmd := exec.Command("go", "list", "-m", "-json")
cmd.Dir = dir
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf(color.RedString("Error: running 'go list -m -json' in directory '%s': %v", dir, err))
}

var moduleInfo struct {
Path string `json:"Path"`
}
if err := json.Unmarshal(output, &moduleInfo); err != nil {
return "", fmt.Errorf(color.RedString("Error: parsing module information from 'go list -m -json' output: %v", err))
}

// Extract the repository name from the module import path
repoURL := moduleInfo.Path
repoName := filepath.Base(repoURL)
return repoName, nil
}

0 comments on commit 7c0db26

Please sign in to comment.