website/internal/build/magefiles/projects.go
Dan Anglin ac7d73f013
checkpoint: update
- added profiles and services to nanoc.yaml
- created an internal module for the build code so that those
  dependencies are not included in the main module.
- upgraded Go to v1.22 and specified that only the GET method is valid
  for the root path.
2024-02-07 15:51:29 +00:00

209 lines
4.9 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 []project `yaml:"projects"`
}
type project struct {
Name string `yaml:"name"`
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)
if err := changeToProjectRoot(); err != nil {
return fmt.Errorf("unable to change to the project's root directory; %w", err)
}
projects, err := parseProjectsConfig()
if err != nil {
return fmt.Errorf("unable to parse the projects configuration; %w", err)
}
for _, project := range projects.Projects {
clonedDirectory, err := cloneProject(project)
if err != nil {
return fmt.Errorf("unable to clone %q; %w", project.Name, err)
}
documentationDir := filepath.Join(clonedDirectory, project.DocumentationRoot)
if err := copyDocumentation(documentationDir, project.Name); err != nil {
return fmt.Errorf("unable to copy the documentation from %q; %w", project.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(p project) (string, error) {
slog.Info("Cloning the project repository.", "repository", p.RepositoryURL)
var (
reference plumbing.ReferenceName
singleBranch bool
depth int
)
if p.Tag != "" {
reference = plumbing.NewTagReferenceName(p.Tag)
singleBranch = false
depth = 1
} else if p.Branch != "" {
reference = plumbing.NewBranchReferenceName(p.Branch)
singleBranch = true
depth = 1
} else {
reference = plumbing.Main
singleBranch = false
depth = 0
}
cloneDir, err := os.MkdirTemp("", p.Name)
if err != nil {
return "", fmt.Errorf("unable to create the temporary clone directory; %w", err)
}
options := git.CloneOptions{
URL: p.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 p.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(p.Commit),
Create: false,
}
if err := worktree.Checkout(&checkoutOptions); err != nil {
return "", fmt.Errorf("unable to checkout %q; %w", p.Commit, err)
}
slog.Info("Project cloned successfully.")
return cloneDir, nil
}
func copyDocumentation(inputDirectory, projectName string) error {
outputDirectory := filepath.Join(projectsDirectory, projectName)
slog.Info("Copying documentation.", "project", projectName, "source", inputDirectory, "destination", outputDirectory)
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
}