cli/pkg/devops/sources/github.go
Snider 06273a12d7
Migrate pkg/devops to Medium abstraction (#293)
* chore(io): migrate pkg/devops to Medium abstraction

This commit migrates the pkg/devops package to use the io.Medium abstraction instead of direct calls to io.Local or the os package.

Changes:
- Updated DevOps, ImageManager, and Manifest structs to hold an io.Medium.
- Updated New, NewImageManager, and LoadConfig to accept an io.Medium.
- Updated ImageSource interface and its implementations (GitHubSource, CDNSource) to accept io.Medium in Download method.
- Refactored internal helper functions (hasFile, hasPackageScript, etc.) to use io.Medium.
- Updated all unit tests and CLI entry points to pass the appropriate io.Medium.

This migration improves the testability and flexibility of the devops package by allowing for different storage backends.

* chore(io): migrate pkg/devops to Medium abstraction

This commit completes the migration of the pkg/devops package to the io.Medium abstraction.

Changes:
- Refactored DevOps, ImageManager, and Manifest structs to use io.Medium for storage operations.
- Updated New, NewImageManager, and LoadConfig to accept an io.Medium.
- Updated ImageSource interface and its implementations (GitHubSource, CDNSource) to accept io.Medium in Download method.
- Refactored internal helper functions (hasFile, hasPackageScript, etc.) to use io.Medium.
- Updated all unit tests and CLI entry points to pass the appropriate io.Medium.
- Fixed formatting issues in test files.

This migration enables easier testing and supports alternative storage backends.
2026-02-04 14:58:03 +00:00

72 lines
1.7 KiB
Go

package sources
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"github.com/host-uk/core/pkg/io"
)
// GitHubSource downloads images from GitHub Releases.
type GitHubSource struct {
config SourceConfig
}
// Compile-time interface check.
var _ ImageSource = (*GitHubSource)(nil)
// NewGitHubSource creates a new GitHub source.
func NewGitHubSource(cfg SourceConfig) *GitHubSource {
return &GitHubSource{config: cfg}
}
// Name returns "github".
func (s *GitHubSource) Name() string {
return "github"
}
// Available checks if gh CLI is installed and authenticated.
func (s *GitHubSource) Available() bool {
_, err := exec.LookPath("gh")
if err != nil {
return false
}
// Check if authenticated
cmd := exec.Command("gh", "auth", "status")
return cmd.Run() == nil
}
// LatestVersion returns the latest release tag.
func (s *GitHubSource) LatestVersion(ctx context.Context) (string, error) {
cmd := exec.CommandContext(ctx, "gh", "release", "view",
"-R", s.config.GitHubRepo,
"--json", "tagName",
"-q", ".tagName",
)
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("github.LatestVersion: %w", err)
}
return strings.TrimSpace(string(out)), nil
}
// Download downloads the image from the latest release.
func (s *GitHubSource) Download(ctx context.Context, m io.Medium, dest string, progress func(downloaded, total int64)) error {
// Get release assets to find our image
cmd := exec.CommandContext(ctx, "gh", "release", "download",
"-R", s.config.GitHubRepo,
"-p", s.config.ImageName,
"-D", dest,
"--clobber",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("github.Download: %w", err)
}
return nil
}