+12








fcd7c979df
Co-authored-by: Artur <145776561+arturict@users.noreply.github.com> Co-authored-by: arturict <arturict@users.noreply.github.com> Co-authored-by: Thomas <thomas@ajnart.fr> Co-authored-by: Afonso Jorge Ramos <afonsojorgeramos@gmail.com> Co-authored-by: Danura30082 <danura30082+github@gmail.com> Co-authored-by: Kushie <122986913+kushiemoon-dev@users.noreply.github.com> Co-authored-by: ajnart <thomas@ajnart.fr> Co-authored-by: ajnart <thomas@ajnart.dev> Co-authored-by: chenjun <chenjun@foxmail.com> Co-authored-by: Wikibear <wikibear@users.noreply.github.com> Co-authored-by: Bergasha <bergasha@gmail.com> Co-authored-by: leafyelin <43306196+leafyelin@users.noreply.github.com> Co-authored-by: Lucas Araujo <lucas.lra@gmail.com> Co-authored-by: GreenMonito <86085570+GreenMonito@users.noreply.github.com> Co-authored-by: Nerdy-Technician <roffo@nerdy-technician.social> Co-authored-by: Jun <34545896+jcstrong@users.noreply.github.com> Co-authored-by: Edmond Abraham <54119952+gitedmond@users.noreply.github.com> Co-authored-by: thrawnx <tight.bulb9384@fastmail.com> Co-authored-by: Chris <58918656+nordada@users.noreply.github.com> Co-authored-by: yi111 <153097222+Yi-111-a@users.noreply.github.com> Co-authored-by: Yanis <naamaneyanis@gmail.com> Co-authored-by: Wikibear <me@sej.de> Co-authored-by: alessiottavo <93441886+alessiottavo@users.noreply.github.com>
129 lines
4.3 KiB
Go
129 lines
4.3 KiB
Go
package run
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/homarr-labs/homarr/tools/homarr-dev/internal/docker"
|
|
"github.com/homarr-labs/homarr/tools/homarr-dev/internal/gh"
|
|
)
|
|
|
|
// Cleanup releases a temporary checkout. It is always safe to call.
|
|
type Cleanup func()
|
|
|
|
func noCleanup() {}
|
|
|
|
// CurrentBuildOptions describes a build of the checkout that contains
|
|
// directory, recording the source and revision on the image so it can be
|
|
// rebuilt later without the user remembering where it came from.
|
|
func CurrentBuildOptions(ctx context.Context, tag, directory string) (docker.BuildOptions, error) {
|
|
root, revision, err := checkoutDetails(ctx, directory)
|
|
if err != nil {
|
|
return docker.BuildOptions{}, err
|
|
}
|
|
return docker.BuildOptions{Context: root, Tag: tag, Source: root, Revision: revision}, nil
|
|
}
|
|
|
|
// PRBuildOptions clones a pull request into a temporary checkout and describes
|
|
// a build of it. The returned Cleanup must run once the build has finished.
|
|
//
|
|
// Progress is reported through log rather than written to stdout, because the
|
|
// caller is a background job rendering inside a TUI, not a shell.
|
|
func PRBuildOptions(ctx context.Context, number int, tag string, log func(string)) (docker.BuildOptions, Cleanup, error) {
|
|
if number <= 0 {
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("PR number must be positive")
|
|
}
|
|
if log == nil {
|
|
log = func(string) {}
|
|
}
|
|
temporaryRoot, err := os.MkdirTemp("", "homarr-pr-*")
|
|
if err != nil {
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("create temporary checkout: %w", err)
|
|
}
|
|
cleanup := func() { _ = os.RemoveAll(temporaryRoot) }
|
|
|
|
checkout := filepath.Join(temporaryRoot, "homarr")
|
|
if err := os.MkdirAll(checkout, 0o755); err != nil {
|
|
cleanup()
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("create checkout directory: %w", err)
|
|
}
|
|
|
|
repoURL := fmt.Sprintf("https://github.com/%s.git", gh.Repo)
|
|
branchName := fmt.Sprintf("pr-%d", number)
|
|
refSpec := fmt.Sprintf("refs/pull/%d/head:%s", number, branchName)
|
|
|
|
log(fmt.Sprintf("fetching PR #%d (shallow) from %s", number, gh.Repo))
|
|
initCmd := exec.CommandContext(ctx, "git", "init", checkout)
|
|
if err := runLogged(initCmd, log); err != nil {
|
|
cleanup()
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("init git repository: %w", err)
|
|
}
|
|
|
|
fetchCmd := exec.CommandContext(ctx, "git", "-C", checkout, "fetch", "--depth", "1", repoURL, refSpec)
|
|
if err := runLogged(fetchCmd, log); err != nil {
|
|
cleanup()
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("fetch PR #%d: %w", number, err)
|
|
}
|
|
|
|
checkoutCmd := exec.CommandContext(ctx, "git", "-C", checkout, "checkout", branchName)
|
|
if err := runLogged(checkoutCmd, log); err != nil {
|
|
cleanup()
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("checkout PR #%d: %w", number, err)
|
|
}
|
|
|
|
_, revision, err := checkoutDetails(ctx, checkout)
|
|
if err != nil {
|
|
cleanup()
|
|
return docker.BuildOptions{}, noCleanup, err
|
|
}
|
|
log("building " + revision[:min(len(revision), 12)])
|
|
return docker.BuildOptions{
|
|
Context: checkout,
|
|
Tag: tag,
|
|
Source: fmt.Sprintf("https://github.com/%s/pull/%d", gh.Repo, number),
|
|
Revision: revision,
|
|
PRNumber: number,
|
|
}, cleanup, nil
|
|
}
|
|
|
|
// RebuildOptions describes a rebuild of an existing local image from the
|
|
// provenance recorded on it, whether that is a pull request or a checkout.
|
|
func RebuildOptions(ctx context.Context, image docker.Image, log func(string)) (docker.BuildOptions, Cleanup, error) {
|
|
if image.PRNumber > 0 {
|
|
return PRBuildOptions(ctx, image.PRNumber, image.Tag, log)
|
|
}
|
|
if image.Source == "" {
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("homarr:%s has no build source metadata", image.Tag)
|
|
}
|
|
if info, err := os.Stat(image.Source); err != nil || !info.IsDir() {
|
|
return docker.BuildOptions{}, noCleanup, fmt.Errorf("build source %s is no longer available", image.Source)
|
|
}
|
|
options, err := CurrentBuildOptions(ctx, image.Tag, image.Source)
|
|
return options, noCleanup, err
|
|
}
|
|
|
|
func runLogged(command *exec.Cmd, log func(string)) error {
|
|
command.Env = append(
|
|
os.Environ(),
|
|
"MISE_TRUSTED_CONFIG_PATHS=*",
|
|
"MISE_QUIET=1",
|
|
"MISE_SILENT=1",
|
|
"MISE_YES=1",
|
|
"MISE_OVERRIDE_CONFIG_FILENAMES=",
|
|
)
|
|
lines, err := docker.StreamCommand(command)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for line := range lines {
|
|
if line.Done {
|
|
return line.Err
|
|
}
|
|
log(line.Text)
|
|
}
|
|
return nil
|
|
}
|