2024-02-06 13:00:48 +00:00
|
|
|
//go:build mage
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log/slog"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/go-git/go-git/v5"
|
|
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
|
|
"github.com/goccy/go-yaml"
|
|
|
|
)
|
|
|
|
|
|
|
|
type projectsConfig struct {
|
2024-02-06 14:58:17 +00:00
|
|
|
Projects []project `yaml:"projects"`
|
2024-02-06 13:00:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type project struct {
|
2024-02-06 14:58:17 +00:00
|
|
|
Name string `yaml:"name"`
|
2024-02-06 13:00:48 +00:00
|
|
|
RepositoryURL string `yaml:"repository_url"`
|
|
|
|
Commit string `yaml:"commit"`
|
|
|
|
Tag string `yaml:"tag"`
|
|
|
|
Branch string `yaml:"branch"`
|
|
|
|
GitReference string `yaml:"git_reference"`
|
|
|
|
DocumentationRoot string `yaml:"documentation_root"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
configFile = "./flow/nanoc.yaml"
|
|
|
|
projectsDirectory = "./flow/content/projects"
|
|
|
|
)
|
|
|
|
|
|
|
|
func Projects() error {
|
|
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
slog.SetDefault(logger)
|
|
|
|
|
|
|
|
projects, err := parseProjectsConfig()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to parse the projects configuration; %w", err)
|
|
|
|
}
|
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
for _, project := range projects.Projects {
|
|
|
|
clonedDirectory, err := cloneProject(project)
|
2024-02-06 13:00:48 +00:00
|
|
|
if err != nil {
|
2024-02-06 14:58:17 +00:00
|
|
|
return fmt.Errorf("unable to clone %q; %w", project.Name, err)
|
2024-02-06 13:00:48 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
documentationDir := filepath.Join(clonedDirectory, project.DocumentationRoot)
|
2024-02-06 13:00:48 +00:00
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
if err := copyDocumentation(documentationDir, project.Name); err != nil {
|
|
|
|
return fmt.Errorf("unable to copy the documentation from %q; %w", project.Name, err)
|
2024-02-06 13:00:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseProjectsConfig() (projectsConfig, error) {
|
|
|
|
slog.Info("Parsing configuration.", "config file", configFile)
|
|
|
|
|
|
|
|
var config projectsConfig
|
|
|
|
|
|
|
|
data, err := os.ReadFile(configFile)
|
|
|
|
if err != nil {
|
|
|
|
return config, fmt.Errorf("unable to read %q; %w", configFile, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
|
|
|
return config, fmt.Errorf("unable to Unmarshal the configuration; %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
slog.Info("Configuration parsed successfully.")
|
|
|
|
|
|
|
|
return config, nil
|
|
|
|
}
|
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
func cloneProject(p project) (string, error) {
|
|
|
|
slog.Info("Cloning the project repository.", "repository", p.RepositoryURL)
|
2024-02-06 13:00:48 +00:00
|
|
|
var (
|
|
|
|
reference plumbing.ReferenceName
|
|
|
|
singleBranch bool
|
|
|
|
depth int
|
|
|
|
)
|
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
if p.Tag != "" {
|
|
|
|
reference = plumbing.NewTagReferenceName(p.Tag)
|
2024-02-06 13:00:48 +00:00
|
|
|
singleBranch = false
|
|
|
|
depth = 1
|
2024-02-06 14:58:17 +00:00
|
|
|
} else if p.Branch != "" {
|
|
|
|
reference = plumbing.NewBranchReferenceName(p.Branch)
|
2024-02-06 13:00:48 +00:00
|
|
|
singleBranch = true
|
|
|
|
depth = 1
|
|
|
|
} else {
|
|
|
|
reference = plumbing.Main
|
|
|
|
singleBranch = false
|
|
|
|
depth = 0
|
|
|
|
}
|
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
cloneDir, err := os.MkdirTemp("", p.Name)
|
2024-02-06 13:00:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("unable to create the temporary clone directory; %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
options := git.CloneOptions{
|
2024-02-06 14:58:17 +00:00
|
|
|
URL: p.RepositoryURL,
|
2024-02-06 13:00:48 +00:00
|
|
|
Progress: nil,
|
|
|
|
ReferenceName: reference,
|
|
|
|
SingleBranch: singleBranch,
|
|
|
|
Depth: depth,
|
|
|
|
}
|
|
|
|
|
|
|
|
repository, err := git.PlainClone(cloneDir, false, &options)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("unable to clone the repository; %w", err)
|
|
|
|
}
|
|
|
|
|
2024-02-06 14:58:17 +00:00
|
|
|
if p.Commit == "" {
|
2024-02-06 13:00:48 +00:00
|
|
|
return cloneDir, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
worktree, err := repository.Worktree()
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("unable to retrieve the work tree; %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
checkoutOptions := git.CheckoutOptions{
|
2024-02-06 14:58:17 +00:00
|
|
|
Hash: plumbing.NewHash(p.Commit),
|
2024-02-06 13:00:48 +00:00
|
|
|
Create: false,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := worktree.Checkout(&checkoutOptions); err != nil {
|
2024-02-06 14:58:17 +00:00
|
|
|
return "", fmt.Errorf("unable to checkout %q; %w", p.Commit, err)
|
2024-02-06 13:00:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
slog.Info("Project cloned successfully.")
|
|
|
|
|
|
|
|
return cloneDir, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyDocumentation(inputDirectory, projectName string) error {
|
2024-02-06 14:58:17 +00:00
|
|
|
outputDirectory := filepath.Join(projectsDirectory, projectName)
|
|
|
|
|
|
|
|
slog.Info("Copying documentation.", "project", projectName, "source", inputDirectory, "destination", outputDirectory)
|
2024-02-06 13:00:48 +00:00
|
|
|
|
|
|
|
defer os.RemoveAll(inputDirectory)
|
|
|
|
|
|
|
|
files, err := os.ReadDir(inputDirectory)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to read files from %q; %w", inputDirectory, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := os.MkdirAll(outputDirectory, 0o750); err != nil {
|
|
|
|
return fmt.Errorf("unable to create %q; %w", outputDirectory, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range files {
|
|
|
|
if file.IsDir() || !strings.HasSuffix(file.Name(), ".asciidoc") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
inputPath := filepath.Join(inputDirectory, file.Name())
|
|
|
|
outputPath := filepath.Join(projectsDirectory, projectName, file.Name())
|
|
|
|
|
|
|
|
err := func() error {
|
|
|
|
inputFile, err := os.Open(inputPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to open %q; %w", inputPath, err)
|
|
|
|
}
|
|
|
|
defer inputFile.Close()
|
|
|
|
|
|
|
|
outputFile, err := os.Create(outputPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to create %q; %w", outputPath, err)
|
|
|
|
}
|
|
|
|
defer outputFile.Close()
|
|
|
|
|
|
|
|
if _, err := io.Copy(outputFile, inputFile); err != nil {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"unable to copy the contents from %q to %q; %w",
|
|
|
|
inputPath,
|
|
|
|
outputPath,
|
|
|
|
err,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
slog.Info("Documentation copied successfully.")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|