Nous allons créer un outil comme make que nous pouvons utiliser pour exécuter des tâches en utilisant un simple fichier yaml comme celui-ci.
tasks: build: description: "compile the project" command: "go build main.go" dependencies: [test] test: description: "run unit tests" command: "go test -v ./..."
Commençons, nous devons d’abord définir le plan d’action. Nous avons déjà défini le schéma du fichier de tâches. Nous pouvons utiliser json au lieu de yaml mais pour le bien de ce projet, nous allons utiliser des fichiers yml.
D'après le fichier, nous pouvons voir que nous aurons besoin d'une structure pour stocker une seule tâche et d'un moyen d'exécuter les tâches dépendantes avant de passer à la tâche principale. Commençons par lancer notre projet. Créez un nouveau dossier et exécutez :
go mod init github.com/vishaaxl/mommy
Vous pouvez nommer votre projet comme vous le souhaitez, je choisis ce nom de « maman ». Nous devons également installer un package pour travailler avec les fichiers yaml - en les convertissant essentiellement en un objet cartographique. Allez-y et installez le package suivant.
go get gopkg.in/yaml.v3
Ensuite, créez un nouveau fichier main.go et commencez par définir la structure « Tâche ».
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. }
Celui-ci est assez explicite. Cela conservera la valeur de chaque tâche individuelle. Ensuite, nous avons besoin d'une structure supplémentaire pour stocker la liste des tâches et charger le contenu du fichier .yaml dans ce nouvel objet.
// 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 }
Ensuite, nous devons créer une fonction qui exécute une seule tâche. Nous utiliserons le module os/exec pour exécuter la tâche dans le shell. Dans Golang, le package os/exec fournit un moyen d'exécuter des commandes shell et des programmes externes.
// 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 }
Maintenant, nous avons tous les éléments constitutifs du programme que nous pouvons utiliser dans la fonction principale pour charger le fichier de configuration et commencer l'automatisation. Nous allons utiliser le package flag pour lire les indicateurs de ligne de commande.
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 } }
Testons le tout, créons un nouveau Mommy.yaml et collez-y le code yaml depuis le début. nous utiliserons notre exécuteur de tâches pour créer des binaires pour notre projet. Exécuter :
go run main.go -task build
Si tout se passe bien, vous verrez un nouveau fichier .exe à la racine du dossier. Génial, nous avons maintenant un exécuteur de tâches fonctionnel. Nous pouvons ajouter l'emplacement de ce fichier .exe dans les variables d'environnement de notre système et l'utiliser de n'importe où en utilisant :
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 } }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!