このような単純な yaml ファイルを使用してタスクを実行できる make のようなツールを作成します。
tasks: build: description: "compile the project" command: "go build main.go" dependencies: [test] test: description: "run unit tests" command: "go test -v ./..."
始めましょう。まず、行動方針の概要を説明する必要があります。タスク ファイルのスキーマはすでに定義されています。 yaml の代わりに json を使用することもできますが、このプロジェクトでは yml ファイルを使用します。
ファイルから、メインのタスクを進める前に、単一のタスクを保存するための構造体と、依存タスクを実行する方法が必要であることがわかります。まずはプロジェクトを開始しましょう。新しいフォルダーを作成して実行します:
go mod init github.com/vishaaxl/mommy
プロジェクトには好きな名前を付けることができますが、私はこの「ママ」という名前を使います。また、yaml ファイルを操作するためのパッケージをインストールする必要があります (基本的に、yaml ファイルをマップ オブジェクトに変換します)。次のパッケージをインストールしてください。
go get gopkg.in/yaml.v3
次に、新しい main.go ファイルを作成し、「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. }
これは非常に説明不要です。これには、個々のタスクの値が保持されます。次に、タスクのリストを保存し、.yaml ファイルの内容をこの新しいオブジェクトにロードするために、もう 1 つの構造体が必要です。
// 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 }
次に、単一のタスクを実行する関数を作成する必要があります。 os/exec モジュールを使用してシェルでタスクを実行します。 Golang では、os/exec パッケージはシェル コマンドと外部プログラムを実行する方法を提供します。
// 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 }
これで、プログラムの構成要素がすべて揃ったので、それらを main 関数で使用して構成ファイルをロードし、自動化を開始できます。コマンドラインフラグを読み取るためにフラグパッケージを使用します。
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 } }
全体をテストして、新しい Mommy.yaml を作成し、そこに yaml コードを最初から貼り付けてみましょう。タスクランナーを使用してプロジェクトのバイナリを作成します。実行:
go run main.go -task build
すべてがうまくいけば、フォルダーのルートに新しい .exe ファイルが表示されます。見事、タスクランナーが動作するようになりました。この .exe ファイルの場所をシステムの環境変数に追加し、次を使用してどこからでもこれを使用できます。
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 } }
以上が初心者向け Go プロジェクト - Go でタスク ランナーを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。