106 lines
3.5 KiB
Go
106 lines
3.5 KiB
Go
package run
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/homarr-labs/homarr/tools/homarr-dev/internal/docker"
|
|
"github.com/homarr-labs/homarr/tools/homarr-dev/internal/gh"
|
|
)
|
|
|
|
func BuildCurrentImage(tag, directory string) error {
|
|
return buildCurrentImage(context.Background(), tag, directory, os.Stdout, os.Stderr)
|
|
}
|
|
|
|
func buildCurrentImage(ctx context.Context, tag, directory string, stdout, stderr io.Writer) error {
|
|
root, revision, err := checkoutDetails(ctx, directory)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return docker.BuildContext(ctx, docker.BuildOptions{Context: root, Tag: tag, Source: root, Revision: revision}, stdout, stderr)
|
|
}
|
|
|
|
func BuildPRImage(number int, tag string) error {
|
|
return buildPRImage(context.Background(), number, tag, os.Stdout, os.Stderr)
|
|
}
|
|
|
|
func buildPRImage(ctx context.Context, number int, tag string, stdout, stderr io.Writer) error {
|
|
if number <= 0 {
|
|
return fmt.Errorf("PR number must be positive")
|
|
}
|
|
temporaryRoot, err := os.MkdirTemp("", "homarr-pr-*")
|
|
if err != nil {
|
|
return fmt.Errorf("create temporary checkout: %w", err)
|
|
}
|
|
defer os.RemoveAll(temporaryRoot)
|
|
|
|
checkout := filepath.Join(temporaryRoot, "homarr")
|
|
clone := exec.CommandContext(ctx, "gh", prCloneArgs(checkout)...)
|
|
clone.Stdout = stdout
|
|
clone.Stderr = stderr
|
|
if err := clone.Run(); err != nil {
|
|
return fmt.Errorf("clone %s: %w", gh.Repo, err)
|
|
}
|
|
command := exec.CommandContext(ctx, "gh", "pr", "checkout", strconv.Itoa(number), "--detach")
|
|
command.Dir = checkout
|
|
command.Stdout = stdout
|
|
command.Stderr = stderr
|
|
if err := command.Run(); err != nil {
|
|
return fmt.Errorf("checkout PR #%d: %w", number, err)
|
|
}
|
|
_, revision, err := checkoutDetails(ctx, checkout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return docker.BuildContext(ctx, docker.BuildOptions{
|
|
Context: checkout,
|
|
Tag: tag,
|
|
Source: fmt.Sprintf("https://github.com/%s/pull/%d", gh.Repo, number),
|
|
Revision: revision,
|
|
PRNumber: number,
|
|
}, stdout, stderr)
|
|
}
|
|
|
|
func prCloneArgs(checkout string) []string {
|
|
return []string{"repo", "clone", gh.Repo, checkout, "--", "--depth=1", "--filter=blob:none"}
|
|
}
|
|
|
|
func RebuildImage(image docker.Image) error {
|
|
return RebuildImageContext(context.Background(), image, os.Stdout, os.Stderr)
|
|
}
|
|
|
|
func RebuildImageContext(ctx context.Context, image docker.Image, stdout, stderr io.Writer) error {
|
|
if image.PRNumber > 0 {
|
|
return buildPRImage(ctx, image.PRNumber, image.Tag, stdout, stderr)
|
|
}
|
|
if image.Source == "" {
|
|
return fmt.Errorf("homarr:%s has no build source metadata", image.Tag)
|
|
}
|
|
if info, err := os.Stat(image.Source); err != nil || !info.IsDir() {
|
|
return fmt.Errorf("build source %s is no longer available", image.Source)
|
|
}
|
|
return buildCurrentImage(ctx, image.Tag, image.Source, stdout, stderr)
|
|
}
|
|
|
|
func checkoutDetails(ctx context.Context, directory string) (root string, revision string, err error) {
|
|
rootOutput, err := exec.CommandContext(ctx, "git", "-C", directory, "rev-parse", "--show-toplevel").CombinedOutput()
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("find Homarr checkout: %w: %s", err, strings.TrimSpace(string(rootOutput)))
|
|
}
|
|
root = strings.TrimSpace(string(rootOutput))
|
|
if _, err := os.Stat(filepath.Join(root, "Dockerfile")); err != nil {
|
|
return "", "", fmt.Errorf("%s is not a Homarr checkout", root)
|
|
}
|
|
revisionOutput, err := exec.CommandContext(ctx, "git", "-C", root, "rev-parse", "HEAD").CombinedOutput()
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("read checkout revision: %w: %s", err, strings.TrimSpace(string(revisionOutput)))
|
|
}
|
|
return root, strings.TrimSpace(string(revisionOutput)), nil
|
|
}
|