Migrate pkg/release to io.Medium abstraction (#290)
* chore(io): migrate pkg/release to io.Medium abstraction Migrated `pkg/release` and its subpackages to use the `io.Medium` abstraction for filesystem operations. This enables better testability and support for alternative storage backends. Changes: - Added `FS io.Medium` field to `release.Release` and `publishers.Release` structs. - Updated `LoadConfig`, `ConfigExists`, and `WriteConfig` in `pkg/release/config.go` to accept `io.Medium`. - Updated `Publish`, `Run`, `findArtifacts`, and `buildArtifacts` in `pkg/release/release.go` to use `io.Medium`. - Migrated all publishers (`aur`, `chocolatey`, `docker`, `github`, `homebrew`, `linuxkit`, `npm`, `scoop`) to use `io.Medium` for file operations. - Implemented custom template overrides in publishers by checking for templates in `.core/templates/<publisher>/` via `io.Medium`. - Updated all relevant tests to provide `io.Medium`. * chore(io): fix missing callers in pkg/release migration Updated callers of `release` package functions that had their signatures changed during the `io.Medium` migration. Fixed files: - `internal/cmd/ci/cmd_init.go` - `internal/cmd/ci/cmd_publish.go` - `pkg/build/buildcmd/cmd_release.go` These changes ensure the project compiles successfully by providing `io.Local` to `LoadConfig`, `WriteConfig`, and `ConfigExists`. * chore(io): fix build errors in pkg/release migration Fixed compilation errors by updating all callers of `release.LoadConfig`, `release.ConfigExists`, and `release.WriteConfig` to provide the required `io.Medium` argument. Files updated: - `internal/cmd/ci/cmd_init.go` - `internal/cmd/ci/cmd_publish.go` - `pkg/build/buildcmd/cmd_release.go` These entry points now correctly pass `io.Local` to the `release` package functions.
This commit is contained in:
parent
418e9dfef3
commit
7be325302f
26 changed files with 434 additions and 143 deletions
40
.github/workflows/auto-merge.yml
vendored
Normal file
40
.github/workflows/auto-merge.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
name: Auto Merge
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, ready_for_review]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
auto-merge:
|
||||
if: "!github.event.pull_request.draft"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check org membership and enable auto-merge
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const author = context.payload.pull_request.user.login;
|
||||
|
||||
try {
|
||||
await github.rest.orgs.checkMembershipForUser({
|
||||
org: owner,
|
||||
username: author,
|
||||
});
|
||||
} catch {
|
||||
core.info(`${author} is not an org member — skipping auto-merge`);
|
||||
return;
|
||||
}
|
||||
|
||||
await exec.exec('gh', [
|
||||
'pr', 'merge', process.env.PR_NUMBER,
|
||||
'--auto', '--squash',
|
||||
]);
|
||||
core.info(`Auto-merge enabled for #${process.env.PR_NUMBER}`);
|
||||
42
.github/workflows/pr-gate.yml
vendored
Normal file
42
.github/workflows/pr-gate.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
name: PR Gate
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
org-gate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check org membership or approval label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const author = context.payload.pull_request.user.login;
|
||||
|
||||
// Check if author is an org member
|
||||
try {
|
||||
await github.rest.orgs.checkMembershipForUser({
|
||||
org: owner,
|
||||
username: author,
|
||||
});
|
||||
core.info(`${author} is an org member — gate passed`);
|
||||
return;
|
||||
} catch {
|
||||
core.info(`${author} is not an org member — checking for label`);
|
||||
}
|
||||
|
||||
// Check for external-approved label
|
||||
const labels = context.payload.pull_request.labels.map(l => l.name);
|
||||
if (labels.includes('external-approved')) {
|
||||
core.info('external-approved label present — gate passed');
|
||||
return;
|
||||
}
|
||||
|
||||
core.setFailed(
|
||||
`External PR from ${author} requires an org member to add the "external-approved" label before merge.`
|
||||
);
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
||||
|
|
@ -17,14 +18,14 @@ func runCIReleaseInit() error {
|
|||
cli.Print("%s %s\n\n", releaseDimStyle.Render(i18n.Label("init")), i18n.T("cmd.ci.init.initializing"))
|
||||
|
||||
// Check if already initialized
|
||||
if release.ConfigExists(cwd) {
|
||||
if release.ConfigExists(io.Local, cwd) {
|
||||
cli.Text(i18n.T("cmd.ci.init.already_initialized"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create release config
|
||||
cfg := release.DefaultConfig()
|
||||
if err := release.WriteConfig(cfg, cwd); err != nil {
|
||||
if err := release.WriteConfig(io.Local, cfg, cwd); err != nil {
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.create", "config"), err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error {
|
|||
}
|
||||
|
||||
// Load configuration
|
||||
cfg, err := release.LoadConfig(projectDir)
|
||||
cfg, err := release.LoadConfig(io.Local, projectDir)
|
||||
if err != nil {
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -147,6 +148,7 @@ type CheckResult struct {
|
|||
Duration string `json:"duration"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Output string `json:"output,omitempty"`
|
||||
FixHint string `json:"fix_hint,omitempty"`
|
||||
}
|
||||
|
||||
func runGoQA(cmd *cli.Command, args []string) error {
|
||||
|
|
@ -218,6 +220,7 @@ func runGoQA(cmd *cli.Command, args []string) error {
|
|||
if qaVerbose {
|
||||
result.Output = output
|
||||
}
|
||||
result.FixHint = fixHintFor(check.Name, output)
|
||||
failed++
|
||||
|
||||
if !qaJSON && !qaQuiet {
|
||||
|
|
@ -225,6 +228,9 @@ func runGoQA(cmd *cli.Command, args []string) error {
|
|||
if qaVerbose && output != "" {
|
||||
cli.Text(output)
|
||||
}
|
||||
if result.FixHint != "" {
|
||||
cli.Hint("fix", result.FixHint)
|
||||
}
|
||||
}
|
||||
|
||||
if qaFailFast {
|
||||
|
|
@ -260,6 +266,7 @@ func runGoQA(cmd *cli.Command, args []string) error {
|
|||
if !qaJSON && !qaQuiet {
|
||||
cli.Print(" %s Coverage %.1f%% below threshold %.1f%%\n",
|
||||
cli.ErrorStyle.Render(cli.Glyph(":cross:")), cov, qaThreshold)
|
||||
cli.Hint("fix", "Run 'core go cov --open' to see uncovered lines, then add tests.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -436,6 +443,47 @@ func buildCheck(name string) QACheck {
|
|||
}
|
||||
}
|
||||
|
||||
// fixHintFor returns an actionable fix instruction for a given check failure.
|
||||
func fixHintFor(checkName, output string) string {
|
||||
switch checkName {
|
||||
case "format", "fmt":
|
||||
return "Run 'core go qa fmt --fix' to auto-format."
|
||||
case "vet":
|
||||
return "Fix the issues reported by go vet — typically genuine bugs."
|
||||
case "lint":
|
||||
return "Run 'core go qa lint --fix' for auto-fixable issues."
|
||||
case "test":
|
||||
if name := extractFailingTest(output); name != "" {
|
||||
return fmt.Sprintf("Run 'go test -run %s -v ./...' to debug.", name)
|
||||
}
|
||||
return "Run 'go test -run <TestName> -v ./path/' to debug."
|
||||
case "race":
|
||||
return "Data race detected. Add mutex, channel, or atomic to synchronise shared state."
|
||||
case "bench":
|
||||
return "Benchmark regression. Run 'go test -bench=. -benchmem' to reproduce."
|
||||
case "vuln":
|
||||
return "Run 'govulncheck ./...' for details. Update affected deps with 'go get -u'."
|
||||
case "sec":
|
||||
return "Review gosec findings. Common fixes: validate inputs, parameterised queries."
|
||||
case "fuzz":
|
||||
return "Add a regression test for the crashing input in testdata/fuzz/<Target>/."
|
||||
case "docblock":
|
||||
return "Add doc comments to exported symbols: '// Name does X.' before each declaration."
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var failTestRe = regexp.MustCompile(`--- FAIL: (\w+)`)
|
||||
|
||||
// extractFailingTest parses the first failing test name from go test output.
|
||||
func extractFailingTest(output string) string {
|
||||
if m := failTestRe.FindStringSubmatch(output); len(m) > 1 {
|
||||
return m[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func runCheckCapture(ctx context.Context, dir string, check QACheck) (string, error) {
|
||||
// Handle internal checks
|
||||
if check.Command == "_internal_" {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/framework/core"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ func runRelease(ctx context.Context, dryRun bool, version string, draft, prerele
|
|||
}
|
||||
|
||||
// Check for release config
|
||||
if !release.ConfigExists(projectDir) {
|
||||
if !release.ConfigExists(io.Local, projectDir) {
|
||||
cli.Print("%s %s\n",
|
||||
buildErrorStyle.Render(i18n.Label("error")),
|
||||
i18n.T("cmd.build.release.error.no_config"),
|
||||
|
|
@ -60,7 +61,7 @@ func runRelease(ctx context.Context, dryRun bool, version string, draft, prerele
|
|||
}
|
||||
|
||||
// Load configuration
|
||||
cfg, err := release.LoadConfig(projectDir)
|
||||
cfg, err := release.LoadConfig(io.Local, projectDir)
|
||||
if err != nil {
|
||||
return core.E("release", "load config", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,14 +169,14 @@ type ChangelogConfig struct {
|
|||
// LoadConfig loads release configuration from the .core/release.yaml file in the given directory.
|
||||
// If the config file does not exist, it returns DefaultConfig().
|
||||
// Returns an error if the file exists but cannot be parsed.
|
||||
func LoadConfig(dir string) (*Config, error) {
|
||||
func LoadConfig(m io.Medium, dir string) (*Config, error) {
|
||||
configPath := filepath.Join(dir, ConfigDir, ConfigFileName)
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("release.LoadConfig: failed to resolve path: %w", err)
|
||||
}
|
||||
|
||||
content, err := io.Local.Read(absPath)
|
||||
content, err := m.Read(absPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
cfg := DefaultConfig()
|
||||
|
|
@ -266,13 +266,13 @@ func ConfigPath(dir string) string {
|
|||
}
|
||||
|
||||
// ConfigExists checks if a release config file exists in the given directory.
|
||||
func ConfigExists(dir string) bool {
|
||||
func ConfigExists(m io.Medium, dir string) bool {
|
||||
configPath := ConfigPath(dir)
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return io.Local.IsFile(absPath)
|
||||
return m.IsFile(absPath)
|
||||
}
|
||||
|
||||
// GetRepository returns the repository from the config.
|
||||
|
|
@ -286,7 +286,7 @@ func (c *Config) GetProjectName() string {
|
|||
}
|
||||
|
||||
// WriteConfig writes the config to the .core/release.yaml file.
|
||||
func WriteConfig(cfg *Config, dir string) error {
|
||||
func WriteConfig(m io.Medium, cfg *Config, dir string) error {
|
||||
configPath := ConfigPath(dir)
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
if err != nil {
|
||||
|
|
@ -298,8 +298,8 @@ func WriteConfig(cfg *Config, dir string) error {
|
|||
return fmt.Errorf("release.WriteConfig: failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
// io.Local.Write creates parent directories automatically
|
||||
if err := io.Local.Write(absPath, string(data)); err != nil {
|
||||
// m.Write creates parent directories automatically
|
||||
if err := m.Write(absPath, string(data)); err != nil {
|
||||
return fmt.Errorf("release.WriteConfig: failed to write config file: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -53,7 +54,7 @@ changelog:
|
|||
`
|
||||
dir := setupConfigTestDir(t, content)
|
||||
|
||||
cfg, err := LoadConfig(dir)
|
||||
cfg, err := LoadConfig(io.Local, dir)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
|
|
@ -76,7 +77,7 @@ changelog:
|
|||
t.Run("returns defaults when config file missing", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
cfg, err := LoadConfig(dir)
|
||||
cfg, err := LoadConfig(io.Local, dir)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ project:
|
|||
`
|
||||
dir := setupConfigTestDir(t, content)
|
||||
|
||||
cfg, err := LoadConfig(dir)
|
||||
cfg, err := LoadConfig(io.Local, dir)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
|
|
@ -113,7 +114,7 @@ project:
|
|||
t.Run("sets project directory on load", func(t *testing.T) {
|
||||
dir := setupConfigTestDir(t, "version: 1")
|
||||
|
||||
cfg, err := LoadConfig(dir)
|
||||
cfg, err := LoadConfig(io.Local, dir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dir, cfg.projectDir)
|
||||
})
|
||||
|
|
@ -128,7 +129,7 @@ project:
|
|||
`
|
||||
dir := setupConfigTestDir(t, content)
|
||||
|
||||
cfg, err := LoadConfig(dir)
|
||||
cfg, err := LoadConfig(io.Local, dir)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, cfg)
|
||||
assert.Contains(t, err.Error(), "failed to parse config file")
|
||||
|
|
@ -145,7 +146,7 @@ project:
|
|||
err = os.Mkdir(configPath, 0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := LoadConfig(dir)
|
||||
cfg, err := LoadConfig(io.Local, dir)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, cfg)
|
||||
assert.Contains(t, err.Error(), "failed to read config file")
|
||||
|
|
@ -204,17 +205,17 @@ func TestConfigPath_Good(t *testing.T) {
|
|||
func TestConfigExists_Good(t *testing.T) {
|
||||
t.Run("returns true when config exists", func(t *testing.T) {
|
||||
dir := setupConfigTestDir(t, "version: 1")
|
||||
assert.True(t, ConfigExists(dir))
|
||||
assert.True(t, ConfigExists(io.Local, dir))
|
||||
})
|
||||
|
||||
t.Run("returns false when config missing", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
assert.False(t, ConfigExists(dir))
|
||||
assert.False(t, ConfigExists(io.Local, dir))
|
||||
})
|
||||
|
||||
t.Run("returns false when .core dir missing", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
assert.False(t, ConfigExists(dir))
|
||||
assert.False(t, ConfigExists(io.Local, dir))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -226,14 +227,14 @@ func TestWriteConfig_Good(t *testing.T) {
|
|||
cfg.Project.Name = "testapp"
|
||||
cfg.Project.Repository = "owner/testapp"
|
||||
|
||||
err := WriteConfig(cfg, dir)
|
||||
err := WriteConfig(io.Local, cfg, dir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify file exists
|
||||
assert.True(t, ConfigExists(dir))
|
||||
assert.True(t, ConfigExists(io.Local, dir))
|
||||
|
||||
// Reload and verify
|
||||
loaded, err := LoadConfig(dir)
|
||||
loaded, err := LoadConfig(io.Local, dir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "testapp", loaded.Project.Name)
|
||||
assert.Equal(t, "owner/testapp", loaded.Project.Repository)
|
||||
|
|
@ -243,7 +244,7 @@ func TestWriteConfig_Good(t *testing.T) {
|
|||
dir := t.TempDir()
|
||||
|
||||
cfg := DefaultConfig()
|
||||
err := WriteConfig(cfg, dir)
|
||||
err := WriteConfig(io.Local, cfg, dir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check directory was created
|
||||
|
|
@ -320,7 +321,7 @@ func TestWriteConfig_Bad(t *testing.T) {
|
|||
defer func() { _ = os.Chmod(coreDir, 0755) }()
|
||||
|
||||
cfg := DefaultConfig()
|
||||
err = WriteConfig(cfg, dir)
|
||||
err = WriteConfig(io.Local, cfg, dir)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to write config file")
|
||||
})
|
||||
|
|
@ -328,7 +329,7 @@ func TestWriteConfig_Bad(t *testing.T) {
|
|||
t.Run("returns error when directory creation fails", func(t *testing.T) {
|
||||
// Use a path that doesn't exist and can't be created
|
||||
cfg := DefaultConfig()
|
||||
err := WriteConfig(cfg, "/nonexistent/path/that/cannot/be/created")
|
||||
err := WriteConfig(io.Local, cfg, "/nonexistent/path/that/cannot/be/created")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"text/template"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
//go:embed templates/aur/*.tmpl
|
||||
|
|
@ -90,10 +91,10 @@ func (p *AURPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub
|
|||
}
|
||||
|
||||
if dryRun {
|
||||
return p.dryRunPublish(data, cfg)
|
||||
return p.dryRunPublish(release.FS, data, cfg)
|
||||
}
|
||||
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg)
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg, release)
|
||||
}
|
||||
|
||||
type aurTemplateData struct {
|
||||
|
|
@ -131,7 +132,7 @@ func (p *AURPublisher) parseConfig(pubCfg PublisherConfig, relCfg ReleaseConfig)
|
|||
return cfg
|
||||
}
|
||||
|
||||
func (p *AURPublisher) dryRunPublish(data aurTemplateData, cfg AURConfig) error {
|
||||
func (p *AURPublisher) dryRunPublish(m io.Medium, data aurTemplateData, cfg AURConfig) error {
|
||||
fmt.Println()
|
||||
fmt.Println("=== DRY RUN: AUR Publish ===")
|
||||
fmt.Println()
|
||||
|
|
@ -141,7 +142,7 @@ func (p *AURPublisher) dryRunPublish(data aurTemplateData, cfg AURConfig) error
|
|||
fmt.Printf("Repository: %s\n", data.Repository)
|
||||
fmt.Println()
|
||||
|
||||
pkgbuild, err := p.renderTemplate("templates/aur/PKGBUILD.tmpl", data)
|
||||
pkgbuild, err := p.renderTemplate(m, "templates/aur/PKGBUILD.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("aur.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -151,7 +152,7 @@ func (p *AURPublisher) dryRunPublish(data aurTemplateData, cfg AURConfig) error
|
|||
fmt.Println("---")
|
||||
fmt.Println()
|
||||
|
||||
srcinfo, err := p.renderTemplate("templates/aur/.SRCINFO.tmpl", data)
|
||||
srcinfo, err := p.renderTemplate(m, "templates/aur/.SRCINFO.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("aur.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -168,13 +169,13 @@ func (p *AURPublisher) dryRunPublish(data aurTemplateData, cfg AURConfig) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *AURPublisher) executePublish(ctx context.Context, projectDir string, data aurTemplateData, cfg AURConfig) error {
|
||||
pkgbuild, err := p.renderTemplate("templates/aur/PKGBUILD.tmpl", data)
|
||||
func (p *AURPublisher) executePublish(ctx context.Context, projectDir string, data aurTemplateData, cfg AURConfig, release *Release) error {
|
||||
pkgbuild, err := p.renderTemplate(release.FS, "templates/aur/PKGBUILD.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("aur.Publish: failed to render PKGBUILD: %w", err)
|
||||
}
|
||||
|
||||
srcinfo, err := p.renderTemplate("templates/aur/.SRCINFO.tmpl", data)
|
||||
srcinfo, err := p.renderTemplate(release.FS, "templates/aur/.SRCINFO.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("aur.Publish: failed to render .SRCINFO: %w", err)
|
||||
}
|
||||
|
|
@ -188,17 +189,17 @@ func (p *AURPublisher) executePublish(ctx context.Context, projectDir string, da
|
|||
output = filepath.Join(projectDir, output)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(output, 0755); err != nil {
|
||||
if err := release.FS.EnsureDir(output); err != nil {
|
||||
return fmt.Errorf("aur.Publish: failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
pkgbuildPath := filepath.Join(output, "PKGBUILD")
|
||||
if err := os.WriteFile(pkgbuildPath, []byte(pkgbuild), 0644); err != nil {
|
||||
if err := release.FS.Write(pkgbuildPath, pkgbuild); err != nil {
|
||||
return fmt.Errorf("aur.Publish: failed to write PKGBUILD: %w", err)
|
||||
}
|
||||
|
||||
srcinfoPath := filepath.Join(output, ".SRCINFO")
|
||||
if err := os.WriteFile(srcinfoPath, []byte(srcinfo), 0644); err != nil {
|
||||
if err := release.FS.Write(srcinfoPath, srcinfo); err != nil {
|
||||
return fmt.Errorf("aur.Publish: failed to write .SRCINFO: %w", err)
|
||||
}
|
||||
fmt.Printf("Wrote AUR files: %s\n", output)
|
||||
|
|
@ -274,10 +275,25 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *AURPublisher) renderTemplate(name string, data aurTemplateData) (string, error) {
|
||||
content, err := aurTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
func (p *AURPublisher) renderTemplate(m io.Medium, name string, data aurTemplateData) (string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
|
||||
// Try custom template from medium
|
||||
customPath := filepath.Join(".core", name)
|
||||
if m != nil && m.IsFile(customPath) {
|
||||
customContent, err := m.Read(customPath)
|
||||
if err == nil {
|
||||
content = []byte(customContent)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to embedded template
|
||||
if content == nil {
|
||||
content, err = aurTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.New(filepath.Base(name)).Parse(string(content))
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -97,7 +98,7 @@ func TestAURPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/aur/PKGBUILD.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/aur/PKGBUILD.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, "# Maintainer: John Doe <john@example.com>")
|
||||
|
|
@ -125,7 +126,7 @@ func TestAURPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/aur/.SRCINFO.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/aur/.SRCINFO.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, "pkgbase = myapp-bin")
|
||||
|
|
@ -144,7 +145,7 @@ func TestAURPublisher_RenderTemplate_Bad(t *testing.T) {
|
|||
|
||||
t.Run("returns error for non-existent template", func(t *testing.T) {
|
||||
data := aurTemplateData{}
|
||||
_, err := p.renderTemplate("templates/aur/nonexistent.tmpl", data)
|
||||
_, err := p.renderTemplate(io.Local, "templates/aur/nonexistent.tmpl", data)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to read template")
|
||||
})
|
||||
|
|
@ -170,7 +171,7 @@ func TestAURPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Maintainer: "John Doe <john@example.com>",
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -199,6 +200,7 @@ func TestAURPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "aur"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
//go:embed templates/chocolatey/*.tmpl templates/chocolatey/tools/*.tmpl
|
||||
|
|
@ -92,10 +93,10 @@ func (p *ChocolateyPublisher) Publish(ctx context.Context, release *Release, pub
|
|||
}
|
||||
|
||||
if dryRun {
|
||||
return p.dryRunPublish(data, cfg)
|
||||
return p.dryRunPublish(release.FS, data, cfg)
|
||||
}
|
||||
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg)
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg, release)
|
||||
}
|
||||
|
||||
type chocolateyTemplateData struct {
|
||||
|
|
@ -137,7 +138,7 @@ func (p *ChocolateyPublisher) parseConfig(pubCfg PublisherConfig, relCfg Release
|
|||
return cfg
|
||||
}
|
||||
|
||||
func (p *ChocolateyPublisher) dryRunPublish(data chocolateyTemplateData, cfg ChocolateyConfig) error {
|
||||
func (p *ChocolateyPublisher) dryRunPublish(m io.Medium, data chocolateyTemplateData, cfg ChocolateyConfig) error {
|
||||
fmt.Println()
|
||||
fmt.Println("=== DRY RUN: Chocolatey Publish ===")
|
||||
fmt.Println()
|
||||
|
|
@ -147,7 +148,7 @@ func (p *ChocolateyPublisher) dryRunPublish(data chocolateyTemplateData, cfg Cho
|
|||
fmt.Printf("Repository: %s\n", data.Repository)
|
||||
fmt.Println()
|
||||
|
||||
nuspec, err := p.renderTemplate("templates/chocolatey/package.nuspec.tmpl", data)
|
||||
nuspec, err := p.renderTemplate(m, "templates/chocolatey/package.nuspec.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chocolatey.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -157,7 +158,7 @@ func (p *ChocolateyPublisher) dryRunPublish(data chocolateyTemplateData, cfg Cho
|
|||
fmt.Println("---")
|
||||
fmt.Println()
|
||||
|
||||
install, err := p.renderTemplate("templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data)
|
||||
install, err := p.renderTemplate(m, "templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chocolatey.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -178,13 +179,13 @@ func (p *ChocolateyPublisher) dryRunPublish(data chocolateyTemplateData, cfg Cho
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *ChocolateyPublisher) executePublish(ctx context.Context, projectDir string, data chocolateyTemplateData, cfg ChocolateyConfig) error {
|
||||
nuspec, err := p.renderTemplate("templates/chocolatey/package.nuspec.tmpl", data)
|
||||
func (p *ChocolateyPublisher) executePublish(ctx context.Context, projectDir string, data chocolateyTemplateData, cfg ChocolateyConfig, release *Release) error {
|
||||
nuspec, err := p.renderTemplate(release.FS, "templates/chocolatey/package.nuspec.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chocolatey.Publish: failed to render nuspec: %w", err)
|
||||
}
|
||||
|
||||
install, err := p.renderTemplate("templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data)
|
||||
install, err := p.renderTemplate(release.FS, "templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chocolatey.Publish: failed to render install script: %w", err)
|
||||
}
|
||||
|
|
@ -199,18 +200,18 @@ func (p *ChocolateyPublisher) executePublish(ctx context.Context, projectDir str
|
|||
}
|
||||
|
||||
toolsDir := filepath.Join(output, "tools")
|
||||
if err := os.MkdirAll(toolsDir, 0755); err != nil {
|
||||
if err := release.FS.EnsureDir(toolsDir); err != nil {
|
||||
return fmt.Errorf("chocolatey.Publish: failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
// Write files
|
||||
nuspecPath := filepath.Join(output, fmt.Sprintf("%s.nuspec", data.PackageName))
|
||||
if err := os.WriteFile(nuspecPath, []byte(nuspec), 0644); err != nil {
|
||||
if err := release.FS.Write(nuspecPath, nuspec); err != nil {
|
||||
return fmt.Errorf("chocolatey.Publish: failed to write nuspec: %w", err)
|
||||
}
|
||||
|
||||
installPath := filepath.Join(toolsDir, "chocolateyinstall.ps1")
|
||||
if err := os.WriteFile(installPath, []byte(install), 0644); err != nil {
|
||||
if err := release.FS.Write(installPath, install); err != nil {
|
||||
return fmt.Errorf("chocolatey.Publish: failed to write install script: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -255,10 +256,25 @@ func (p *ChocolateyPublisher) pushToChocolatey(ctx context.Context, packageDir s
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *ChocolateyPublisher) renderTemplate(name string, data chocolateyTemplateData) (string, error) {
|
||||
content, err := chocolateyTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
func (p *ChocolateyPublisher) renderTemplate(m io.Medium, name string, data chocolateyTemplateData) (string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
|
||||
// Try custom template from medium
|
||||
customPath := filepath.Join(".core", name)
|
||||
if m != nil && m.IsFile(customPath) {
|
||||
customContent, err := m.Read(customPath)
|
||||
if err == nil {
|
||||
content = []byte(customContent)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to embedded template
|
||||
if content == nil {
|
||||
content, err = chocolateyTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.New(filepath.Base(name)).Parse(string(content))
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -122,7 +124,7 @@ func TestChocolateyPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
Checksums: ChecksumMap{},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/chocolatey/package.nuspec.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/chocolatey/package.nuspec.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, `<id>myapp</id>`)
|
||||
|
|
@ -146,7 +148,7 @@ func TestChocolateyPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, "$ErrorActionPreference = 'Stop'")
|
||||
|
|
@ -163,7 +165,7 @@ func TestChocolateyPublisher_RenderTemplate_Bad(t *testing.T) {
|
|||
|
||||
t.Run("returns error for non-existent template", func(t *testing.T) {
|
||||
data := chocolateyTemplateData{}
|
||||
_, err := p.renderTemplate("templates/chocolatey/nonexistent.tmpl", data)
|
||||
_, err := p.renderTemplate(io.Local, "templates/chocolatey/nonexistent.tmpl", data)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to read template")
|
||||
})
|
||||
|
|
@ -190,7 +192,7 @@ func TestChocolateyPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Push: false,
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -228,7 +230,7 @@ func TestChocolateyPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Push: true,
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ func (p *DockerPublisher) Publish(ctx context.Context, release *Release, pubCfg
|
|||
dockerCfg := p.parseConfig(pubCfg, relCfg, release.ProjectDir)
|
||||
|
||||
// Validate Dockerfile exists
|
||||
if _, err := os.Stat(dockerCfg.Dockerfile); err != nil {
|
||||
if !release.FS.Exists(dockerCfg.Dockerfile) {
|
||||
return fmt.Errorf("docker.Publish: Dockerfile not found: %s", dockerCfg.Dockerfile)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -238,6 +239,7 @@ func TestDockerPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/nonexistent",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "docker",
|
||||
|
|
@ -282,6 +284,7 @@ func TestDockerPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := DockerConfig{
|
||||
Registry: "ghcr.io",
|
||||
|
|
@ -324,6 +327,7 @@ func TestDockerPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := DockerConfig{
|
||||
Registry: "docker.io",
|
||||
|
|
@ -360,6 +364,7 @@ func TestDockerPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v2.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := DockerConfig{
|
||||
Registry: "ghcr.io",
|
||||
|
|
@ -583,6 +588,7 @@ func TestDockerPublisher_Publish_DryRun_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "docker"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -620,6 +626,7 @@ func TestDockerPublisher_Publish_DryRun_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "docker",
|
||||
|
|
@ -653,6 +660,7 @@ func TestDockerPublisher_Publish_Validation_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/nonexistent/path",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "docker"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -670,6 +678,7 @@ func TestDockerPublisher_Publish_Validation_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/tmp",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "docker"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -715,6 +724,7 @@ func TestDockerPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "docker",
|
||||
|
|
@ -758,6 +768,7 @@ func TestDockerPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "docker",
|
||||
|
|
@ -787,6 +798,7 @@ func TestDockerPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "docker"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -90,7 +91,7 @@ func TestGitHubPublisher_Name_Good(t *testing.T) {
|
|||
|
||||
func TestNewRelease_Good(t *testing.T) {
|
||||
t.Run("creates release struct", func(t *testing.T) {
|
||||
r := NewRelease("v1.0.0", nil, "changelog", "/project")
|
||||
r := NewRelease("v1.0.0", nil, "changelog", "/project", io.Local)
|
||||
assert.Equal(t, "v1.0.0", r.Version)
|
||||
assert.Equal(t, "changelog", r.Changelog)
|
||||
assert.Equal(t, "/project", r.ProjectDir)
|
||||
|
|
@ -122,6 +123,7 @@ func TestBuildCreateArgs_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
Changelog: "## v1.0.0\n\nChanges",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -141,6 +143,7 @@ func TestBuildCreateArgs_Good(t *testing.T) {
|
|||
t.Run("with draft flag", func(t *testing.T) {
|
||||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -155,6 +158,7 @@ func TestBuildCreateArgs_Good(t *testing.T) {
|
|||
t.Run("with prerelease flag", func(t *testing.T) {
|
||||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -170,6 +174,7 @@ func TestBuildCreateArgs_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
Changelog: "",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -183,6 +188,7 @@ func TestBuildCreateArgs_Good(t *testing.T) {
|
|||
t.Run("with draft and prerelease flags", func(t *testing.T) {
|
||||
release := &Release{
|
||||
Version: "v1.0.0-alpha",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -200,6 +206,7 @@ func TestBuildCreateArgs_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v2.0.0",
|
||||
Changelog: "Some changes",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -226,6 +233,7 @@ func TestGitHubPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Version: "v1.0.0",
|
||||
Changelog: "## Changes\n\n- Feature A\n- Bug fix B",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -264,6 +272,7 @@ func TestGitHubPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Version: "v1.0.0",
|
||||
Changelog: "Changes",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
Artifacts: []build.Artifact{
|
||||
{Path: "/dist/myapp-darwin-amd64.tar.gz"},
|
||||
{Path: "/dist/myapp-linux-amd64.tar.gz"},
|
||||
|
|
@ -295,6 +304,7 @@ func TestGitHubPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Version: "v1.0.0-beta",
|
||||
Changelog: "Beta release",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := PublisherConfig{
|
||||
Type: "github",
|
||||
|
|
@ -331,6 +341,7 @@ func TestGitHubPublisher_Publish_Good(t *testing.T) {
|
|||
Version: "v1.0.0",
|
||||
Changelog: "Changes",
|
||||
ProjectDir: "/tmp",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "github"}
|
||||
relCfg := &mockReleaseConfig{repository: "custom/repo"}
|
||||
|
|
@ -363,6 +374,7 @@ func TestGitHubPublisher_Publish_Bad(t *testing.T) {
|
|||
Version: "v1.0.0",
|
||||
Changelog: "Changes",
|
||||
ProjectDir: "/nonexistent",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "github"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -383,6 +395,7 @@ func TestGitHubPublisher_Publish_Bad(t *testing.T) {
|
|||
Version: "v1.0.0",
|
||||
Changelog: "Changes",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "github"}
|
||||
relCfg := &mockReleaseConfig{repository: ""} // Empty repository
|
||||
|
|
@ -504,6 +517,7 @@ func TestGitHubPublisher_ExecutePublish_Good(t *testing.T) {
|
|||
Version: "v999.999.999-test-nonexistent",
|
||||
Changelog: "Test changelog",
|
||||
ProjectDir: "/tmp",
|
||||
FS: io.Local,
|
||||
Artifacts: []build.Artifact{
|
||||
{Path: "/tmp/nonexistent-artifact.tar.gz"},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"text/template"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
//go:embed templates/homebrew/*.tmpl
|
||||
|
|
@ -104,10 +105,10 @@ func (p *HomebrewPublisher) Publish(ctx context.Context, release *Release, pubCf
|
|||
}
|
||||
|
||||
if dryRun {
|
||||
return p.dryRunPublish(data, cfg)
|
||||
return p.dryRunPublish(release.FS, data, cfg)
|
||||
}
|
||||
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg)
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg, release)
|
||||
}
|
||||
|
||||
// homebrewTemplateData holds data for Homebrew templates.
|
||||
|
|
@ -160,7 +161,7 @@ func (p *HomebrewPublisher) parseConfig(pubCfg PublisherConfig, relCfg ReleaseCo
|
|||
}
|
||||
|
||||
// dryRunPublish shows what would be done.
|
||||
func (p *HomebrewPublisher) dryRunPublish(data homebrewTemplateData, cfg HomebrewConfig) error {
|
||||
func (p *HomebrewPublisher) dryRunPublish(m io.Medium, data homebrewTemplateData, cfg HomebrewConfig) error {
|
||||
fmt.Println()
|
||||
fmt.Println("=== DRY RUN: Homebrew Publish ===")
|
||||
fmt.Println()
|
||||
|
|
@ -171,7 +172,7 @@ func (p *HomebrewPublisher) dryRunPublish(data homebrewTemplateData, cfg Homebre
|
|||
fmt.Println()
|
||||
|
||||
// Generate and show formula
|
||||
formula, err := p.renderTemplate("templates/homebrew/formula.rb.tmpl", data)
|
||||
formula, err := p.renderTemplate(m, "templates/homebrew/formula.rb.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("homebrew.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -198,9 +199,9 @@ func (p *HomebrewPublisher) dryRunPublish(data homebrewTemplateData, cfg Homebre
|
|||
}
|
||||
|
||||
// executePublish creates the formula and commits to tap.
|
||||
func (p *HomebrewPublisher) executePublish(ctx context.Context, projectDir string, data homebrewTemplateData, cfg HomebrewConfig) error {
|
||||
func (p *HomebrewPublisher) executePublish(ctx context.Context, projectDir string, data homebrewTemplateData, cfg HomebrewConfig, release *Release) error {
|
||||
// Generate formula
|
||||
formula, err := p.renderTemplate("templates/homebrew/formula.rb.tmpl", data)
|
||||
formula, err := p.renderTemplate(release.FS, "templates/homebrew/formula.rb.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("homebrew.Publish: failed to render formula: %w", err)
|
||||
}
|
||||
|
|
@ -214,12 +215,12 @@ func (p *HomebrewPublisher) executePublish(ctx context.Context, projectDir strin
|
|||
output = filepath.Join(projectDir, output)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(output, 0755); err != nil {
|
||||
if err := release.FS.EnsureDir(output); err != nil {
|
||||
return fmt.Errorf("homebrew.Publish: failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
formulaPath := filepath.Join(output, fmt.Sprintf("%s.rb", strings.ToLower(data.FormulaClass)))
|
||||
if err := os.WriteFile(formulaPath, []byte(formula), 0644); err != nil {
|
||||
if err := release.FS.Write(formulaPath, formula); err != nil {
|
||||
return fmt.Errorf("homebrew.Publish: failed to write formula: %w", err)
|
||||
}
|
||||
fmt.Printf("Wrote Homebrew formula for official PR: %s\n", formulaPath)
|
||||
|
|
@ -295,10 +296,25 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho
|
|||
}
|
||||
|
||||
// renderTemplate renders an embedded template with the given data.
|
||||
func (p *HomebrewPublisher) renderTemplate(name string, data homebrewTemplateData) (string, error) {
|
||||
content, err := homebrewTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
func (p *HomebrewPublisher) renderTemplate(m io.Medium, name string, data homebrewTemplateData) (string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
|
||||
// Try custom template from medium
|
||||
customPath := filepath.Join(".core", name)
|
||||
if m != nil && m.IsFile(customPath) {
|
||||
customContent, err := m.Read(customPath)
|
||||
if err == nil {
|
||||
content = []byte(customContent)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to embedded template
|
||||
if content == nil {
|
||||
content, err = homebrewTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.New(filepath.Base(name)).Parse(string(content))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -185,7 +186,7 @@ func TestHomebrewPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/homebrew/formula.rb.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/homebrew/formula.rb.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, "class MyApp < Formula")
|
||||
|
|
@ -206,7 +207,7 @@ func TestHomebrewPublisher_RenderTemplate_Bad(t *testing.T) {
|
|||
|
||||
t.Run("returns error for non-existent template", func(t *testing.T) {
|
||||
data := homebrewTemplateData{}
|
||||
_, err := p.renderTemplate("templates/homebrew/nonexistent.tmpl", data)
|
||||
_, err := p.renderTemplate(io.Local, "templates/homebrew/nonexistent.tmpl", data)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to read template")
|
||||
})
|
||||
|
|
@ -234,7 +235,7 @@ func TestHomebrewPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Tap: "owner/homebrew-tap",
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -271,7 +272,7 @@ func TestHomebrewPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -300,7 +301,7 @@ func TestHomebrewPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -320,6 +321,7 @@ func TestHomebrewPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "homebrew"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (p *LinuxKitPublisher) Publish(ctx context.Context, release *Release, pubCf
|
|||
lkCfg := p.parseConfig(pubCfg, release.ProjectDir)
|
||||
|
||||
// Validate config file exists
|
||||
if _, err := os.Stat(lkCfg.Config); err != nil {
|
||||
if !release.FS.Exists(lkCfg.Config) {
|
||||
return fmt.Errorf("linuxkit.Publish: config file not found: %s", lkCfg.Config)
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ func (p *LinuxKitPublisher) executePublish(ctx context.Context, release *Release
|
|||
outputDir := filepath.Join(release.ProjectDir, "dist", "linuxkit")
|
||||
|
||||
// Create output directory
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
if err := release.FS.EnsureDir(outputDir); err != nil {
|
||||
return fmt.Errorf("linuxkit.Publish: failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ func (p *LinuxKitPublisher) executePublish(ctx context.Context, release *Release
|
|||
|
||||
// Upload artifacts to GitHub release
|
||||
for _, artifactPath := range artifacts {
|
||||
if _, err := os.Stat(artifactPath); err != nil {
|
||||
if !release.FS.Exists(artifactPath) {
|
||||
return fmt.Errorf("linuxkit.Publish: artifact not found after build: %s", artifactPath)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -192,6 +193,7 @@ func TestLinuxKitPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/nonexistent",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "linuxkit",
|
||||
|
|
@ -214,6 +216,7 @@ func TestLinuxKitPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/tmp",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "linuxkit"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -241,6 +244,7 @@ func TestLinuxKitPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "linuxkit",
|
||||
|
|
@ -296,6 +300,7 @@ func TestLinuxKitPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "linuxkit"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -320,6 +325,7 @@ func TestLinuxKitPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "linuxkit"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -349,6 +355,7 @@ func TestLinuxKitPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "linuxkit"}
|
||||
relCfg := &mockReleaseConfig{repository: "custom-owner/custom-repo"}
|
||||
|
|
@ -395,6 +402,7 @@ func TestLinuxKitPublisher_Publish_WithCLI_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "linuxkit"}
|
||||
relCfg := &mockReleaseConfig{repository: ""} // Empty to trigger detection
|
||||
|
|
@ -490,6 +498,7 @@ func TestLinuxKitPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := LinuxKitConfig{
|
||||
Config: "/project/.core/linuxkit/server.yml",
|
||||
|
|
@ -531,6 +540,7 @@ func TestLinuxKitPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := LinuxKitConfig{
|
||||
Config: "/config.yml",
|
||||
|
|
@ -560,6 +570,7 @@ func TestLinuxKitPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v2.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
cfg := LinuxKitConfig{
|
||||
Config: "/config.yml",
|
||||
|
|
@ -823,6 +834,7 @@ func TestLinuxKitPublisher_Publish_DryRun_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "linuxkit"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -855,6 +867,7 @@ func TestLinuxKitPublisher_Publish_DryRun_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "linuxkit",
|
||||
|
|
@ -892,6 +905,7 @@ func TestLinuxKitPublisher_Publish_DryRun_Good(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v2.0.0",
|
||||
ProjectDir: tmpDir,
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "linuxkit",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
//go:embed templates/npm/*.tmpl
|
||||
|
|
@ -88,10 +90,10 @@ func (p *NpmPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub
|
|||
}
|
||||
|
||||
if dryRun {
|
||||
return p.dryRunPublish(data, &npmCfg)
|
||||
return p.dryRunPublish(release.FS, data, &npmCfg)
|
||||
}
|
||||
|
||||
return p.executePublish(ctx, data, &npmCfg)
|
||||
return p.executePublish(ctx, release.FS, data, &npmCfg)
|
||||
}
|
||||
|
||||
// parseConfig extracts npm-specific configuration from the publisher config.
|
||||
|
|
@ -127,7 +129,7 @@ type npmTemplateData struct {
|
|||
}
|
||||
|
||||
// dryRunPublish shows what would be done without actually publishing.
|
||||
func (p *NpmPublisher) dryRunPublish(data npmTemplateData, cfg *NpmConfig) error {
|
||||
func (p *NpmPublisher) dryRunPublish(m io.Medium, data npmTemplateData, cfg *NpmConfig) error {
|
||||
fmt.Println()
|
||||
fmt.Println("=== DRY RUN: npm Publish ===")
|
||||
fmt.Println()
|
||||
|
|
@ -139,7 +141,7 @@ func (p *NpmPublisher) dryRunPublish(data npmTemplateData, cfg *NpmConfig) error
|
|||
fmt.Println()
|
||||
|
||||
// Generate and show package.json
|
||||
pkgJSON, err := p.renderTemplate("templates/npm/package.json.tmpl", data)
|
||||
pkgJSON, err := p.renderTemplate(m, "templates/npm/package.json.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("npm.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -157,7 +159,7 @@ func (p *NpmPublisher) dryRunPublish(data npmTemplateData, cfg *NpmConfig) error
|
|||
}
|
||||
|
||||
// executePublish actually creates and publishes the npm package.
|
||||
func (p *NpmPublisher) executePublish(ctx context.Context, data npmTemplateData, cfg *NpmConfig) error {
|
||||
func (p *NpmPublisher) executePublish(ctx context.Context, m io.Medium, data npmTemplateData, cfg *NpmConfig) error {
|
||||
// Check for NPM_TOKEN
|
||||
if os.Getenv("NPM_TOKEN") == "" {
|
||||
return fmt.Errorf("npm.Publish: NPM_TOKEN environment variable is required")
|
||||
|
|
@ -177,7 +179,7 @@ func (p *NpmPublisher) executePublish(ctx context.Context, data npmTemplateData,
|
|||
}
|
||||
|
||||
// Generate package.json
|
||||
pkgJSON, err := p.renderTemplate("templates/npm/package.json.tmpl", data)
|
||||
pkgJSON, err := p.renderTemplate(m, "templates/npm/package.json.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("npm.Publish: failed to render package.json: %w", err)
|
||||
}
|
||||
|
|
@ -186,7 +188,7 @@ func (p *NpmPublisher) executePublish(ctx context.Context, data npmTemplateData,
|
|||
}
|
||||
|
||||
// Generate install.js
|
||||
installJS, err := p.renderTemplate("templates/npm/install.js.tmpl", data)
|
||||
installJS, err := p.renderTemplate(m, "templates/npm/install.js.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("npm.Publish: failed to render install.js: %w", err)
|
||||
}
|
||||
|
|
@ -195,7 +197,7 @@ func (p *NpmPublisher) executePublish(ctx context.Context, data npmTemplateData,
|
|||
}
|
||||
|
||||
// Generate run.js
|
||||
runJS, err := p.renderTemplate("templates/npm/run.js.tmpl", data)
|
||||
runJS, err := p.renderTemplate(m, "templates/npm/run.js.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("npm.Publish: failed to render run.js: %w", err)
|
||||
}
|
||||
|
|
@ -228,10 +230,25 @@ func (p *NpmPublisher) executePublish(ctx context.Context, data npmTemplateData,
|
|||
}
|
||||
|
||||
// renderTemplate renders an embedded template with the given data.
|
||||
func (p *NpmPublisher) renderTemplate(name string, data npmTemplateData) (string, error) {
|
||||
content, err := npmTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
func (p *NpmPublisher) renderTemplate(m io.Medium, name string, data npmTemplateData) (string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
|
||||
// Try custom template from medium
|
||||
customPath := filepath.Join(".core", name)
|
||||
if m != nil && m.IsFile(customPath) {
|
||||
customContent, err := m.Read(customPath)
|
||||
if err == nil {
|
||||
content = []byte(customContent)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to embedded template
|
||||
if content == nil {
|
||||
content, err = npmTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.New(filepath.Base(name)).Parse(string(content))
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -101,7 +103,7 @@ func TestNpmPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
Access: "public",
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/npm/package.json.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/npm/package.json.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, `"name": "@myorg/mycli"`)
|
||||
|
|
@ -125,7 +127,7 @@ func TestNpmPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
Access: "restricted",
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/npm/package.json.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/npm/package.json.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, `"access": "restricted"`)
|
||||
|
|
@ -137,7 +139,7 @@ func TestNpmPublisher_RenderTemplate_Bad(t *testing.T) {
|
|||
|
||||
t.Run("returns error for non-existent template", func(t *testing.T) {
|
||||
data := npmTemplateData{}
|
||||
_, err := p.renderTemplate("templates/npm/nonexistent.tmpl", data)
|
||||
_, err := p.renderTemplate(io.Local, "templates/npm/nonexistent.tmpl", data)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to read template")
|
||||
})
|
||||
|
|
@ -164,7 +166,7 @@ func TestNpmPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Access: "public",
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -202,7 +204,7 @@ func TestNpmPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Access: "restricted",
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -224,6 +226,7 @@ func TestNpmPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "npm"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
@ -246,6 +249,7 @@ func TestNpmPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{
|
||||
Type: "npm",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
// Release represents a release to be published.
|
||||
|
|
@ -17,6 +18,8 @@ type Release struct {
|
|||
Changelog string
|
||||
// ProjectDir is the root directory of the project.
|
||||
ProjectDir string
|
||||
// FS is the medium for file operations.
|
||||
FS io.Medium
|
||||
}
|
||||
|
||||
// PublisherConfig holds configuration for a publisher.
|
||||
|
|
@ -48,12 +51,13 @@ type Publisher interface {
|
|||
|
||||
// NewRelease creates a Release from the release package's Release type.
|
||||
// This is a helper to convert between packages.
|
||||
func NewRelease(version string, artifacts []build.Artifact, changelog, projectDir string) *Release {
|
||||
func NewRelease(version string, artifacts []build.Artifact, changelog, projectDir string, fs io.Medium) *Release {
|
||||
return &Release{
|
||||
Version: version,
|
||||
Artifacts: artifacts,
|
||||
Changelog: changelog,
|
||||
ProjectDir: projectDir,
|
||||
FS: fs,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"text/template"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
//go:embed templates/scoop/*.tmpl
|
||||
|
|
@ -82,10 +83,10 @@ func (p *ScoopPublisher) Publish(ctx context.Context, release *Release, pubCfg P
|
|||
}
|
||||
|
||||
if dryRun {
|
||||
return p.dryRunPublish(data, cfg)
|
||||
return p.dryRunPublish(release.FS, data, cfg)
|
||||
}
|
||||
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg)
|
||||
return p.executePublish(ctx, release.ProjectDir, data, cfg, release)
|
||||
}
|
||||
|
||||
type scoopTemplateData struct {
|
||||
|
|
@ -119,7 +120,7 @@ func (p *ScoopPublisher) parseConfig(pubCfg PublisherConfig, relCfg ReleaseConfi
|
|||
return cfg
|
||||
}
|
||||
|
||||
func (p *ScoopPublisher) dryRunPublish(data scoopTemplateData, cfg ScoopConfig) error {
|
||||
func (p *ScoopPublisher) dryRunPublish(m io.Medium, data scoopTemplateData, cfg ScoopConfig) error {
|
||||
fmt.Println()
|
||||
fmt.Println("=== DRY RUN: Scoop Publish ===")
|
||||
fmt.Println()
|
||||
|
|
@ -129,7 +130,7 @@ func (p *ScoopPublisher) dryRunPublish(data scoopTemplateData, cfg ScoopConfig)
|
|||
fmt.Printf("Repository: %s\n", data.Repository)
|
||||
fmt.Println()
|
||||
|
||||
manifest, err := p.renderTemplate("templates/scoop/manifest.json.tmpl", data)
|
||||
manifest, err := p.renderTemplate(m, "templates/scoop/manifest.json.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("scoop.dryRunPublish: %w", err)
|
||||
}
|
||||
|
|
@ -155,8 +156,8 @@ func (p *ScoopPublisher) dryRunPublish(data scoopTemplateData, cfg ScoopConfig)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *ScoopPublisher) executePublish(ctx context.Context, projectDir string, data scoopTemplateData, cfg ScoopConfig) error {
|
||||
manifest, err := p.renderTemplate("templates/scoop/manifest.json.tmpl", data)
|
||||
func (p *ScoopPublisher) executePublish(ctx context.Context, projectDir string, data scoopTemplateData, cfg ScoopConfig, release *Release) error {
|
||||
manifest, err := p.renderTemplate(release.FS, "templates/scoop/manifest.json.tmpl", data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("scoop.Publish: failed to render manifest: %w", err)
|
||||
}
|
||||
|
|
@ -170,12 +171,12 @@ func (p *ScoopPublisher) executePublish(ctx context.Context, projectDir string,
|
|||
output = filepath.Join(projectDir, output)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(output, 0755); err != nil {
|
||||
if err := release.FS.EnsureDir(output); err != nil {
|
||||
return fmt.Errorf("scoop.Publish: failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
manifestPath := filepath.Join(output, fmt.Sprintf("%s.json", data.PackageName))
|
||||
if err := os.WriteFile(manifestPath, []byte(manifest), 0644); err != nil {
|
||||
if err := release.FS.Write(manifestPath, manifest); err != nil {
|
||||
return fmt.Errorf("scoop.Publish: failed to write manifest: %w", err)
|
||||
}
|
||||
fmt.Printf("Wrote Scoop manifest for official PR: %s\n", manifestPath)
|
||||
|
|
@ -245,10 +246,25 @@ func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *ScoopPublisher) renderTemplate(name string, data scoopTemplateData) (string, error) {
|
||||
content, err := scoopTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
func (p *ScoopPublisher) renderTemplate(m io.Medium, name string, data scoopTemplateData) (string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
|
||||
// Try custom template from medium
|
||||
customPath := filepath.Join(".core", name)
|
||||
if m != nil && m.IsFile(customPath) {
|
||||
customContent, err := m.Read(customPath)
|
||||
if err == nil {
|
||||
content = []byte(customContent)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to embedded template
|
||||
if content == nil {
|
||||
content, err = scoopTemplates.ReadFile(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.New(filepath.Base(name)).Parse(string(content))
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -105,7 +107,7 @@ func TestScoopPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/scoop/manifest.json.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/scoop/manifest.json.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, `"version": "1.2.3"`)
|
||||
|
|
@ -132,7 +134,7 @@ func TestScoopPublisher_RenderTemplate_Good(t *testing.T) {
|
|||
Checksums: ChecksumMap{},
|
||||
}
|
||||
|
||||
result, err := p.renderTemplate("templates/scoop/manifest.json.tmpl", data)
|
||||
result, err := p.renderTemplate(io.Local, "templates/scoop/manifest.json.tmpl", data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, result, `"checkver"`)
|
||||
|
|
@ -146,7 +148,7 @@ func TestScoopPublisher_RenderTemplate_Bad(t *testing.T) {
|
|||
|
||||
t.Run("returns error for non-existent template", func(t *testing.T) {
|
||||
data := scoopTemplateData{}
|
||||
_, err := p.renderTemplate("templates/scoop/nonexistent.tmpl", data)
|
||||
_, err := p.renderTemplate(io.Local, "templates/scoop/nonexistent.tmpl", data)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to read template")
|
||||
})
|
||||
|
|
@ -171,7 +173,7 @@ func TestScoopPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
Bucket: "owner/scoop-bucket",
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -209,7 +211,7 @@ func TestScoopPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -238,7 +240,7 @@ func TestScoopPublisher_DryRunPublish_Good(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err := p.dryRunPublish(data, cfg)
|
||||
err := p.dryRunPublish(io.Local, data, cfg)
|
||||
|
||||
_ = w.Close()
|
||||
var buf bytes.Buffer
|
||||
|
|
@ -258,6 +260,7 @@ func TestScoopPublisher_Publish_Bad(t *testing.T) {
|
|||
release := &Release{
|
||||
Version: "v1.0.0",
|
||||
ProjectDir: "/project",
|
||||
FS: io.Local,
|
||||
}
|
||||
pubCfg := PublisherConfig{Type: "scoop"}
|
||||
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
|
|
@ -25,6 +26,8 @@ type Release struct {
|
|||
Changelog string
|
||||
// ProjectDir is the root directory of the project.
|
||||
ProjectDir string
|
||||
// FS is the medium for file operations.
|
||||
FS io.Medium
|
||||
}
|
||||
|
||||
// Publish publishes pre-built artifacts from dist/ to configured targets.
|
||||
|
|
@ -35,6 +38,8 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
return nil, fmt.Errorf("release.Publish: config is nil")
|
||||
}
|
||||
|
||||
m := io.Local
|
||||
|
||||
projectDir := cfg.projectDir
|
||||
if projectDir == "" {
|
||||
projectDir = "."
|
||||
|
|
@ -57,7 +62,7 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
|
||||
// Step 2: Find pre-built artifacts in dist/
|
||||
distDir := filepath.Join(absProjectDir, "dist")
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(m, distDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("release.Publish: %w", err)
|
||||
}
|
||||
|
|
@ -78,11 +83,12 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
Artifacts: artifacts,
|
||||
Changelog: changelog,
|
||||
ProjectDir: absProjectDir,
|
||||
FS: m,
|
||||
}
|
||||
|
||||
// Step 4: Publish to configured targets
|
||||
if len(cfg.Publishers) > 0 {
|
||||
pubRelease := publishers.NewRelease(release.Version, release.Artifacts, release.Changelog, release.ProjectDir)
|
||||
pubRelease := publishers.NewRelease(release.Version, release.Artifacts, release.Changelog, release.ProjectDir, release.FS)
|
||||
|
||||
for _, pubCfg := range cfg.Publishers {
|
||||
publisher, err := getPublisher(pubCfg.Type)
|
||||
|
|
@ -102,14 +108,14 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
}
|
||||
|
||||
// findArtifacts discovers pre-built artifacts in the dist directory.
|
||||
func findArtifacts(distDir string) ([]build.Artifact, error) {
|
||||
if !io.Local.IsDir(distDir) {
|
||||
func findArtifacts(m io.Medium, distDir string) ([]build.Artifact, error) {
|
||||
if !m.IsDir(distDir) {
|
||||
return nil, fmt.Errorf("dist/ directory not found")
|
||||
}
|
||||
|
||||
var artifacts []build.Artifact
|
||||
|
||||
entries, err := io.Local.List(distDir)
|
||||
entries, err := m.List(distDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read dist/: %w", err)
|
||||
}
|
||||
|
|
@ -143,6 +149,8 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
return nil, fmt.Errorf("release.Run: config is nil")
|
||||
}
|
||||
|
||||
m := io.Local
|
||||
|
||||
projectDir := cfg.projectDir
|
||||
if projectDir == "" {
|
||||
projectDir = "."
|
||||
|
|
@ -171,7 +179,7 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
}
|
||||
|
||||
// Step 3: Build artifacts
|
||||
artifacts, err := buildArtifacts(ctx, cfg, absProjectDir, version)
|
||||
artifacts, err := buildArtifacts(ctx, m, cfg, absProjectDir, version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("release.Run: build failed: %w", err)
|
||||
}
|
||||
|
|
@ -181,12 +189,13 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
Artifacts: artifacts,
|
||||
Changelog: changelog,
|
||||
ProjectDir: absProjectDir,
|
||||
FS: m,
|
||||
}
|
||||
|
||||
// Step 4: Publish to configured targets
|
||||
if len(cfg.Publishers) > 0 {
|
||||
// Convert to publisher types
|
||||
pubRelease := publishers.NewRelease(release.Version, release.Artifacts, release.Changelog, release.ProjectDir)
|
||||
pubRelease := publishers.NewRelease(release.Version, release.Artifacts, release.Changelog, release.ProjectDir, release.FS)
|
||||
|
||||
for _, pubCfg := range cfg.Publishers {
|
||||
publisher, err := getPublisher(pubCfg.Type)
|
||||
|
|
@ -207,7 +216,7 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
|||
}
|
||||
|
||||
// buildArtifacts builds all artifacts for the release.
|
||||
func buildArtifacts(ctx context.Context, cfg *Config, projectDir, version string) ([]build.Artifact, error) {
|
||||
func buildArtifacts(ctx context.Context, m io.Medium, cfg *Config, projectDir, version string) ([]build.Artifact, error) {
|
||||
// Load build configuration
|
||||
buildCfg, err := build.LoadConfig(projectDir)
|
||||
if err != nil {
|
||||
|
|
@ -287,7 +296,16 @@ func buildArtifacts(ctx context.Context, cfg *Config, projectDir, version string
|
|||
|
||||
// Write CHECKSUMS.txt
|
||||
checksumPath := filepath.Join(outputDir, "CHECKSUMS.txt")
|
||||
if err := build.WriteChecksumFile(checksummedArtifacts, checksumPath); err != nil {
|
||||
var lines []string
|
||||
for _, artifact := range checksummedArtifacts {
|
||||
if artifact.Checksum != "" {
|
||||
lines = append(lines, fmt.Sprintf("%s %s", artifact.Checksum, filepath.Base(artifact.Path)))
|
||||
}
|
||||
}
|
||||
sort.Strings(lines)
|
||||
content := strings.Join(lines, "\n") + "\n"
|
||||
|
||||
if err := m.Write(checksumPath, content); err != nil {
|
||||
return nil, fmt.Errorf("failed to write checksums file: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/host-uk/core/pkg/build"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -22,7 +23,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app-linux-amd64.tar.gz"), []byte("test"), 0644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app-darwin-arm64.tar.gz"), []byte("test"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, artifacts, 2)
|
||||
|
|
@ -35,7 +36,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app-windows-amd64.zip"), []byte("test"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, artifacts, 1)
|
||||
|
|
@ -49,7 +50,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "CHECKSUMS.txt"), []byte("checksums"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, artifacts, 1)
|
||||
|
|
@ -63,7 +64,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app.tar.gz.sig"), []byte("signature"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, artifacts, 1)
|
||||
|
|
@ -79,7 +80,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "CHECKSUMS.txt"), []byte("checksums"), 0644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app.sig"), []byte("sig"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, artifacts, 4)
|
||||
|
|
@ -94,7 +95,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app.exe"), []byte("binary"), 0644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app.tar.gz"), []byte("artifact"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, artifacts, 1)
|
||||
|
|
@ -110,7 +111,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "app.tar.gz"), []byte("artifact"), 0644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(distDir, "subdir", "nested.tar.gz"), []byte("nested"), 0644))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should only find the top-level artifact
|
||||
|
|
@ -122,7 +123,7 @@ func TestFindArtifacts_Good(t *testing.T) {
|
|||
distDir := filepath.Join(dir, "dist")
|
||||
require.NoError(t, os.MkdirAll(distDir, 0755))
|
||||
|
||||
artifacts, err := findArtifacts(distDir)
|
||||
artifacts, err := findArtifacts(io.Local, distDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, artifacts)
|
||||
|
|
@ -134,7 +135,7 @@ func TestFindArtifacts_Bad(t *testing.T) {
|
|||
dir := t.TempDir()
|
||||
distDir := filepath.Join(dir, "dist")
|
||||
|
||||
_, err := findArtifacts(distDir)
|
||||
_, err := findArtifacts(io.Local, distDir)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "dist/ directory not found")
|
||||
})
|
||||
|
|
@ -149,7 +150,7 @@ func TestFindArtifacts_Bad(t *testing.T) {
|
|||
require.NoError(t, os.Chmod(distDir, 0000))
|
||||
defer func() { _ = os.Chmod(distDir, 0755) }()
|
||||
|
||||
_, err := findArtifacts(distDir)
|
||||
_, err := findArtifacts(io.Local, distDir)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to read dist/")
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue