First commit
This commit is contained in:
		
						commit
						886da1fce3
					
				
							
								
								
									
										29
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
on:
 | 
			
		||||
  release:
 | 
			
		||||
    types: [created]
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
    contents: write
 | 
			
		||||
    packages: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  goreleaser:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
      -
 | 
			
		||||
        name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v4
 | 
			
		||||
      -
 | 
			
		||||
        name: Run GoReleaser
 | 
			
		||||
        uses: goreleaser/goreleaser-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          distribution: goreleaser
 | 
			
		||||
          version: latest
 | 
			
		||||
          args: release --clean
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
							
								
								
									
										21
									
								
								.github/workflows/test_golang.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.github/workflows/test_golang.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
name: Go package
 | 
			
		||||
 | 
			
		||||
on: [push]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v4
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: '1.21'
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: go build -v ./...
 | 
			
		||||
 | 
			
		||||
      - name: Test
 | 
			
		||||
        run: go test -v ./...
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
goreleaser-http-repo-builder
 | 
			
		||||
							
								
								
									
										26
									
								
								.goreleaser.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.goreleaser.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
# This is an example .goreleaser.yml file with some sensible defaults.
 | 
			
		||||
# Make sure to check the documentation at https://goreleaser.com
 | 
			
		||||
 | 
			
		||||
# The lines below are called `modelines`. See `:help modeline`
 | 
			
		||||
# Feel free to remove those if you don't want/need to use them.
 | 
			
		||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
 | 
			
		||||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
 | 
			
		||||
 | 
			
		||||
version: 2
 | 
			
		||||
 | 
			
		||||
before:
 | 
			
		||||
  hooks:
 | 
			
		||||
    - go mod tidy
 | 
			
		||||
 | 
			
		||||
builds:
 | 
			
		||||
  - env:
 | 
			
		||||
      - CGO_ENABLED=0
 | 
			
		||||
    goos:
 | 
			
		||||
      - linux
 | 
			
		||||
      - darwin
 | 
			
		||||
 | 
			
		||||
archives:
 | 
			
		||||
  - format: tar.gz
 | 
			
		||||
    # this name template makes the OS and Arch compatible with the results of `uname`.
 | 
			
		||||
    name_template: "{{ .ProjectName }}-{{ .Version }}.{{ .Os }}-{{ .Arch }}"
 | 
			
		||||
    wrap_in_directory: true
 | 
			
		||||
							
								
								
									
										19
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
Copyright (c) 2024 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										63
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
# goreleaser-http-repo-builder
 | 
			
		||||
 | 
			
		||||
