Kami akan membuat alat seperti make yang boleh kami gunakan menjalankan tugas menggunakan fail yaml mudah seperti ini.
tasks: build: description: "compile the project" command: "go build main.go" dependencies: [test] test: description: "run unit tests" command: "go test -v ./..."
Mari mulakan, mula-mula kita perlu menggariskan tindakan. Kami telah pun menentukan skema fail tugasan. Kami boleh menggunakan json dan bukannya yaml tetapi demi projek ini kami akan menggunakan fail yml.
Daripada fail, kita dapat melihat bahawa kita memerlukan struct untuk menyimpan satu tugasan dan satu cara untuk menjalankan tugas bergantung sebelum meneruskan dengan tugas utama. Mari mulakan dengan memulakan projek kami. Cipta folder baharu dan jalankan:
go mod init github.com/vishaaxl/mommy
Anda boleh menamakan projek anda mengikut kehendak anda, saya akan menggunakan nama 'mommy' ini. Kami juga perlu memasang beberapa pakej untuk berfungsi dengan fail yaml - pada asasnya menukarnya menjadi objek peta. Teruskan dan pasang pakej berikut.
go get gopkg.in/yaml.v3
Seterusnya buat fail main.go baharu dan mulakan dengan mentakrifkan struct 'Task'.
package main import ( "gopkg.in/yaml.v3" ) // Task defines the structure of a task in the configuration file. // Each task has a description, a command to run, and a list of dependencies // (other tasks that need to be completed before this task). type Task struct { Description string `yaml:"description"` // A brief description of the task. Command string `yaml:"command"` // The shell command to execute for the task. Dependencies []string `yaml:"dependencies"` // List of tasks that need to be completed before this task. }
Yang ini cukup jelas. Ini akan mengekalkan nilai setiap tugasan individu. Seterusnya kita memerlukan satu lagi struct untuk menyimpan senarai tugas dan memuatkan kandungan fail .yaml ke dalam objek baharu ini.
// Config represents the entire configuration file, // which contains a map of tasks by name. type Config struct { Tasks map[string]Task `yaml:"tasks"` // A map of task names to task details. } // loadConfig reads and parses the configuration file (e.g., Makefile.yaml), // and returns a Config struct containing the tasks and their details. func loadConfig(filename string) (Config, error) { // Read the content of the config file. data, err := os.ReadFile(filename) if err != nil { return Config{}, err } // Unmarshal the YAML data into a Config struct. var config Config err = yaml.Unmarshal(data, &config) if err != nil { return Config{}, err } return config, nil }
Seterusnya kita perlu mencipta fungsi yang melaksanakan satu tugasan. Kami akan menggunakan modul os/exec untuk menjalankan tugas dalam shell. Di Golang, pakej os/exec menyediakan cara untuk melaksanakan arahan shell dan program luaran.
// executeTask recursively executes the specified task and its dependencies. // It first ensures that all dependencies are executed before running the current task's command. func executeTask(taskName string, tasks map[string]Task, executed map[string]bool) error { // If the task has already been executed, skip it. if executed[taskName] { return nil } // Get the task details from the tasks map. task, exists := tasks[taskName] if !exists { return fmt.Errorf("task %s not found", taskName) } // First, execute all the dependencies of this task. for _, dep := range task.Dependencies { // Recursively execute each dependency. if err := executeTask(dep, tasks, executed); err != nil { return err } } // Now that dependencies are executed, run the task's command. fmt.Printf("Running task: %s\n", taskName) fmt.Printf("Command: %s\n", task.Command) // Execute the task's command using the shell (sh -c allows for complex shell commands). cmd := exec.Command("sh", "-c", task.Command) cmd.Stdout = os.Stdout // Direct standard output to the terminal. cmd.Stderr = os.Stderr // Direct error output to the terminal. // Run the command and check for any errors. if err := cmd.Run(); err != nil { return fmt.Errorf("failed to execute command %s: %v", task.Command, err) } // Mark the task as executed. executed[taskName] = true return nil }
Kini kami mempunyai semua blok binaan program yang boleh kami gunakan dalam fungsi utama untuk memuatkan fail konfigurasi dan mula mengautomasikan. Kami akan menggunakan pakej bendera untuk membaca bendera baris arahan.
func main() { // Define command-line flags configFile := flag.String("f", "Mommy.yaml", "Path to the configuration file") // Path to the config file (defaults to Makefile.yaml) taskName := flag.String("task", "", "Task to execute") // The task to execute (required flag) // Parse the flags flag.Parse() // Check if the task flag is provided if *taskName == "" { fmt.Println("Error: Please specify a task using -task flag.") os.Exit(1) // Exit if no task is provided } // Load the configuration file config, err := loadConfig(*configFile) if err != nil { fmt.Printf("Failed to load config: %v\n", err) os.Exit(1) // Exit if the configuration file can't be loaded } // Map to track which tasks have been executed already (avoiding re-execution). executed := make(map[string]bool) // Start executing the specified task (with dependencies) if err := executeTask(*taskName, config.Tasks, executed); err != nil { fmt.Printf("Error executing task: %v\n", err) os.Exit(1) // Exit if task execution fails } }
Mari kita uji semuanya, buat Mommy.yaml baharu dan tampal kod yaml dari mula ke dalamnya. kami akan menggunakan pelari tugas untuk mencipta binari untuk projek kami. Lari:
go run main.go -task build
Jika semuanya berjalan lancar, anda akan melihat fail .exe baharu dalam akar folder. Hebat, kami mempunyai pelari tugas yang bekerja sekarang. Kami boleh menambah lokasi fail .exe ini dalam pembolehubah persekitaran sistem kami dan menggunakannya dari mana-mana sahaja menggunakan:
mommy -task build
package main import ( "flag" "fmt" "os" "os/exec" "gopkg.in/yaml.v3" ) // Task defines the structure of a task in the configuration file. // Each task has a description, a command to run, and a list of dependencies // (other tasks that need to be completed before this task). type Task struct { Description string `yaml:"description"` // A brief description of the task. Command string `yaml:"command"` // The shell command to execute for the task. Dependencies []string `yaml:"dependencies"` // List of tasks that need to be completed before this task. } // Config represents the entire configuration file, // which contains a map of tasks by name. type Config struct { Tasks map[string]Task `yaml:"tasks"` // A map of task names to task details. } // loadConfig reads and parses the configuration file (e.g., Makefile.yaml), // and returns a Config struct containing the tasks and their details. func loadConfig(filename string) (Config, error) { // Read the content of the config file. data, err := os.ReadFile(filename) if err != nil { return Config{}, err } // Unmarshal the YAML data into a Config struct. var config Config err = yaml.Unmarshal(data, &config) if err != nil { return Config{}, err } return config, nil } // executeTask recursively executes the specified task and its dependencies. // It first ensures that all dependencies are executed before running the current task's command. func executeTask(taskName string, tasks map[string]Task, executed map[string]bool) error { // If the task has already been executed, skip it. if executed[taskName] { return nil } // Get the task details from the tasks map. task, exists := tasks[taskName] if !exists { return fmt.Errorf("task %s not found", taskName) } // First, execute all the dependencies of this task. for _, dep := range task.Dependencies { // Recursively execute each dependency. if err := executeTask(dep, tasks, executed); err != nil { return err } } // Now that dependencies are executed, run the task's command. fmt.Printf("Running task: %s\n", taskName) fmt.Printf("Command: %s\n", task.Command) // Execute the task's command using the shell (sh -c allows for complex shell commands). cmd := exec.Command("sh", "-c", task.Command) cmd.Stdout = os.Stdout // Direct standard output to the terminal. cmd.Stderr = os.Stderr // Direct error output to the terminal. // Run the command and check for any errors. if err := cmd.Run(); err != nil { return fmt.Errorf("failed to execute command %s: %v", task.Command, err) } // Mark the task as executed. executed[taskName] = true return nil } func main() { // Define command-line flags configFile := flag.String("f", "Makefile.yaml", "Path to the configuration file") // Path to the config file (defaults to Makefile.yaml) taskName := flag.String("task", "", "Task to execute") // The task to execute (required flag) // Parse the flags flag.Parse() // Check if the task flag is provided if *taskName == "" { fmt.Println("Error: Please specify a task using -task flag.") os.Exit(1) // Exit if no task is provided } // Load the configuration file config, err := loadConfig(*configFile) if err != nil { fmt.Printf("Failed to load config: %v\n", err) os.Exit(1) // Exit if the configuration file can't be loaded } // Map to track which tasks have been executed already (avoiding re-execution). executed := make(map[string]bool) // Start executing the specified task (with dependencies) if err := executeTask(*taskName, config.Tasks, executed); err != nil { fmt.Printf("Error executing task: %v\n", err) os.Exit(1) // Exit if task execution fails } }
Atas ialah kandungan terperinci Projek Beginner Go - Buat Task Runner dalam Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!