feat(devops): add GitHub Releases source

Downloads core-devops images from GitHub Releases using gh CLI.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-29 02:06:20 +00:00
parent 919ecd3eba
commit 4286052c9e
2 changed files with 89 additions and 0 deletions

View file

@ -0,0 +1,70 @@
package sources
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
)
// 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, 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
}

View file

@ -0,0 +1,19 @@
package sources
import (
"testing"
)
func TestGitHubSource_Good_Available(t *testing.T) {
src := NewGitHubSource(SourceConfig{
GitHubRepo: "host-uk/core-images",
ImageName: "core-devops-darwin-arm64.qcow2",
})
if src.Name() != "github" {
t.Errorf("expected name 'github', got %q", src.Name())
}
// Available depends on gh CLI being installed
_ = src.Available()
}