This tool was written out of the need to build a release repository compatible with [go-selfupdate](https://github.com/creativeprojects/go-selfupdate) with releases built by [goreleaser](https://goreleaser.com/).
 | 
			
		||||
 | 
			
		||||
## Example Usage
 | 
			
		||||
 | 
			
		||||
The command has extensive help available, the following is an example of building a release and adding it to a new repo.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
goreleaser release --snapshot --skip=publish
 | 
			
		||||
mkdir repo
 | 
			
		||||
goreleaser-http-repo-builder add-release --repo=repo/ --release=dist/
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After adding a release, you can copy the repo to your web server for update distrobution.
 | 
			
		||||
 | 
			
		||||
## Example Goreleaser Config
 | 
			
		||||
 | 
			
		||||
While there is good [documentation available](https://goreleaser.com/customization/) that I'd recommend reading, the following provides some examples that may be helpful in generating a release that is compatible with go-selfupdate.
 | 
			
		||||
 | 
			
		||||
- The checksums file name defaults to preappend the project name, which is not compatible if you wish to use the checksums to verify an update.
 | 
			
		||||
- If you're signing releases with an ECDSA key, this is what I found works best.
 | 
			
		||||
- If you need to specify the version manually, you can edit the version template. By default, goreleaser will use the git tag to determine the version.
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
version: 2
 | 
			
		||||
 | 
			
		||||
before:
 | 
			
		||||
  hooks:
 | 
			
		||||
    - go mod tidy
 | 
			
		||||
 | 
			
		||||
builds:
 | 
			
		||||
  - env:
 | 
			
		||||
      - CGO_ENABLED=0
 | 
			
		||||
    goos:
 | 
			
		||||
      - linux
 | 
			
		||||
    goarch:
 | 
			
		||||
      - amd64
 | 
			
		||||
      - arm64
 | 
			
		||||
 | 
			
		||||
archives:
 | 
			
		||||
  - format: tar.gz
 | 
			
		||||
    name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
 | 
			
		||||
    wrap_in_directory: true
 | 
			
		||||
 | 
			
		||||
checksum:
 | 
			
		||||
    name_template: "checksums.txt"
 | 
			
		||||
 | 
			
		||||
signs:
 | 
			
		||||
  - artifacts: all
 | 
			
		||||
    cmd: openssl
 | 
			
		||||
    args:
 | 
			
		||||
        - dgst
 | 
			
		||||
        - -sha256
 | 
			
		||||
        - -sign
 | 
			
		||||
        - "signing.key"
 | 
			
		||||
        - -out
 | 
			
		||||
        - ${signature}
 | 
			
		||||
        - ${artifact}
 | 
			
		||||
 | 
			
		||||
snapshot:
 | 
			
		||||
    version_template: "v0.1.2"
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										187
									
								
								add_release_cmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								add_release_cmd.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,187 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AddReleaseCmd struct {
 | 
			
		||||
	Release        string    `help:"Path to goreleaser dist folder." required:"" type:"existingdir"`
 | 
			
		||||
	Notes          string    `help:"Notes about this release."`
 | 
			
		||||
	Draft          bool      `help:"Is this release a draft?"`
 | 
			
		||||
	Prerelease     bool      `help:"Is this a prelease?"`
 | 
			
		||||
	IncludeBinary  bool      `help:"Include binary artifacts."`
 | 
			
		||||
	Force          bool      `help:"Force add, removing existing if needed."`
 | 
			
		||||
	PublishedAt    time.Time `help:"Specify exact time for release."`
 | 
			
		||||
	PublishedAtNow bool      `help:"Use the current time for published at instead of the metadata date."`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Adds a release to a repo.
 | 
			
		||||
func (a *AddReleaseCmd) Run() error {
 | 
			
		||||
	// Read existing manifest for repo.
 | 
			
		||||
	manifestFile := filepath.Join(app.flags.Repo, "manifest.yaml")
 | 
			
		||||
	manifest, err := readManifestFile(manifestFile)
 | 
			
		||||
	if os.IsNotExist(err) {
 | 
			
		||||
		err = os.MkdirAll(app.flags.Repo, 0755)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read metadata from goreleaser.
 | 
			
		||||
	metadata, err := readMetadataFile(filepath.Join(a.Release, "metadata.json"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	versionPath := filepath.Join(app.flags.Repo, metadata.Version)
 | 
			
		||||
 | 
			
		||||
	// Read the artifcats to ensure we have a valid release.
 | 
			
		||||
	artifacts, err := readArtifactFile(filepath.Join(a.Release, "artifacts.json"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if len(artifacts) == 0 {
 | 
			
		||||
		return errors.New("no artifacts in release")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Validate the base dir for artifacts. It could be one dir up, or 2 dirs up.
 | 
			
		||||
	artifcatBase := a.Release
 | 
			
		||||
	artifactLayers := 0
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(artifcatBase, artifacts[0].Path)); serr != nil {
 | 
			
		||||
		artifcatBase = filepath.Dir(artifcatBase)
 | 
			
		||||
		artifactLayers = 1
 | 
			
		||||
		if _, serr := os.Stat(filepath.Join(artifcatBase, artifacts[0].Path)); serr != nil {
 | 
			
		||||
			artifcatBase = filepath.Dir(artifcatBase)
 | 
			
		||||
			artifactLayers = 2
 | 
			
		||||
			if _, serr := os.Stat(filepath.Join(artifcatBase, artifacts[0].Path)); serr != nil {
 | 
			
		||||
				return errors.New("unable to determine artificate base path")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if the version already exists.
 | 
			
		||||
	existingIndex := -1
 | 
			
		||||
	for i, release := range manifest.Releases {
 | 
			
		||||
		if release.TagName == metadata.Version {
 | 
			
		||||
			existingIndex = i
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the version already exists, ask about replacing.
 | 
			
		||||
	if existingIndex != -1 {
 | 
			
		||||
		if !a.Force {
 | 
			
		||||
			ans := askForConfirmation("This release already exists, should we replace?")
 | 
			
		||||
 | 
			
		||||
			// If we don't want to replace, we should stop here.
 | 
			
		||||
			if !ans {
 | 
			
		||||
				return errors.New("version already exists")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We need to replace the release, so remove it.
 | 
			
		||||
		manifest.Releases = append(manifest.Releases[:existingIndex], manifest.Releases[existingIndex+1:]...)
 | 
			
		||||
 | 
			
		||||
		// Remove the version directory.
 | 
			
		||||
		os.RemoveAll(versionPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Make the release.
 | 
			
		||||
	manifest.LastReleaseID++
 | 
			
		||||
	release := &HttpRelease{
 | 
			
		||||
		ReleaseID:    manifest.LastReleaseID,
 | 
			
		||||
		Name:         metadata.Name,
 | 
			
		||||
		TagName:      metadata.Version,
 | 
			
		||||
		URL:          metadata.Version,
 | 
			
		||||
		Draft:        a.Draft,
 | 
			
		||||
		Prerelease:   a.Prerelease,
 | 
			
		||||
		PublishedAt:  metadata.Date,
 | 
			
		||||
		ReleaseNotes: a.Notes,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the publish date provided is valid, override.
 | 
			
		||||
	if !a.PublishedAt.IsZero() {
 | 
			
		||||
		release.PublishedAt = a.PublishedAt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If published at is requested to be now, override.
 | 
			
		||||
	if a.PublishedAtNow {
 | 
			
		||||
		release.PublishedAt = app.now
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Make the directory for the release.
 | 
			
		||||
	err = os.Mkdir(versionPath, 0755)
 | 
			
		||||
	if err != nil && !os.IsExist(err) {
 | 
			
		||||
		return fmt.Errorf("Error making release directory: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add artifacts.
 | 
			
		||||
	for _, artifact := range artifacts {
 | 
			
		||||
		// Skip binaries if not included.
 | 
			
		||||
		if artifact.Type == "Binary" && !a.IncludeBinary {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get the file path and confirm it exists and get its stat for file size.
 | 
			
		||||
		path := filepath.Join(artifcatBase, artifact.Path)
 | 
			
		||||
		stat, serr := os.Stat(path)
 | 
			
		||||
		if serr != nil {
 | 
			
		||||
			log.Println("Ignoring artifact", artifact.Name, "as its file does not exist.")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Determine relative path.
 | 
			
		||||
		s := strings.Split(artifact.Path, "/")
 | 
			
		||||
		relativePath := filepath.Join(s[artifactLayers:]...)
 | 
			
		||||
 | 
			
		||||
		// Determine if artifact is in its own sub dir, make sure it exists.
 | 
			
		||||
		dir := filepath.Dir(relativePath)
 | 
			
		||||
		if dir != "." {
 | 
			
		||||
			os.MkdirAll(filepath.Join(versionPath, dir), 0755)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Copy artifact to repo.
 | 
			
		||||
		err = copyFile(path, filepath.Join(versionPath, relativePath))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("Failed to copy artifact, skipping it: %s", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Make asset.
 | 
			
		||||
		manifest.LastAssetID++
 | 
			
		||||
		asset := &HttpAsset{
 | 
			
		||||
			ID:   manifest.LastAssetID,
 | 
			
		||||
			Name: artifact.Name,
 | 
			
		||||
			Size: int(stat.Size()),
 | 
			
		||||
			URL:  filepath.Join(metadata.Version, relativePath),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Add to the release.
 | 
			
		||||
		release.Assets = append(release.Assets, asset)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add release to manifest.
 | 
			
		||||
	manifest.Releases = append(manifest.Releases, release)
 | 
			
		||||
 | 
			
		||||
	// Write the manifest.
 | 
			
		||||
	err = writeManifestFile(manifestFile, manifest)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If not a draft or prerelease, link latest to this release.
 | 
			
		||||
	if !a.Draft && !a.Prerelease {
 | 
			
		||||
		latestPath := filepath.Join(app.flags.Repo, "latest")
 | 
			
		||||
		os.Remove(latestPath)
 | 
			
		||||
		os.Symlink(metadata.Version, latestPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Println("Added release", metadata.Version, "for", metadata.Name, "to the repo", app.flags.Repo)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								flags.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								flags.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/alecthomas/kong"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type VersionFlag bool
 | 
			
		||||
 | 
			
		||||
func (v VersionFlag) Decode(ctx *kong.DecodeContext) error { return nil }
 | 
			
		||||
func (v VersionFlag) IsBool() bool                         { return true }
 | 
			
		||||
func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error {
 | 
			
		||||
	fmt.Println(appName + ": " + appVersion)
 | 
			
		||||
	app.Exit(0)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flags supplied to cli.
 | 
			
		||||
type Flags struct {
 | 
			
		||||
	Version    VersionFlag   `name:"version" help:"Print version information and quit"`
 | 
			
		||||
	Repo       string        `help:"The path to a repo" required:"" type:"existingdir"`
 | 
			
		||||
	AddRelease AddReleaseCmd `cmd:"" help:"Add an release to the repo"`
 | 
			
		||||
	Prune      PruneCmd      `cmd:"" help:"Prune releases from repo."`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse the supplied flags.
 | 
			
		||||
func (a *App) ParseFlags() *kong.Context {
 | 
			
		||||
	app.flags = &Flags{}
 | 
			
		||||
 | 
			
		||||
	ctx := kong.Parse(app.flags,
 | 
			
		||||
		kong.Name(appName),
 | 
			
		||||
		kong.Description(appDescription),
 | 
			
		||||
		kong.UsageOnError(),
 | 
			
		||||
		kong.ConfigureHelp(kong.HelpOptions{
 | 
			
		||||
			Compact: true,
 | 
			
		||||
		}),
 | 
			
		||||
	)
 | 
			
		||||
	return ctx
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
module github.com/grmrgecko/goreleaser-http-repo-builder
 | 
			
		||||
 | 
			
		||||
go 1.23
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/alecthomas/kong v1.2.1
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										12
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY=
 | 
			
		||||
github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
 | 
			
		||||
github.com/alecthomas/kong v1.2.1 h1:E8jH4Tsgv6wCRX2nGrdPyHDUCSG83WH2qE4XLACD33Q=
 | 
			
		||||
github.com/alecthomas/kong v1.2.1/go.mod h1:rKTSFhbdp3Ryefn8x5MOEprnRFQ7nlmMC01GKhehhBM=
 | 
			
		||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
 | 
			
		||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 | 
			
		||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 | 
			
		||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
							
								
								
									
										57
									
								
								goreleaser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								goreleaser.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The metadata needed from goreleaser.
 | 
			
		||||
type Metadata struct {
 | 
			
		||||
	Name    string    `json:"project_name"`
 | 
			
		||||
	Version string    `json:"version"`
 | 
			
		||||
	Date    time.Time `json:"date"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read and parse metadata file
 | 
			
		||||
func readMetadataFile(metadataFile string) (*Metadata, error) {
 | 
			
		||||
	// Read file, if error return the error.
 | 
			
		||||
	jsonFile, err := os.Open(metadataFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Attempt to decode the file.
 | 
			
		||||
	metadata := new(Metadata)
 | 
			
		||||
	decoder := json.NewDecoder(jsonFile)
 | 
			
		||||
	err = decoder.Decode(metadata)
 | 
			
		||||
	jsonFile.Close()
 | 
			
		||||
 | 
			
		||||
	// Return the metadata and if any error occurred.
 | 
			
		||||
	return metadata, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Artifcat map.
 | 
			
		||||
type Artifact struct {
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Path string `json:"path"`
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read and parse metadata file
 | 
			
		||||
func readArtifactFile(artifactFile string) ([]*Artifact, error) {
 | 
			
		||||
	// Read file, if error return the error.
 | 
			
		||||
	jsonFile, err := os.Open(artifactFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Attempt to decode the file.
 | 
			
		||||
	var artifacts []*Artifact
 | 
			
		||||
	decoder := json.NewDecoder(jsonFile)
 | 
			
		||||
	err = decoder.Decode(&artifacts)
 | 
			
		||||
	jsonFile.Close()
 | 
			
		||||
 | 
			
		||||
	// Return the metadata and if any error occurred.
 | 
			
		||||
	return artifacts, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	appName        = "goreleaser-http-repo-builder"
 | 
			
		||||
	appDescription = "Builds a repo for use with go-selfupdate"
 | 
			
		||||
	appVersion     = "0.1.0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// App is the global application structure for communicating between servers and storing information.
 | 
			
		||||
type App struct {
 | 
			
		||||
	flags *Flags
 | 
			
		||||
	now   time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var app *App
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = time.Now()
 | 
			
		||||
	ctx := app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	err := ctx.Run()
 | 
			
		||||
	ctx.FatalIfErrorf(err)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										224
									
								
								main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								main_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,224 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Test the add release option.
 | 
			
		||||
func TestAppFunctionality(t *testing.T) {
 | 
			
		||||
	// Make temp directory to build repo.
 | 
			
		||||
	dname, err := os.MkdirTemp("", "goreleaser-http-repo-builder")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error making tempdir: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the tests dir with test files.
 | 
			
		||||
	testsDir, err := filepath.Abs("tests")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error finding tests dir: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now date for app defines.
 | 
			
		||||
	now, _ := time.Parse(time.DateOnly, "2024-10-08")
 | 
			
		||||
 | 
			
		||||
	// Test adding a release of v0.1.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "add-release", "--notes", "This is a test.", "--release", filepath.Join(testsDir, "v0.1")}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx := app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test adding a release of v0.1.1.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "add-release", "--draft", "--release", filepath.Join(testsDir, "v0.1.1")}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test adding a release of v0.1.2.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "add-release", "--prerelease", "--published-at", "2024-10-05T22:15:21.731224367-05:00", "--release", filepath.Join(testsDir, "v0.1.2")}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm the latest release is v0.1.0.
 | 
			
		||||
	latestPath, err := os.Readlink(filepath.Join(dname, "latest"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error reading link to latest: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if latestPath != "v0.1.0" {
 | 
			
		||||
		t.Error("the latest link isn't correctly linked")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hash the manifest file without the published_at dates.
 | 
			
		||||
	hfun := md5.New()
 | 
			
		||||
	d, err := os.ReadFile(filepath.Join(dname, "manifest.yaml"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error reading manifest file: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hash the result and confirm.
 | 
			
		||||
	hfun.Write(d)
 | 
			
		||||
	sum := hfun.Sum(nil)
 | 
			
		||||
	hash := hex.EncodeToString(sum)
 | 
			
		||||
	if hash != "01240af1d189ea540418903e15eb3068" {
 | 
			
		||||
		t.Errorf("hash isn't valid for manifest file: %s", hash)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Binaries are not included.
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.0/example_linux_amd64/example")); !os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.0 binary exists, when it shouldn't exist.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm the asset was copied correctly.
 | 
			
		||||
	d, err = os.ReadFile(filepath.Join(dname, "v0.1.0/example_linux_amd64.tar.gz"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error reading test file: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	hfun.Reset()
 | 
			
		||||
	hfun.Write(d)
 | 
			
		||||
	sum = hfun.Sum(nil)
 | 
			
		||||
	hash = hex.EncodeToString(sum)
 | 
			
		||||
	if hash != "9cffcbe826ae684db1c8a08ff9216f34" {
 | 
			
		||||
		t.Errorf("hash isn't valid for test file: %s", hash)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm pruning of max releases works.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "prune", "--max-age=216h"}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm pruned state.
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.0/example_linux_amd64.tar.gz")); !os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.0 exists, when it shouldn't exist.")
 | 
			
		||||
	}
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.1/example_linux_amd64.tar.gz")); os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.1 does not exists, when it should.")
 | 
			
		||||
	}
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.2/example_linux_amd64.tar.gz")); os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.2 does not exists, when it should.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Delete all files, and reset.
 | 
			
		||||
	os.RemoveAll(dname)
 | 
			
		||||
	os.Mkdir(dname, 0755)
 | 
			
		||||
 | 
			
		||||
	// Test adding a release of v0.1.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "add-release", "--include-binary", "--release", filepath.Join(testsDir, "v0.1")}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test adding a release of v0.1.1.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "add-release", "--release", filepath.Join(testsDir, "v0.1.1")}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test adding a release of v0.1.2.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "add-release", "--published-at-now", "--release", filepath.Join(testsDir, "v0.1.2")}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm the latest release is v0.1.2.
 | 
			
		||||
	latestPath, err = os.Readlink(filepath.Join(dname, "latest"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error reading link to latest: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if latestPath != "v0.1.2" {
 | 
			
		||||
		t.Error("the latest link isn't correctly linked")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hash the manifest file without the published_at dates.
 | 
			
		||||
	hfun.Reset()
 | 
			
		||||
	d, err = os.ReadFile(filepath.Join(dname, "manifest.yaml"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error reading manifest file: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hash the result and confirm.
 | 
			
		||||
	hfun.Write(d)
 | 
			
		||||
	sum = hfun.Sum(nil)
 | 
			
		||||
	hash = hex.EncodeToString(sum)
 | 
			
		||||
	if hash != "dfac4ec2fc35bb04c8f5f79e057dfbe9" {
 | 
			
		||||
		t.Errorf("hash isn't valid for manifest file: %s", hash)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Binaries are not included.
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.0/example_linux_amd64/example")); os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.0 binary does not exists, when it shouldn.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm pruning of max releases works.
 | 
			
		||||
	os.Args = []string{"test", "--repo", dname, "prune", "--max-releases=1"}
 | 
			
		||||
	app = new(App)
 | 
			
		||||
	app.now = now
 | 
			
		||||
	ctx = app.ParseFlags()
 | 
			
		||||
 | 
			
		||||
	// Run the command.
 | 
			
		||||
	err = ctx.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error running the app: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Confirm pruned state.
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.0/example_linux_amd64.tar.gz")); !os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.0 exists, when it shouldn't exist.")
 | 
			
		||||
	}
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.1/example_linux_amd64.tar.gz")); !os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.1 exists, when it shouldn't exist.")
 | 
			
		||||
	}
 | 
			
		||||
	if _, serr := os.Stat(filepath.Join(dname, "v0.1.2/example_linux_amd64.tar.gz")); os.IsNotExist(serr) {
 | 
			
		||||
		t.Error("v0.1.2 does not exists, when it should.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Cleanup.
 | 
			
		||||
	os.RemoveAll(dname)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								manifest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								manifest.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/yaml.v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// An individual asset.
 | 
			
		||||
type HttpAsset struct {
 | 
			
		||||
	ID   int64  `yaml:"id"`
 | 
			
		||||
	Name string `yaml:"name"`
 | 
			
		||||
	Size int    `yaml:"size"`
 | 
			
		||||
	URL  string `yaml:"url"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An individual release.
 | 
			
		||||
type HttpRelease struct {
 | 
			
		||||
	ReleaseID    int64        `yaml:"release_id"`
 | 
			
		||||
	Name         string       `yaml:"name"`
 | 
			
		||||
	TagName      string       `yaml:"tag_name"`
 | 
			
		||||
	URL          string       `yaml:"url"`
 | 
			
		||||
	Draft        bool         `yaml:"draft"`
 | 
			
		||||
	Prerelease   bool         `yaml:"prerelease"`
 | 
			
		||||
	PublishedAt  time.Time    `yaml:"published_at"`
 | 
			
		||||
	ReleaseNotes string       `yaml:"release_notes"`
 | 
			
		||||
	Assets       []*HttpAsset `yaml:"assets"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The manifest file structure.
 | 
			
		||||
type HttpManifest struct {
 | 
			
		||||
	LastReleaseID int64          `yaml:"last_release_id"`
 | 
			
		||||
	LastAssetID   int64          `yaml:"last_asset_id"`
 | 
			
		||||
	Releases      []*HttpRelease `yaml:"releases"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read and parse manifest file.
 | 
			
		||||
func readManifestFile(manifestFile string) (*HttpManifest, error) {
 | 
			
		||||
	// We always want a manifest incase repo just needs to start from scratch.
 | 
			
		||||
	manifest := new(HttpManifest)
 | 
			
		||||
 | 
			
		||||
	// Read file, if error return the error.
 | 
			
		||||
	yamlFile, err := os.Open(manifestFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return manifest, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Attempt to decode the file.
 | 
			
		||||
	decoder := yaml.NewDecoder(yamlFile)
 | 
			
		||||
	err = decoder.Decode(manifest)
 | 
			
		||||
	yamlFile.Close()
 | 
			
		||||
 | 
			
		||||
	// Return the manifest and if any error occurred.
 | 
			
		||||
	return manifest, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write manifest file.
 | 
			
		||||
func writeManifestFile(manifestFile string, manifest *HttpManifest) error {
 | 
			
		||||
	// Open the file for write.
 | 
			
		||||
	yamlFile, err := os.OpenFile(manifestFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer yamlFile.Close()
 | 
			
		||||
 | 
			
		||||
	// Encode data.
 | 
			
		||||
	encoder := yaml.NewEncoder(yamlFile)
 | 
			
		||||
	err = encoder.Encode(manifest)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								prune_cmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								prune_cmd.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,125 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PruneCmd struct {
 | 
			
		||||
	MaxAge      time.Duration `help:"Delete releases older than."`
 | 
			
		||||
	MaxReleases int           `help:"Maximum number of releases to keep."`
 | 
			
		||||
	DryRun      bool          `help:"Just log the result without actually pruning."`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extra help to explain you can't set 2 prune stratages.
 | 
			
		||||
func (a *PruneCmd) Help() string {
 | 
			
		||||
	return "You cannot use both max-age and max-releases, only set one."
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Verify the options provided to the command.
 | 
			
		||||
func (a *PruneCmd) AfterApply() error {
 | 
			
		||||
	// If both stratages are defined, we don't allow that.
 | 
			
		||||
	if a.MaxAge > time.Duration(0) && a.MaxReleases > 0 {
 | 
			
		||||
		return errors.New("must only provide one prune argument")
 | 
			
		||||
	}
 | 
			
		||||
	// If no stratages are defined, we don't allow that.
 | 
			
		||||
	if a.MaxAge <= time.Duration(0) && a.MaxReleases <= 0 {
 | 
			
		||||
		return errors.New("must provide one prune argument")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Adds a release to a repo.
 | 
			
		||||
func (a *PruneCmd) Run() error {
 | 
			
		||||
	// Read existing manifest for repo.
 | 
			
		||||
	manifestFile := filepath.Join(app.flags.Repo, "manifest.yaml")
 | 
			
		||||
	manifest, err := readManifestFile(manifestFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Keep reference of number of pruned releases.
 | 
			
		||||
	releasesPruned := 0
 | 
			
		||||
	n := len(manifest.Releases)
 | 
			
		||||
 | 
			
		||||
	// If max releases defined and is less than number of releases, look for items to prune.
 | 
			
		||||
	if a.MaxReleases > 0 && n > a.MaxReleases {
 | 
			
		||||
		// Loop starting at max releases.
 | 
			
		||||
		for i := a.MaxReleases; i < n; i++ {
 | 
			
		||||
			// Get the current release.
 | 
			
		||||
			// We want pull from the top of the stack downward to keep newer releases.
 | 
			
		||||
			version := manifest.Releases[n-(i+1)].TagName
 | 
			
		||||
			log.Println("Removing release:", version)
 | 
			
		||||
 | 
			
		||||
			// If this isn't a dry run, remove the version directory.
 | 
			
		||||
			if !a.DryRun {
 | 
			
		||||
				err = os.RemoveAll(filepath.Join(app.flags.Repo, version))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return fmt.Errorf("untable to remove release files: %s", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Count the number pruned.
 | 
			
		||||
			releasesPruned++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Remove releases from the slice.
 | 
			
		||||
		manifest.Releases = manifest.Releases[n-a.MaxReleases:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we are pruning based on duration, do so.
 | 
			
		||||
	if a.MaxAge > time.Duration(0) {
 | 
			
		||||
		// Loop through the releases, and find old releases.
 | 
			
		||||
		for i := 0; i < n; i++ {
 | 
			
		||||
			// If n is 1, we removed too many entries and need to stop.
 | 
			
		||||
			if n == 1 {
 | 
			
		||||
				log.Println("The repo has only 1 release remaining, ending the prune here to keep 1 release.")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Get the current release, and confirm its age.
 | 
			
		||||
			release := manifest.Releases[i]
 | 
			
		||||
			if app.now.Sub(release.PublishedAt) >= a.MaxAge {
 | 
			
		||||
				// This release is too old, so we need to remove it.
 | 
			
		||||
				version := release.TagName
 | 
			
		||||
				log.Println("Removing release:", version)
 | 
			
		||||
 | 
			
		||||
				// If this isn't a dry run, remove the version files.
 | 
			
		||||
				if !a.DryRun {
 | 
			
		||||
					err = os.RemoveAll(filepath.Join(app.flags.Repo, version))
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return fmt.Errorf("untable to remove release files: %s", err)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Remove the release from the slice.
 | 
			
		||||
				manifest.Releases = append(manifest.Releases[:i], manifest.Releases[i+1:]...)
 | 
			
		||||
 | 
			
		||||
				// Back up one on the index and number of releases as one release was deleted.
 | 
			
		||||
				i--
 | 
			
		||||
				n--
 | 
			
		||||
 | 
			
		||||
				// Count number of releases pruned.
 | 
			
		||||
				releasesPruned++
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Write the manifest if this isn't a dry run.
 | 
			
		||||
	if !a.DryRun {
 | 
			
		||||
		err = writeManifestFile(manifestFile, manifest)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Provide details on what's been pruned.
 | 
			
		||||
	log.Println("Pruned", releasesPruned, "release from the repo.")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								tests/v0.1.1/artifacts.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								tests/v0.1.1/artifacts.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "name": "metadata.json",
 | 
			
		||||
        "path": "v0.1.1/metadata.json",
 | 
			
		||||
        "internal_type": 30,
 | 
			
		||||
        "type": "Metadata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "example_linux_amd64.tar.gz",
 | 
			
		||||
        "path": "v0.1.1/example_linux_amd64.tar.gz",
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64",
 | 
			
		||||
        "goamd64": "v1",
 | 
			
		||||
        "internal_type": 1,
 | 
			
		||||
        "type": "Archive",
 | 
			
		||||
        "extra": {
 | 
			
		||||
            "Binaries": [
 | 
			
		||||
                "example"
 | 
			
		||||
            ],
 | 
			
		||||
            "Checksum": "sha256:9208c58af1265438c6894499847355bd5e77f93d04b201393baf41297d4680a3",
 | 
			
		||||
            "Format": "tar.gz",
 | 
			
		||||
            "ID": "default",
 | 
			
		||||
            "Replaces": null,
 | 
			
		||||
            "WrappedIn": "example_linux_amd64"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "checksums.txt",
 | 
			
		||||
        "path": "v0.1.1/checksums.txt",
 | 
			
		||||
        "internal_type": 12,
 | 
			
		||||
        "type": "Checksum",
 | 
			
		||||
        "extra": {}
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/v0.1.1/checksums.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/v0.1.1/checksums.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
9208c58af1265438c6894499847355bd5e77f93d04b201393baf41297d4680a3  example_linux_amd64.tar.gz
 | 
			
		||||
							
								
								
									
										113
									
								
								tests/v0.1.1/config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								tests/v0.1.1/config.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
version: 2
 | 
			
		||||
project_name: example
 | 
			
		||||
release:
 | 
			
		||||
  github:
 | 
			
		||||
    owner: GRMrGecko
 | 
			
		||||
    name: goreleaser-http-repo-builder
 | 
			
		||||
  name_template: '{{.Tag}}'
 | 
			
		||||
builds:
 | 
			
		||||
  - id: example
 | 
			
		||||
    goos:
 | 
			
		||||
      - linux
 | 
			
		||||
    goarch:
 | 
			
		||||
      - amd64
 | 
			
		||||
      - arm64
 | 
			
		||||
    goarm:
 | 
			
		||||
      - "6"
 | 
			
		||||
    gomips:
 | 
			
		||||
      - hardfloat
 | 
			
		||||
    goamd64:
 | 
			
		||||
      - v1
 | 
			
		||||
    targets:
 | 
			
		||||
      - linux_amd64_v1
 | 
			
		||||
      - linux_arm64
 | 
			
		||||
    dir: .
 | 
			
		||||
    main: .
 | 
			
		||||
    binary: example
 | 
			
		||||
    builder: go
 | 
			
		||||
    gobinary: go
 | 
			
		||||
    command: build
 | 
			
		||||
    ldflags:
 | 
			
		||||
      - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser
 | 
			
		||||
    env:
 | 
			
		||||
      - CGO_ENABLED=0
 | 
			
		||||
archives:
 | 
			
		||||
  - id: default
 | 
			
		||||
    name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
 | 
			
		||||
    format: tar.gz
 | 
			
		||||
    wrap_in_directory: "true"
 | 
			
		||||
    files:
 | 
			
		||||
      - src: license*
 | 
			
		||||
      - src: LICENSE*
 | 
			
		||||
      - src: readme*
 | 
			
		||||
      - src: README*
 | 
			
		||||
      - src: changelog*
 | 
			
		||||
      - src: CHANGELOG*
 | 
			
		||||
snapshot:
 | 
			
		||||
  version_template: v0.1.0
 | 
			
		||||
checksum:
 | 
			
		||||
  name_template: checksums.txt
 | 
			
		||||
  algorithm: sha256
 | 
			
		||||
changelog:
 | 
			
		||||
  format: '{{ .SHA }}: {{ .Message }} ({{ with .AuthorUsername }}@{{ . }}{{ else }}{{ .AuthorName }} <{{ .AuthorEmail }}>{{ end }})'
 | 
			
		||||
dist: dist
 | 
			
		||||
env_files:
 | 
			
		||||
  github_token: ~/.config/goreleaser/github_token
 | 
			
		||||
  gitlab_token: ~/.config/goreleaser/gitlab_token
 | 
			
		||||
  gitea_token: ~/.config/goreleaser/gitea_token
 | 
			
		||||
before:
 | 
			
		||||
  hooks:
 | 
			
		||||
    - go mod tidy
 | 
			
		||||
source:
 | 
			
		||||
  name_template: '{{ .ProjectName }}-{{ .Version }}'
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
gomod:
 | 
			
		||||
  gobinary: go
 | 
			
		||||
announce:
 | 
			
		||||
  twitter:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
  mastodon:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    server: ""
 | 
			
		||||
  reddit:
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    url_template: '{{ .ReleaseURL }}'
 | 
			
		||||
  slack:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    username: GoReleaser
 | 
			
		||||
  discord:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    author: GoReleaser
 | 
			
		||||
    color: "3888754"
 | 
			
		||||
    icon_url: https://goreleaser.com/static/avatar.png
 | 
			
		||||
  teams:
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    color: '#2D313E'
 | 
			
		||||
    icon_url: https://goreleaser.com/static/avatar.png
 | 
			
		||||
  smtp:
 | 
			
		||||
    subject_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    body_template: 'You can view details from: {{ .ReleaseURL }}'
 | 
			
		||||
  mattermost:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    username: GoReleaser
 | 
			
		||||
  linkedin:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
  telegram:
 | 
			
		||||
    message_template: '{{ mdv2escape .ProjectName }} {{ mdv2escape .Tag }} is out{{ mdv2escape "!" }} Check it out at {{ mdv2escape .ReleaseURL }}'
 | 
			
		||||
    parse_mode: MarkdownV2
 | 
			
		||||
  webhook:
 | 
			
		||||
    message_template: '{ "message": "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"}'
 | 
			
		||||
    content_type: application/json; charset=utf-8
 | 
			
		||||
  opencollective:
 | 
			
		||||
    title_template: '{{ .Tag }}'
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out!<br/>Check it out at <a href="{{ .ReleaseURL }}">{{ .ReleaseURL }}</a>'
 | 
			
		||||
  bluesky:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
git:
 | 
			
		||||
  tag_sort: -version:refname
 | 
			
		||||
github_urls:
 | 
			
		||||
  download: https://github.com
 | 
			
		||||
gitlab_urls:
 | 
			
		||||
  download: https://gitlab.com
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/v0.1.1/example_linux_amd64.tar.gz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/v0.1.1/example_linux_amd64.tar.gz
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
 | 
			
		||||
ŽíõÞ}k© Î<>Iƒ`É<>cú|ÀGéNÙãðFñttV—hÉtÍËËõ€ÕØÙŸ¥øF
 | 
			
		||||
							
								
								
									
										12
									
								
								tests/v0.1.1/metadata.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/v0.1.1/metadata.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "project_name": "example",
 | 
			
		||||
    "tag": "v0.0.0",
 | 
			
		||||
    "previous_tag": "",
 | 
			
		||||
    "version": "v0.1.1",
 | 
			
		||||
    "commit": "521be63afb85d785ce36b4bd0d7412664593ac1d",
 | 
			
		||||
    "date": "2024-09-30T09:26:01.612178185-05:00",
 | 
			
		||||
    "runtime": {
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								tests/v0.1.2/artifacts.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								tests/v0.1.2/artifacts.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "name": "metadata.json",
 | 
			
		||||
        "path": "v0.1.2/metadata.json",
 | 
			
		||||
        "internal_type": 30,
 | 
			
		||||
        "type": "Metadata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "example_linux_amd64.tar.gz",
 | 
			
		||||
        "path": "v0.1.2/example_linux_amd64.tar.gz",
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64",
 | 
			
		||||
        "goamd64": "v1",
 | 
			
		||||
        "internal_type": 1,
 | 
			
		||||
        "type": "Archive",
 | 
			
		||||
        "extra": {
 | 
			
		||||
            "Binaries": [
 | 
			
		||||
                "example"
 | 
			
		||||
            ],
 | 
			
		||||
            "Checksum": "sha256:9208c58af1265438c6894499847355bd5e77f93d04b201393baf41297d4680a3",
 | 
			
		||||
            "Format": "tar.gz",
 | 
			
		||||
            "ID": "default",
 | 
			
		||||
            "Replaces": null,
 | 
			
		||||
            "WrappedIn": "example_linux_amd64"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "checksums.txt",
 | 
			
		||||
        "path": "v0.1.2/checksums.txt",
 | 
			
		||||
        "internal_type": 12,
 | 
			
		||||
        "type": "Checksum",
 | 
			
		||||
        "extra": {}
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/v0.1.2/checksums.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/v0.1.2/checksums.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
9208c58af1265438c6894499847355bd5e77f93d04b201393baf41297d4680a3  example_linux_amd64.tar.gz
 | 
			
		||||
							
								
								
									
										113
									
								
								tests/v0.1.2/config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								tests/v0.1.2/config.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
version: 2
 | 
			
		||||
project_name: example
 | 
			
		||||
release:
 | 
			
		||||
  github:
 | 
			
		||||
    owner: GRMrGecko
 | 
			
		||||
    name: goreleaser-http-repo-builder
 | 
			
		||||
  name_template: '{{.Tag}}'
 | 
			
		||||
builds:
 | 
			
		||||
  - id: example
 | 
			
		||||
    goos:
 | 
			
		||||
      - linux
 | 
			
		||||
    goarch:
 | 
			
		||||
      - amd64
 | 
			
		||||
      - arm64
 | 
			
		||||
    goarm:
 | 
			
		||||
      - "6"
 | 
			
		||||
    gomips:
 | 
			
		||||
      - hardfloat
 | 
			
		||||
    goamd64:
 | 
			
		||||
      - v1
 | 
			
		||||
    targets:
 | 
			
		||||
      - linux_amd64_v1
 | 
			
		||||
      - linux_arm64
 | 
			
		||||
    dir: .
 | 
			
		||||
    main: .
 | 
			
		||||
    binary: example
 | 
			
		||||
    builder: go
 | 
			
		||||
    gobinary: go
 | 
			
		||||
    command: build
 | 
			
		||||
    ldflags:
 | 
			
		||||
      - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser
 | 
			
		||||
    env:
 | 
			
		||||
      - CGO_ENABLED=0
 | 
			
		||||
archives:
 | 
			
		||||
  - id: default
 | 
			
		||||
    name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
 | 
			
		||||
    format: tar.gz
 | 
			
		||||
    wrap_in_directory: "true"
 | 
			
		||||
    files:
 | 
			
		||||
      - src: license*
 | 
			
		||||
      - src: LICENSE*
 | 
			
		||||
      - src: readme*
 | 
			
		||||
      - src: README*
 | 
			
		||||
      - src: changelog*
 | 
			
		||||
      - src: CHANGELOG*
 | 
			
		||||
snapshot:
 | 
			
		||||
  version_template: v0.1.0
 | 
			
		||||
checksum:
 | 
			
		||||
  name_template: checksums.txt
 | 
			
		||||
  algorithm: sha256
 | 
			
		||||
changelog:
 | 
			
		||||
  format: '{{ .SHA }}: {{ .Message }} ({{ with .AuthorUsername }}@{{ . }}{{ else }}{{ .AuthorName }} <{{ .AuthorEmail }}>{{ end }})'
 | 
			
		||||
dist: dist
 | 
			
		||||
env_files:
 | 
			
		||||
  github_token: ~/.config/goreleaser/github_token
 | 
			
		||||
  gitlab_token: ~/.config/goreleaser/gitlab_token
 | 
			
		||||
  gitea_token: ~/.config/goreleaser/gitea_token
 | 
			
		||||
before:
 | 
			
		||||
  hooks:
 | 
			
		||||
    - go mod tidy
 | 
			
		||||
source:
 | 
			
		||||
  name_template: '{{ .ProjectName }}-{{ .Version }}'
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
gomod:
 | 
			
		||||
  gobinary: go
 | 
			
		||||
announce:
 | 
			
		||||
  twitter:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
  mastodon:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    server: ""
 | 
			
		||||
  reddit:
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    url_template: '{{ .ReleaseURL }}'
 | 
			
		||||
  slack:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    username: GoReleaser
 | 
			
		||||
  discord:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    author: GoReleaser
 | 
			
		||||
    color: "3888754"
 | 
			
		||||
    icon_url: https://goreleaser.com/static/avatar.png
 | 
			
		||||
  teams:
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    color: '#2D313E'
 | 
			
		||||
    icon_url: https://goreleaser.com/static/avatar.png
 | 
			
		||||
  smtp:
 | 
			
		||||
    subject_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    body_template: 'You can view details from: {{ .ReleaseURL }}'
 | 
			
		||||
  mattermost:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    username: GoReleaser
 | 
			
		||||
  linkedin:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
  telegram:
 | 
			
		||||
    message_template: '{{ mdv2escape .ProjectName }} {{ mdv2escape .Tag }} is out{{ mdv2escape "!" }} Check it out at {{ mdv2escape .ReleaseURL }}'
 | 
			
		||||
    parse_mode: MarkdownV2
 | 
			
		||||
  webhook:
 | 
			
		||||
    message_template: '{ "message": "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"}'
 | 
			
		||||
    content_type: application/json; charset=utf-8
 | 
			
		||||
  opencollective:
 | 
			
		||||
    title_template: '{{ .Tag }}'
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out!<br/>Check it out at <a href="{{ .ReleaseURL }}">{{ .ReleaseURL }}</a>'
 | 
			
		||||
  bluesky:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
git:
 | 
			
		||||
  tag_sort: -version:refname
 | 
			
		||||
github_urls:
 | 
			
		||||
  download: https://github.com
 | 
			
		||||
gitlab_urls:
 | 
			
		||||
  download: https://gitlab.com
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/v0.1.2/example_linux_amd64.tar.gz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/v0.1.2/example_linux_amd64.tar.gz
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
 | 
			
		||||
ŽíõÞ}k© Î<>Iƒ`É<>cú|ÀGéNÙãðFñttV—hÉtÍËËõ€ÕØÙŸ¥øF
 | 
			
		||||
							
								
								
									
										12
									
								
								tests/v0.1.2/metadata.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/v0.1.2/metadata.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "project_name": "example",
 | 
			
		||||
    "tag": "v0.0.0",
 | 
			
		||||
    "previous_tag": "",
 | 
			
		||||
    "version": "v0.1.2",
 | 
			
		||||
    "commit": "94bb85eb32ea07f33d627ce0dee905e29d8d1c96",
 | 
			
		||||
    "date": "2024-10-07T22:15:21.731224367-05:00",
 | 
			
		||||
    "runtime": {
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								tests/v0.1/artifacts.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tests/v0.1/artifacts.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "name": "metadata.json",
 | 
			
		||||
        "path": "v0.1/metadata.json",
 | 
			
		||||
        "internal_type": 30,
 | 
			
		||||
        "type": "Metadata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "example",
 | 
			
		||||
        "path": "v0.1/example_linux_amd64/example",
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64",
 | 
			
		||||
        "internal_type": 4,
 | 
			
		||||
        "type": "Binary",
 | 
			
		||||
        "extra": {
 | 
			
		||||
            "Binary": "example",
 | 
			
		||||
            "Ext": "",
 | 
			
		||||
            "ID": "example"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "example_linux_amd64.tar.gz",
 | 
			
		||||
        "path": "v0.1/example_linux_amd64.tar.gz",
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64",
 | 
			
		||||
        "goamd64": "v1",
 | 
			
		||||
        "internal_type": 1,
 | 
			
		||||
        "type": "Archive",
 | 
			
		||||
        "extra": {
 | 
			
		||||
            "Binaries": [
 | 
			
		||||
                "example"
 | 
			
		||||
            ],
 | 
			
		||||
            "Checksum": "sha256:9208c58af1265438c6894499847355bd5e77f93d04b201393baf41297d4680a3",
 | 
			
		||||
            "Format": "tar.gz",
 | 
			
		||||
            "ID": "default",
 | 
			
		||||
            "Replaces": null,
 | 
			
		||||
            "WrappedIn": "example_linux_amd64"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "name": "checksums.txt",
 | 
			
		||||
        "path": "v0.1/checksums.txt",
 | 
			
		||||
        "internal_type": 12,
 | 
			
		||||
        "type": "Checksum",
 | 
			
		||||
        "extra": {}
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/v0.1/checksums.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/v0.1/checksums.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
9208c58af1265438c6894499847355bd5e77f93d04b201393baf41297d4680a3  example_linux_amd64.tar.gz
 | 
			
		||||
							
								
								
									
										113
									
								
								tests/v0.1/config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								tests/v0.1/config.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
version: 2
 | 
			
		||||
project_name: example
 | 
			
		||||
release:
 | 
			
		||||
  github:
 | 
			
		||||
    owner: GRMrGecko
 | 
			
		||||
    name: goreleaser-http-repo-builder
 | 
			
		||||
  name_template: '{{.Tag}}'
 | 
			
		||||
builds:
 | 
			
		||||
  - id: example
 | 
			
		||||
    goos:
 | 
			
		||||
      - linux
 | 
			
		||||
    goarch:
 | 
			
		||||
      - amd64
 | 
			
		||||
      - arm64
 | 
			
		||||
    goarm:
 | 
			
		||||
      - "6"
 | 
			
		||||
    gomips:
 | 
			
		||||
      - hardfloat
 | 
			
		||||
    goamd64:
 | 
			
		||||
      - v1
 | 
			
		||||
    targets:
 | 
			
		||||
      - linux_amd64_v1
 | 
			
		||||
      - linux_arm64
 | 
			
		||||
    dir: .
 | 
			
		||||
    main: .
 | 
			
		||||
    binary: example
 | 
			
		||||
    builder: go
 | 
			
		||||
    gobinary: go
 | 
			
		||||
    command: build
 | 
			
		||||
    ldflags:
 | 
			
		||||
      - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser
 | 
			
		||||
    env:
 | 
			
		||||
      - CGO_ENABLED=0
 | 
			
		||||
archives:
 | 
			
		||||
  - id: default
 | 
			
		||||
    name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
 | 
			
		||||
    format: tar.gz
 | 
			
		||||
    wrap_in_directory: "true"
 | 
			
		||||
    files:
 | 
			
		||||
      - src: license*
 | 
			
		||||
      - src: LICENSE*
 | 
			
		||||
      - src: readme*
 | 
			
		||||
      - src: README*
 | 
			
		||||
      - src: changelog*
 | 
			
		||||
      - src: CHANGELOG*
 | 
			
		||||
snapshot:
 | 
			
		||||
  version_template: v0.1.0
 | 
			
		||||
checksum:
 | 
			
		||||
  name_template: checksums.txt
 | 
			
		||||
  algorithm: sha256
 | 
			
		||||
changelog:
 | 
			
		||||
  format: '{{ .SHA }}: {{ .Message }} ({{ with .AuthorUsername }}@{{ . }}{{ else }}{{ .AuthorName }} <{{ .AuthorEmail }}>{{ end }})'
 | 
			
		||||
dist: dist
 | 
			
		||||
env_files:
 | 
			
		||||
  github_token: ~/.config/goreleaser/github_token
 | 
			
		||||
  gitlab_token: ~/.config/goreleaser/gitlab_token
 | 
			
		||||
  gitea_token: ~/.config/goreleaser/gitea_token
 | 
			
		||||
before:
 | 
			
		||||
  hooks:
 | 
			
		||||
    - go mod tidy
 | 
			
		||||
source:
 | 
			
		||||
  name_template: '{{ .ProjectName }}-{{ .Version }}'
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
gomod:
 | 
			
		||||
  gobinary: go
 | 
			
		||||
announce:
 | 
			
		||||
  twitter:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
  mastodon:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    server: ""
 | 
			
		||||
  reddit:
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    url_template: '{{ .ReleaseURL }}'
 | 
			
		||||
  slack:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    username: GoReleaser
 | 
			
		||||
  discord:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    author: GoReleaser
 | 
			
		||||
    color: "3888754"
 | 
			
		||||
    icon_url: https://goreleaser.com/static/avatar.png
 | 
			
		||||
  teams:
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    color: '#2D313E'
 | 
			
		||||
    icon_url: https://goreleaser.com/static/avatar.png
 | 
			
		||||
  smtp:
 | 
			
		||||
    subject_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    body_template: 'You can view details from: {{ .ReleaseURL }}'
 | 
			
		||||
  mattermost:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
    title_template: '{{ .ProjectName }} {{ .Tag }} is out!'
 | 
			
		||||
    username: GoReleaser
 | 
			
		||||
  linkedin:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
  telegram:
 | 
			
		||||
    message_template: '{{ mdv2escape .ProjectName }} {{ mdv2escape .Tag }} is out{{ mdv2escape "!" }} Check it out at {{ mdv2escape .ReleaseURL }}'
 | 
			
		||||
    parse_mode: MarkdownV2
 | 
			
		||||
  webhook:
 | 
			
		||||
    message_template: '{ "message": "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"}'
 | 
			
		||||
    content_type: application/json; charset=utf-8
 | 
			
		||||
  opencollective:
 | 
			
		||||
    title_template: '{{ .Tag }}'
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out!<br/>Check it out at <a href="{{ .ReleaseURL }}">{{ .ReleaseURL }}</a>'
 | 
			
		||||
  bluesky:
 | 
			
		||||
    message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}'
 | 
			
		||||
git:
 | 
			
		||||
  tag_sort: -version:refname
 | 
			
		||||
github_urls:
 | 
			
		||||
  download: https://github.com
 | 
			
		||||
gitlab_urls:
 | 
			
		||||
  download: https://gitlab.com
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/v0.1/example_linux_amd64.tar.gz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/v0.1/example_linux_amd64.tar.gz
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
 | 
			
		||||
ŽíõÞ}k© Î<>Iƒ`É<>cú|ÀGéNÙãðFñttV—hÉtÍËËõ€ÕØÙŸ¥øF
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/v0.1/example_linux_amd64/example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/v0.1/example_linux_amd64/example
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
 | 
			
		||||
ŽíõÞ}k© Î<>Iƒ`É<>cú|ÀGéNÙãðFñttV—hÉtÍËËõ€ÕØÙŸ¥øF
 | 
			
		||||
							
								
								
									
										12
									
								
								tests/v0.1/metadata.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/v0.1/metadata.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "project_name": "example",
 | 
			
		||||
    "tag": "v0.0.0",
 | 
			
		||||
    "previous_tag": "",
 | 
			
		||||
    "version": "v0.1.0",
 | 
			
		||||
    "commit": "1869b4455d572d3e3c7e00114586b47f184a2e35",
 | 
			
		||||
    "date": "2024-09-27T12:10:11.314135185-05:00",
 | 
			
		||||
    "runtime": {
 | 
			
		||||
        "goos": "linux",
 | 
			
		||||
        "goarch": "amd64"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								util.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Helper for CLI to ask for confirmation.
 | 
			
		||||
func askForConfirmation(message string) bool {
 | 
			
		||||
	// Read stdanrd input for each new line.
 | 
			
		||||
	scanner := bufio.NewScanner(os.Stdin)
 | 
			
		||||
 | 
			
		||||
	// Loop the question until answered.
 | 
			
		||||
	for {
 | 
			
		||||
		fmt.Printf("%s [y/n]: ", message)
 | 
			
		||||
 | 
			
		||||
		// Get next line.
 | 
			
		||||
		scanner.Scan()
 | 
			
		||||
		resp := strings.ToLower(strings.TrimSpace(scanner.Text()))
 | 
			
		||||
 | 
			
		||||
		// Check if yes or no.
 | 
			
		||||
		switch resp {
 | 
			
		||||
		case "y", "yes":
 | 
			
		||||
			return true
 | 
			
		||||
		case "n", "no":
 | 
			
		||||
			return false
 | 
			
		||||
		default:
 | 
			
		||||
			fmt.Println("Invalid answer.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper for copying files.
 | 
			
		||||
func copyFile(srcFile, dstFile string) (err error) {
 | 
			
		||||
	// Open the source file.
 | 
			
		||||
	f, err := os.Open(srcFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	// Open the destination file.
 | 
			
		||||
	d, err := os.Create(dstFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer d.Close()
 | 
			
		||||
 | 
			
		||||
	// Copy the data to the new file.
 | 
			
		||||
	_, err = io.Copy(d, f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure new file is fully written.
 | 
			
		||||
	err = d.Sync()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user