recherche
Maisondéveloppement back-endGolangProjet Go pour débutants – Créer un exécuteur de tâches dans Go

Beginner Go Project - Create a Task Runner in Go

Ce que nous allons construire

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

Code complet

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!

Déclaration
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Golang en action: Exemples et applications du monde réelGolang en action: Exemples et applications du monde réelApr 12, 2025 am 12:11 AM

Golang excelle dans les applications pratiques et est connu pour sa simplicité, son efficacité et sa concurrence. 1) La programmation simultanée est implémentée via des goroutines et des canaux, 2) le code flexible est écrit à l'aide d'interfaces et de polymorphismes, 3) Simplifier la programmation réseau avec des packages Net / HTTP, 4) Construire des robots concurrents efficaces, 5) Déboggage et optimisation par le biais d'outils et de meilleures pratiques.

Golang: le langage de programmation Go expliquéGolang: le langage de programmation Go expliquéApr 10, 2025 am 11:18 AM

Les caractéristiques principales de GO incluent la collection de déchets, la liaison statique et le support de concurrence. 1. Le modèle de concurrence du langage GO réalise une programmation concurrente efficace via le goroutine et le canal. 2. Les interfaces et les polymorphismes sont implémentés via des méthodes d'interface, de sorte que différents types peuvent être traités de manière unifiée. 3. L'utilisation de base démontre l'efficacité de la définition et de l'appel des fonctions. 4. Dans une utilisation avancée, les tranches offrent des fonctions puissantes de redimensionnement dynamique. 5. Des erreurs courantes telles que les conditions de course peuvent être détectées et résolues par l'imagerie. 6. Optimisation des performances Réutiliser les objets via Sync.Pool pour réduire la pression de collecte des ordures.

Objectif de Golang: Construire des systèmes efficaces et évolutifsObjectif de Golang: Construire des systèmes efficaces et évolutifsApr 09, 2025 pm 05:17 PM

GO Language fonctionne bien dans la construction de systèmes efficaces et évolutifs. Ses avantages incluent: 1. Haute performance: compilé en code machine, vitesse de course rapide; 2. Programmation simultanée: simplifier le multitâche via les goroutines et les canaux; 3. Simplicité: syntaxe concise, réduction des coûts d'apprentissage et de maintenance; 4. Plate-forme multipliée: prend en charge la compilation multiplateforme, déploiement facile.

Pourquoi les résultats d'ordre par des déclarations dans le tri SQL semblent-ils parfois aléatoires?Pourquoi les résultats d'ordre par des déclarations dans le tri SQL semblent-ils parfois aléatoires?Apr 02, 2025 pm 05:24 PM

Confus quant au tri des résultats de la requête SQL. Dans le processus d'apprentissage de SQL, vous rencontrez souvent des problèmes déroutants. Récemment, l'auteur lit "Mick-SQL Basics" ...

La convergence de la pile technologique est-elle juste un processus de sélection de la pile technologique?La convergence de la pile technologique est-elle juste un processus de sélection de la pile technologique?Apr 02, 2025 pm 05:21 PM

La relation entre la convergence des piles technologiques et la sélection de la technologie dans le développement de logiciels, la sélection et la gestion des piles technologiques sont un problème très critique. Récemment, certains lecteurs ont proposé ...

Comment utiliser la comparaison de la réflexion et gérer les différences entre trois structures en Go?Comment utiliser la comparaison de la réflexion et gérer les différences entre trois structures en Go?Apr 02, 2025 pm 05:15 PM

Comment comparer et gérer trois structures en langue go. Dans la programmation GO, il est parfois nécessaire de comparer les différences entre deux structures et d'appliquer ces différences au ...

Comment afficher les packages installés à l'échelle mondiale dans Go?Comment afficher les packages installés à l'échelle mondiale dans Go?Apr 02, 2025 pm 05:12 PM

Comment afficher les packages installés à l'échelle mondiale dans Go? En train de se développer avec le langage Go, GO utilise souvent ...

See all articles

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
3 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
3 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
3 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
3 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌

Outils chauds

MinGW - GNU minimaliste pour Windows

MinGW - GNU minimaliste pour Windows

Ce projet est en cours de migration vers osdn.net/projects/mingw, vous pouvez continuer à nous suivre là-bas. MinGW : un port Windows natif de GNU Compiler Collection (GCC), des bibliothèques d'importation et des fichiers d'en-tête librement distribuables pour la création d'applications Windows natives ; inclut des extensions du runtime MSVC pour prendre en charge la fonctionnalité C99. Tous les logiciels MinGW peuvent fonctionner sur les plates-formes Windows 64 bits.

PhpStorm version Mac

PhpStorm version Mac

Le dernier (2018.2.1) outil de développement intégré PHP professionnel

SublimeText3 Linux nouvelle version

SublimeText3 Linux nouvelle version

Dernière version de SublimeText3 Linux

Listes Sec

Listes Sec

SecLists est le compagnon ultime du testeur de sécurité. Il s'agit d'une collection de différents types de listes fréquemment utilisées lors des évaluations de sécurité, le tout en un seul endroit. SecLists contribue à rendre les tests de sécurité plus efficaces et productifs en fournissant facilement toutes les listes dont un testeur de sécurité pourrait avoir besoin. Les types de listes incluent les noms d'utilisateur, les mots de passe, les URL, les charges utiles floues, les modèles de données sensibles, les shells Web, etc. Le testeur peut simplement extraire ce référentiel sur une nouvelle machine de test et il aura accès à tous les types de listes dont il a besoin.

Télécharger la version Mac de l'éditeur Atom

Télécharger la version Mac de l'éditeur Atom

L'éditeur open source le plus populaire