website/magefiles/projects.go

203 lines
4.8 KiB
Go

//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 {
Projects map[string]project `yaml:"projects"`
}
type project struct {
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)
}
for name, details := range projects.Projects {
clonedDirectory, err := cloneProject(name, details)
if err != nil {
return fmt.Errorf("unable to clone %q; %w", name, err)
}
documentationDir := filepath.Join(clonedDirectory, details.DocumentationRoot)
if err := copyDocumentation(documentationDir, name); err != nil {
return fmt.Errorf("unable to copy the documentation from %q; %w", name, err)
}
}
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
}
func cloneProject(projectName string, projectDetails project) (string, error) {
slog.Info("Cloning the project repository.", "repository", projectDetails.RepositoryURL)
var (
reference plumbing.ReferenceName
singleBranch bool
depth int
)
if projectDetails.Tag != "" {
reference = plumbing.NewTagReferenceName(projectDetails.Tag)
singleBranch = false
depth = 1
} else if projectDetails.Branch != "" {
reference = plumbing.NewBranchReferenceName(projectDetails.Branch)
singleBranch = true
depth = 1
} else {
reference = plumbing.Main
singleBranch = false
depth = 0
}
cloneDir, err := os.MkdirTemp("", projectName)
if err != nil {
return "", fmt.Errorf("unable to create the temporary clone directory; %w", err)
}
options := git.CloneOptions{
URL: projectDetails.RepositoryURL,
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)
}
if projectDetails.Commit == "" {
return cloneDir, nil
}
worktree, err := repository.Worktree()
if err != nil {
return "", fmt.Errorf("unable to retrieve the work tree; %w", err)
}
checkoutOptions := git.CheckoutOptions{
Hash: plumbing.NewHash(projectDetails.Commit),
Create: false,
}
if err := worktree.Checkout(&checkoutOptions); err != nil {
return "", fmt.Errorf("unable to checkout %q; %w", projectDetails.Commit, err)
}
slog.Info("Project cloned successfully.")
return cloneDir, nil
}
func copyDocumentation(inputDirectory, projectName string) error {
slog.Info("Copying documentation.", "project", projectName, "directory", inputDirectory)
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
}
outputDirectory := filepath.Join(projectsDirectory, projectName)
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
}