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…
Reference in New Issue
Block a user