Rumah > pembangunan bahagian belakang > Golang > Projek Beginner Go - Buat Task Runner dalam Go

Projek Beginner Go - Buat Task Runner dalam Go

Linda Hamilton
Lepaskan: 2024-12-30 17:07:10
asal
614 orang telah melayarinya

Beginner Go Project - Create a Task Runner in Go

Apa yang akan kita bina

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 ./..."
Salin selepas log masuk

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
Salin selepas log masuk

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
Salin selepas log masuk

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.
}
Salin selepas log masuk

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
}
Salin selepas log masuk

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
}
Salin selepas log masuk

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
    }
}
Salin selepas log masuk

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
Salin selepas log masuk

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
Salin selepas log masuk

Kod Lengkap

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
    }
}
Salin selepas log masuk

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!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan