Skip to content

Commit b54d257

Browse files
bepclaude
andcommitted
Restructure publish config to use publishers slice
Follow the archives pattern: top-level publish_settings for shared defaults and a publishers slice where each entry can match specific releases via paths and has its own type (built-in or plugin). - Add internal/publish/publishformats package with Format enum - Rewrite publish_config.go with Publisher struct and PublishType - Add Publishers field to Config struct - Update core.go to compile publisher -> release mappings - Rewrite publishcmd to iterate over publishers - Update test scripts and hugoreleaser.yaml Built-in publisher types: - github_release: Undrafts the GitHub release - homebrew_cask: Updates Homebrew cask file - _plugin: External plugin (future) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 0f31095 commit b54d257

File tree

14 files changed

+392
-157
lines changed

14 files changed

+392
-157
lines changed

cmd/corecmd/core.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,18 @@ func (c *Core) Init() error {
413413
}
414414
}
415415

416+
// Precompile publisher -> release mappings.
417+
for i := range c.Config.Publishers {
418+
pub := &c.Config.Publishers[i]
419+
for j := range c.Config.Releases {
420+
release := &c.Config.Releases[j]
421+
// If no release paths specified, match all releases.
422+
if pub.ReleasePathsCompiled == nil || pub.ReleasePathsCompiled.Match(release.Path) {
423+
pub.ReleasesCompiled = append(pub.ReleasesCompiled, release)
424+
}
425+
}
426+
}
427+
416428
// Registry for archive plugins.
417429
c.PluginsRegistryArchive = make(map[string]*execrpc.Client[apimodel.Config, archiveplugin.Request, any, apimodel.Receipt])
418430

cmd/publishcmd/publish.go

Lines changed: 107 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ import (
2525
"text/template"
2626

2727
"github.com/bep/logg"
28+
"github.com/gohugoio/hugoreleaser-plugins-api/model"
2829
"github.com/gohugoio/hugoreleaser/cmd/corecmd"
30+
"github.com/gohugoio/hugoreleaser/internal/common/matchers"
2931
"github.com/gohugoio/hugoreleaser/internal/common/templ"
3032
"github.com/gohugoio/hugoreleaser/internal/config"
33+
"github.com/gohugoio/hugoreleaser/internal/publish/publishformats"
3134
"github.com/gohugoio/hugoreleaser/internal/releases"
3235
"github.com/gohugoio/hugoreleaser/staticfiles"
3336
"github.com/peterbourgon/ff/v3/ffcli"
@@ -46,7 +49,7 @@ func New(core *corecmd.Core) *ffcli.Command {
4649
return &ffcli.Command{
4750
Name: commandName,
4851
ShortUsage: corecmd.CommandName + " publish [flags]",
49-
ShortHelp: "Publish a draft release and update package managers.",
52+
ShortHelp: "Publish releases and update package managers.",
5053
FlagSet: fs,
5154
Exec: publisher.Exec,
5255
}
@@ -77,21 +80,43 @@ func (p *Publisher) Exec(ctx context.Context, args []string) error {
7780
return err
7881
}
7982

80-
// Get release settings from config.
81-
if len(p.core.Config.Releases) == 0 {
82-
return fmt.Errorf("%s: no releases defined in config", commandName)
83+
if len(p.core.Config.Publishers) == 0 {
84+
p.infoLog.Log(logg.String("No publishers configured"))
85+
return nil
8386
}
8487

85-
// Use first release for settings (consistent with release command behavior).
86-
release := p.core.Config.Releases[0]
87-
settings := release.ReleaseSettings
88-
8988
logFields := logg.Fields{
9089
{Name: "tag", Value: p.core.Tag},
91-
{Name: "repository", Value: fmt.Sprintf("%s/%s", settings.RepositoryOwner, settings.Repository)},
9290
}
9391
logCtx := p.infoLog.WithFields(logFields)
9492

93+
// Process each publisher.
94+
for i := range p.core.Config.Publishers {
95+
pub := &p.core.Config.Publishers[i]
96+
97+
if len(pub.ReleasesCompiled) == 0 {
98+
continue
99+
}
100+
101+
// Process each release that matches this publisher.
102+
for _, release := range pub.ReleasesCompiled {
103+
if err := p.handlePublisher(ctx, logCtx, pub, release); err != nil {
104+
return err
105+
}
106+
}
107+
}
108+
109+
return nil
110+
}
111+
112+
func (p *Publisher) handlePublisher(
113+
ctx context.Context,
114+
logCtx logg.LevelLogger,
115+
pub *config.Publisher,
116+
release *config.Release,
117+
) error {
118+
settings := release.ReleaseSettings
119+
95120
// Create client.
96121
var client releases.PublishClient
97122
if p.core.Try {
@@ -108,7 +133,31 @@ func (p *Publisher) Exec(ctx context.Context, args []string) error {
108133
}
109134
}
110135

111-
// Step 1: Check and publish the GitHub release.
136+
switch pub.Type.FormatParsed {
137+
case publishformats.GitHubRelease:
138+
return p.publishGitHubRelease(ctx, logCtx, client, release)
139+
case publishformats.HomebrewCask:
140+
return p.updateHomebrewCask(ctx, logCtx, client, pub, release)
141+
case publishformats.Plugin:
142+
return fmt.Errorf("%s: plugin publishers not yet implemented", commandName)
143+
default:
144+
return fmt.Errorf("%s: unknown publisher format: %s", commandName, pub.Type.Format)
145+
}
146+
}
147+
148+
func (p *Publisher) publishGitHubRelease(
149+
ctx context.Context,
150+
logCtx logg.LevelLogger,
151+
client releases.PublishClient,
152+
release *config.Release,
153+
) error {
154+
settings := release.ReleaseSettings
155+
156+
logCtx = logCtx.WithFields(logg.Fields{
157+
{Name: "action", Value: "github_release"},
158+
{Name: "repository", Value: fmt.Sprintf("%s/%s", settings.RepositoryOwner, settings.Repository)},
159+
})
160+
112161
logCtx.Log(logg.String("Checking release status"))
113162

114163
releaseID, isDraft, err := client.GetReleaseByTag(ctx, settings.RepositoryOwner, settings.Repository, p.core.Tag)
@@ -126,17 +175,20 @@ func (p *Publisher) Exec(ctx context.Context, args []string) error {
126175
logCtx.Log(logg.String("Release is already published"))
127176
}
128177

129-
// Step 2: Update Homebrew cask if enabled.
130-
caskSettings := p.core.Config.PublishSettings.HomebrewCask
131-
if caskSettings.Enabled {
132-
if err := p.updateHomebrewCask(ctx, logCtx, client, release, caskSettings); err != nil {
133-
return fmt.Errorf("%s: failed to update Homebrew cask: %v", commandName, err)
134-
}
135-
}
136-
137178
return nil
138179
}
139180

181+
// HomebrewCaskSettings holds the custom settings for homebrew_cask publisher.
182+
type HomebrewCaskSettings struct {
183+
BundleIdentifier string `mapstructure:"bundle_identifier"`
184+
TapRepository string `mapstructure:"tap_repository"`
185+
Name string `mapstructure:"name"`
186+
CaskPath string `mapstructure:"cask_path"`
187+
TemplateFilename string `mapstructure:"template_filename"`
188+
Description string `mapstructure:"description"`
189+
Homepage string `mapstructure:"homepage"`
190+
}
191+
140192
// HomebrewCaskContext holds data for the Homebrew cask template.
141193
type HomebrewCaskContext struct {
142194
Name string
@@ -154,17 +206,34 @@ func (p *Publisher) updateHomebrewCask(
154206
ctx context.Context,
155207
logCtx logg.LevelLogger,
156208
client releases.PublishClient,
157-
release config.Release,
158-
caskSettings config.HomebrewCaskSettings,
209+
pub *config.Publisher,
210+
release *config.Release,
159211
) error {
160-
logCtx = logCtx.WithField("action", "homebrew-cask")
212+
logCtx = logCtx.WithField("action", "homebrew_cask")
161213
logCtx.Log(logg.String("Updating Homebrew cask"))
162214

163215
releaseSettings := release.ReleaseSettings
164216
version := strings.TrimPrefix(p.core.Tag, "v")
165217

166-
// Find the first .pkg archive matching the path pattern.
167-
pkgInfo, err := p.findPkgArchive(release, caskSettings)
218+
// Read settings from custom_settings.
219+
settings, err := model.FromMap[any, HomebrewCaskSettings](pub.CustomSettings)
220+
if err != nil {
221+
return fmt.Errorf("failed to parse homebrew_cask settings: %w", err)
222+
}
223+
224+
// Apply defaults.
225+
if settings.TapRepository == "" {
226+
settings.TapRepository = "homebrew-tap"
227+
}
228+
if settings.Name == "" {
229+
settings.Name = p.core.Config.Project
230+
}
231+
if settings.CaskPath == "" {
232+
settings.CaskPath = fmt.Sprintf("Casks/%s.rb", settings.Name)
233+
}
234+
235+
// Find the first .pkg archive matching the archive paths pattern.
236+
pkgInfo, err := p.findPkgArchive(release, pub.ArchivePathsCompiled)
168237
if err != nil {
169238
return err
170239
}
@@ -182,23 +251,23 @@ func (p *Publisher) updateHomebrewCask(
182251

183252
// Build cask context.
184253
caskCtx := HomebrewCaskContext{
185-
Name: caskSettings.Name,
254+
Name: settings.Name,
186255
DisplayName: p.core.Config.Project,
187256
Version: version,
188257
SHA256: pkgInfo.SHA256,
189258
URL: downloadURL,
190-
Description: caskSettings.Description,
191-
Homepage: caskSettings.Homepage,
259+
Description: settings.Description,
260+
Homepage: settings.Homepage,
192261
PkgFilename: pkgInfo.Name,
193-
BundleIdentifier: caskSettings.BundleIdentifier,
262+
BundleIdentifier: settings.BundleIdentifier,
194263
}
195264

196265
// Render cask template.
197266
var caskContent bytes.Buffer
198267
var tmpl *template.Template
199268

200-
if caskSettings.TemplateFilename != "" {
201-
templatePath := caskSettings.TemplateFilename
269+
if settings.TemplateFilename != "" {
270+
templatePath := settings.TemplateFilename
202271
if !filepath.IsAbs(templatePath) {
203272
templatePath = filepath.Join(p.core.ProjectDir, templatePath)
204273
}
@@ -219,11 +288,11 @@ func (p *Publisher) updateHomebrewCask(
219288
}
220289

221290
// Update file in tap repository.
222-
commitMessage := fmt.Sprintf("Update %s to %s", caskSettings.Name, p.core.Tag)
291+
commitMessage := fmt.Sprintf("Update %s to %s", settings.Name, p.core.Tag)
223292

224293
logCtx.WithFields(logg.Fields{
225-
{Name: "tap", Value: fmt.Sprintf("%s/%s", releaseSettings.RepositoryOwner, caskSettings.TapRepository)},
226-
{Name: "path", Value: caskSettings.CaskPath},
294+
{Name: "tap", Value: fmt.Sprintf("%s/%s", releaseSettings.RepositoryOwner, settings.TapRepository)},
295+
{Name: "path", Value: settings.CaskPath},
227296
}).Log(logg.String("Committing cask update"))
228297

229298
if p.core.Try {
@@ -234,8 +303,8 @@ func (p *Publisher) updateHomebrewCask(
234303
sha, err := client.UpdateFileInRepo(
235304
ctx,
236305
releaseSettings.RepositoryOwner,
237-
caskSettings.TapRepository,
238-
caskSettings.CaskPath,
306+
settings.TapRepository,
307+
settings.CaskPath,
239308
commitMessage,
240309
caskContent.Bytes(),
241310
)
@@ -253,18 +322,16 @@ type pkgArchiveInfo struct {
253322
SHA256 string
254323
}
255324

256-
// findPkgArchive finds the first .pkg archive for darwin matching the path pattern.
257-
func (p *Publisher) findPkgArchive(release config.Release, caskSettings config.HomebrewCaskSettings) (pkgArchiveInfo, error) {
258-
pathMatcher := caskSettings.PathCompiled
259-
325+
// findPkgArchive finds the first .pkg archive for darwin matching the archive paths pattern.
326+
func (p *Publisher) findPkgArchive(release *config.Release, archivePathsMatcher matchers.Matcher) (pkgArchiveInfo, error) {
260327
for _, archPath := range release.ArchsCompiled {
261328
// Only consider darwin archives.
262329
if archPath.Arch.Os == nil || archPath.Arch.Os.Goos != "darwin" {
263330
continue
264331
}
265332

266333
// Check if the path matches the pattern.
267-
if pathMatcher != nil && !pathMatcher.Match(archPath.Path) {
334+
if archivePathsMatcher != nil && !archivePathsMatcher.Match(archPath.Path) {
268335
continue
269336
}
270337

@@ -277,5 +344,5 @@ func (p *Publisher) findPkgArchive(release config.Release, caskSettings config.H
277344
}
278345
}
279346

280-
return pkgArchiveInfo{}, fmt.Errorf("no .pkg archive found for darwin matching path pattern %q", caskSettings.Path)
347+
return pkgArchiveInfo{}, fmt.Errorf("no .pkg archive found for darwin")
281348
}

cmd/releasecmd/release.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ func (b *Releaser) Init() error {
7979
}
8080

8181
b.infoLog = b.core.InfoLog.WithField("cmd", commandName)
82-
8382
releaseMatches := b.core.Config.FindReleases(b.core.PathsReleasesCompiled)
8483
if len(releaseMatches) == 0 {
8584
return fmt.Errorf("%s: no releases found matching -paths %v", commandName, b.core.Paths)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/gohugoio/hugoreleaser/plugins v0.1.1-0.20220822083757-38d81884db04
1313
github.com/google/go-github/v45 v45.2.0
1414
github.com/mattn/go-isatty v0.0.20
15-
github.com/mitchellh/mapstructure v1.5.0 // indirect
15+
github.com/mitchellh/mapstructure v1.5.0
1616
github.com/pelletier/go-toml/v2 v2.2.4
1717
github.com/peterbourgon/ff/v3 v3.4.0
1818
github.com/rogpeppe/go-internal v1.14.1

0 commit comments

Comments
 (0)