mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
c98348416c
commit
45e1084ff2
13 changed files with 447 additions and 1192 deletions
|
@ -1,51 +1,114 @@
|
|||
defaults: &defaults
|
||||
docker:
|
||||
- image: bepsays/ci-goreleaser:1.21900.20000
|
||||
environment:
|
||||
CGO_ENABLED: "0"
|
||||
parameters:
|
||||
|
||||
# v2: 11m.
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.21900.20000
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
prepare_release:
|
||||
<<: *defaults
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
steps:
|
||||
- &remote-docker
|
||||
setup_remote_docker:
|
||||
version: 20.10.14
|
||||
- checkout:
|
||||
path: hugo
|
||||
- run:
|
||||
- &git-config
|
||||
run:
|
||||
command: |
|
||||
git clone git@github.com:gohugoio/hugoDocs.git
|
||||
cd hugo
|
||||
go mod download
|
||||
sleep 5
|
||||
go mod verify
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths: .
|
||||
release:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /root/project
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
|
||||
git config --global user.name "hugoreleaser"
|
||||
go run -tags release main.go release -r ${CIRCLE_BRANCH}
|
||||
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
go mod download
|
||||
go run -tags release main.go release --step 1
|
||||
- save_cache:
|
||||
key: git-sha-{{ .Revision }}
|
||||
paths:
|
||||
- hugo
|
||||
- gomodcache
|
||||
build_container1:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
steps:
|
||||
- &restore-cache
|
||||
restore_cache:
|
||||
key: git-sha-{{ .Revision }}
|
||||
- run:
|
||||
no_output_timeout: 20m
|
||||
command: |
|
||||
mkdir -p /tmp/files/dist1
|
||||
cd hugo
|
||||
hugoreleaser build -paths "builds/container1/**" -workers 3 -dist /tmp/files/dist1 -chunks $CIRCLE_NODE_TOTAL -chunk-index $CIRCLE_NODE_INDEX
|
||||
- &persist-workspace
|
||||
persist_to_workspace:
|
||||
root: /tmp/files
|
||||
paths:
|
||||
- dist1
|
||||
- dist2
|
||||
parallelism: 7
|
||||
build_container2:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.21900.20000
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: |
|
||||
mkdir -p /tmp/files/dist2
|
||||
cd hugo
|
||||
hugoreleaser build -paths "builds/container2/**" -workers 1 -dist /tmp/files/dist2
|
||||
- *persist-workspace
|
||||
archive_and_release:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
steps:
|
||||
- *restore-cache
|
||||
- *attach-workspace
|
||||
- *git-config
|
||||
- run:
|
||||
command: |
|
||||
cp -a /tmp/workspace/dist1/. ./hugo/dist
|
||||
cp -a /tmp/workspace/dist2/. ./hugo/dist
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
hugoreleaser archive
|
||||
hugoreleaser release
|
||||
go run -tags release main.go release --step 2
|
||||
workflows:
|
||||
version: 2
|
||||
release:
|
||||
jobs:
|
||||
- build:
|
||||
- prepare_release:
|
||||
filters:
|
||||
branches:
|
||||
only: /release-.*/
|
||||
- hold:
|
||||
type: approval
|
||||
- build_container1:
|
||||
requires:
|
||||
- build
|
||||
- release:
|
||||
- prepare_release
|
||||
- build_container2:
|
||||
requires:
|
||||
- prepare_release
|
||||
- archive_and_release:
|
||||
context: org-global
|
||||
requires:
|
||||
- hold
|
||||
- build_container1
|
||||
- build_container2
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/releaser"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -29,9 +27,8 @@ var _ cmder = (*releaseCommandeer)(nil)
|
|||
type releaseCommandeer struct {
|
||||
cmd *cobra.Command
|
||||
|
||||
version string
|
||||
|
||||
skipPublish bool
|
||||
step int
|
||||
skipPush bool
|
||||
try bool
|
||||
}
|
||||
|
||||
|
@ -50,9 +47,9 @@ func createReleaser() cmder {
|
|||
return r.release()
|
||||
}
|
||||
|
||||
r.cmd.PersistentFlags().StringVarP(&r.version, "rel", "r", "", "new release version, i.e. 0.25.1")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.skipPublish, "skip-publish", "", false, "skip all publishing pipes of the release")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "simulate a release, i.e. no changes")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.skipPush, "skip-push", "", false, "skip pushing to remote")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "no changes")
|
||||
r.cmd.PersistentFlags().IntVarP(&r.step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)")
|
||||
|
||||
return r
|
||||
}
|
||||
|
@ -65,8 +62,10 @@ func (c *releaseCommandeer) flagsToConfig(cfg config.Provider) {
|
|||
}
|
||||
|
||||
func (r *releaseCommandeer) release() error {
|
||||
if r.version == "" {
|
||||
return errors.New("must set the --rel flag to the relevant version number")
|
||||
rel, err := releaser.New(r.skipPush, r.try, r.step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return releaser.New(r.version, r.skipPublish, r.try).Run()
|
||||
|
||||
return rel.Run()
|
||||
}
|
||||
|
|
214
goreleaser.yml
214
goreleaser.yml
|
@ -1,214 +0,0 @@
|
|||
project_name: hugo
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GOPROXY=https://proxy.golang.org
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo_unix
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
goos:
|
||||
- freebsd
|
||||
- netbsd
|
||||
- openbsd
|
||||
- dragonfly
|
||||
goarch:
|
||||
- amd64
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo_extended_windows
|
||||
ldflags:
|
||||
- -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
- "-extldflags '-static'"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
- CXX=x86_64-w64-mingw32-g++
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- binary: hugo
|
||||
id: hugo_extended_darwin
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- binary: hugo
|
||||
id: hugo_extended_linux
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
hooks:
|
||||
post: ./goreleaser-hook-post-linux.sh
|
||||
release:
|
||||
draft: true
|
||||
|
||||
archives:
|
||||
-
|
||||
id: "hugo"
|
||||
builds: ['hugo', 'hugo_unix', 'hugo_fat_darwin']
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
-
|
||||
id: "hugo_extended"
|
||||
builds: ['hugo_extended_windows', 'hugo_extended_linux', 'hugo_extended_fat_darwin']
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "{{.ProjectName}}_extended_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
universal_binaries:
|
||||
-
|
||||
id: hugo_fat_darwin
|
||||
|
||||
ids:
|
||||
- hugo
|
||||
|
||||
replace: true
|
||||
|
||||
-
|
||||
id: hugo_extended_fat_darwin
|
||||
|
||||
ids:
|
||||
- hugo_extended_darwin
|
||||
|
||||
replace: true
|
||||
|
||||
nfpms:
|
||||
-
|
||||
id: "hugo"
|
||||
builds: ['hugo']
|
||||
formats:
|
||||
- deb
|
||||
vendor: "gohugo.io"
|
||||
homepage: "https://gohugo.io/"
|
||||
maintainer: "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description: "A Fast and Flexible Static Site Generator built with love in GoLang."
|
||||
license: "Apache-2.0"
|
||||
file_name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
-
|
||||
id: "hugo_extended"
|
||||
builds: ['hugo_extended_linux']
|
||||
formats:
|
||||
- deb
|
||||
vendor: "gohugo.io"
|
||||
homepage: "https://gohugo.io/"
|
||||
maintainer: "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description: "A Fast and Flexible Static Site Generator built with love in GoLang."
|
||||
license: "Apache-2.0"
|
||||
file_name_template: "{{.ProjectName}}_extended_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
6
hugoreleaser.env
Normal file
6
hugoreleaser.env
Normal file
|
@ -0,0 +1,6 @@
|
|||
HUGO_RELEASE_NAME=New release setup
|
||||
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=xxx
|
||||
HUGORELEASER_COMMITISH=xxx
|
232
hugoreleaser.toml
Normal file
232
hugoreleaser.toml
Normal file
|
@ -0,0 +1,232 @@
|
|||
project = "hugo"
|
||||
|
||||
[go_settings]
|
||||
go_proxy = "https://proxy.golang.org"
|
||||
go_exe = "go"
|
||||
|
||||
[build_settings]
|
||||
binary = "hugo"
|
||||
flags = ["-buildmode", "exe"]
|
||||
env = ["CGO_ENABLED=0"]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"
|
||||
|
||||
[archive_settings]
|
||||
name_template = "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
extra_files = [
|
||||
{ source_path = "README.md", target_path = "README.md" },
|
||||
{ source_path = "LICENSE", target_path = "LICENSE" },
|
||||
]
|
||||
[archive_settings.type]
|
||||
format = "tar.gz"
|
||||
extension = ".tar.gz"
|
||||
[archive_settings.replacements]
|
||||
amd64 = "64bit"
|
||||
386 = "32bit"
|
||||
arm = "ARM"
|
||||
arm64 = "ARM64"
|
||||
darwin = "macOS"
|
||||
linux = "Linux"
|
||||
windows = "Windows"
|
||||
openbsd = "OpenBSD"
|
||||
netbsd = "NetBSD"
|
||||
freebsd = "FreeBSD"
|
||||
dragonfly = "DragonFlyBSD"
|
||||
|
||||
[release_settings]
|
||||
name = "${HUGO_RELEASE_NAME}"
|
||||
type = "github"
|
||||
repository = "hugo"
|
||||
repository_owner = "gohugoio"
|
||||
draft = true
|
||||
prerelease = false
|
||||
|
||||
[release_settings.release_notes_settings]
|
||||
# Use Hugoreleaser's autogenerated release notes.
|
||||
generate = true
|
||||
|
||||
# Collapse relases with < 10 changes below one title.
|
||||
short_threshold = 10
|
||||
short_title = "What's Changed"
|
||||
|
||||
groups = [
|
||||
# Group the changes in the release notes by title.
|
||||
# You need at least one.
|
||||
# The groups will be tested in order until a match is found.
|
||||
# The titles will so be listed in the given order in the release note.
|
||||
# Any match with ignore=true title will be dropped.
|
||||
{ regexp = "Merge commit|Squashed", ignore = true },
|
||||
{ title = "Bug fixes", regexp = "fix", ordinal = 10 },
|
||||
{ title = "Dependency Updates", regexp = "deps", ordinal = 30 },
|
||||
{ title = "Build Setup", regexp = "(snap|release|update to)", ordinal = 40 },
|
||||
{ title = "Documentation", regexp = "(doc|readme)", ordinal = 40 },
|
||||
{ title = "Improvements", regexp = ".*", ordinal = 20 },
|
||||
]
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/regular"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm"
|
||||
[builds.os.archs.build_settings]
|
||||
env = ["CGO_ENABLED=0", "GOARM=7"]
|
||||
|
||||
# Unix BSD variants
|
||||
[[builds.os]]
|
||||
goos = "dragonfly"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "freebsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "netbsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "openbsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
env = ["CGO_ENABLED=1"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[builds.os.build_settings]
|
||||
env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"]
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container2/linux/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[builds.os.build_settings]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=aarch64-linux-gnu-gcc",
|
||||
"CXX=aarch64-linux-gnu-g++",
|
||||
]
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/regular"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=x86_64-w64-mingw32-gcc",
|
||||
"CXX=x86_64-w64-mingw32-g++",
|
||||
]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/regular/**"]
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
# Only extended builds in container2.
|
||||
paths = ["builds/container2/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/regular/**"]
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/regular/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
[archives.archive_settings.type]
|
||||
format = "_plugin"
|
||||
extension = ".deb"
|
||||
[archives.archive_settings.plugin]
|
||||
id = "deb"
|
||||
type = "gorun"
|
||||
command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@v0.5.0"
|
||||
[archives.archive_settings.custom_settings]
|
||||
vendor = "gohugo.io"
|
||||
homepage = "https://github.com/gohugoio/hugoreleaser"
|
||||
maintainer = "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description = "Build, archive and release Go programs."
|
||||
license = "Apache-2.0"
|
||||
[[archives]]
|
||||
paths = ["builds/**/extended/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "_plugin"
|
||||
extension = ".deb"
|
||||
[archives.archive_settings.plugin]
|
||||
id = "deb"
|
||||
type = "gorun"
|
||||
command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest"
|
||||
[archives.archive_settings.custom_settings]
|
||||
vendor = "gohugo.io"
|
||||
homepage = "https://github.com/gohugoio/hugoreleaser"
|
||||
maintainer = "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description = "Build, archive and release Go programs."
|
||||
license = "Apache-2.0"
|
||||
|
||||
[[releases]]
|
||||
paths = ["archives/**"]
|
||||
path = "r1"
|
||||
|
||||
# The above should allow the following build commands:
|
||||
# hugoreleaser build -paths "builds/container1/**"
|
||||
# hugoreleaser build -paths "builds/container2/**"
|
||||
# hugoreleaser archive
|
||||
# hugoreleaser release
|
253
releaser/git.go
253
releaser/git.go
|
@ -1,253 +0,0 @@
|
|||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
)
|
||||
|
||||
var issueRe = regexp.MustCompile(`(?i)(?:Updates?|Closes?|Fix.*|See) #(\d+)`)
|
||||
|
||||
type changeLog struct {
|
||||
Version string
|
||||
Notes gitInfos
|
||||
All gitInfos
|
||||
Docs gitInfos
|
||||
|
||||
// Overall stats
|
||||
Repo *gitHubRepo
|
||||
ContributorCount int
|
||||
ThemeCount int
|
||||
}
|
||||
|
||||
func newChangeLog(infos, docInfos gitInfos) *changeLog {
|
||||
log := &changeLog{
|
||||
Docs: docInfos,
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
// TODO(bep) improve
|
||||
if regexp.MustCompile("(?i)deprecate|note").MatchString(info.Subject) {
|
||||
log.Notes = append(log.Notes, info)
|
||||
}
|
||||
|
||||
log.All = append(log.All, info)
|
||||
info.Subject = strings.TrimSpace(info.Subject)
|
||||
|
||||
}
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
type gitInfo struct {
|
||||
Hash string
|
||||
Author string
|
||||
Subject string
|
||||
Body string
|
||||
|
||||
GitHubCommit *gitHubCommit
|
||||
}
|
||||
|
||||
func (g gitInfo) Issues() []int {
|
||||
return extractIssues(g.Body)
|
||||
}
|
||||
|
||||
func (g gitInfo) AuthorID() string {
|
||||
if g.GitHubCommit != nil {
|
||||
return g.GitHubCommit.Author.Login
|
||||
}
|
||||
return g.Author
|
||||
}
|
||||
|
||||
func extractIssues(body string) []int {
|
||||
var i []int
|
||||
m := issueRe.FindAllStringSubmatch(body, -1)
|
||||
for _, mm := range m {
|
||||
issueID, err := strconv.Atoi(mm[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
i = append(i, issueID)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
type gitInfos []gitInfo
|
||||
|
||||
func git(args ...string) (string, error) {
|
||||
cmd, _ := hexec.SafeCommand("git", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func getGitInfos(tag, repo, repoPath string, remote bool) (gitInfos, error) {
|
||||
return getGitInfosBefore("HEAD", tag, repo, repoPath, remote)
|
||||
}
|
||||
|
||||
type countribCount struct {
|
||||
Author string
|
||||
GitHubAuthor gitHubAuthor
|
||||
Count int
|
||||
}
|
||||
|
||||
func (c countribCount) AuthorLink() string {
|
||||
if c.GitHubAuthor.HTMLURL != "" {
|
||||
return fmt.Sprintf("[@%s](%s)", c.GitHubAuthor.Login, c.GitHubAuthor.HTMLURL)
|
||||
}
|
||||
|
||||
if !strings.Contains(c.Author, "@") {
|
||||
return c.Author
|
||||
}
|
||||
|
||||
return c.Author[:strings.Index(c.Author, "@")]
|
||||
}
|
||||
|
||||
type contribCounts []countribCount
|
||||
|
||||
func (c contribCounts) Less(i, j int) bool { return c[i].Count > c[j].Count }
|
||||
func (c contribCounts) Len() int { return len(c) }
|
||||
func (c contribCounts) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
|
||||
func (g gitInfos) ContribCountPerAuthor() contribCounts {
|
||||
var c contribCounts
|
||||
|
||||
counters := make(map[string]countribCount)
|
||||
|
||||
for _, gi := range g {
|
||||
authorID := gi.AuthorID()
|
||||
if count, ok := counters[authorID]; ok {
|
||||
count.Count = count.Count + 1
|
||||
counters[authorID] = count
|
||||
} else {
|
||||
var ghA gitHubAuthor
|
||||
if gi.GitHubCommit != nil {
|
||||
ghA = gi.GitHubCommit.Author
|
||||
}
|
||||
authorCount := countribCount{Count: 1, Author: gi.Author, GitHubAuthor: ghA}
|
||||
counters[authorID] = authorCount
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range counters {
|
||||
c = append(c, v)
|
||||
}
|
||||
|
||||
sort.Sort(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func getGitInfosBefore(ref, tag, repo, repoPath string, remote bool) (gitInfos, error) {
|
||||
client := newGitHubAPI(repo)
|
||||
var g gitInfos
|
||||
|
||||
log, err := gitLogBefore(ref, tag, repoPath)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
|
||||
log = strings.Trim(log, "\n\x1e'")
|
||||
entries := strings.Split(log, "\x1e")
|
||||
|
||||
for _, entry := range entries {
|
||||
items := strings.Split(entry, "\x1f")
|
||||
gi := gitInfo{}
|
||||
|
||||
if len(items) > 0 {
|
||||
gi.Hash = items[0]
|
||||
}
|
||||
if len(items) > 1 {
|
||||
gi.Author = items[1]
|
||||
}
|
||||
if len(items) > 2 {
|
||||
gi.Subject = items[2]
|
||||
}
|
||||
if len(items) > 3 {
|
||||
gi.Body = items[3]
|
||||
}
|
||||
|
||||
if remote && gi.Hash != "" {
|
||||
gc, err := client.fetchCommit(gi.Hash)
|
||||
if err == nil {
|
||||
gi.GitHubCommit = &gc
|
||||
}
|
||||
}
|
||||
g = append(g, gi)
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// Ignore autogenerated commits etc. in change log. This is a regexp.
|
||||
const ignoredCommits = "snapcraft:|Merge commit|Squashed"
|
||||
|
||||
func gitLogBefore(ref, tag, repoPath string) (string, error) {
|
||||
var prevTag string
|
||||
var err error
|
||||
if tag != "" {
|
||||
prevTag = tag
|
||||
} else {
|
||||
prevTag, err = gitVersionTagBefore(ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
defaultArgs := []string{"log", "-E", fmt.Sprintf("--grep=%s", ignoredCommits), "--invert-grep", "--pretty=format:%x1e%h%x1f%aE%x1f%s%x1f%b", "--abbrev-commit", prevTag + ".." + ref}
|
||||
|
||||
var args []string
|
||||
|
||||
if repoPath != "" {
|
||||
args = append([]string{"-C", repoPath}, defaultArgs...)
|
||||
} else {
|
||||
args = defaultArgs
|
||||
}
|
||||
|
||||
log, err := git(args...)
|
||||
if err != nil {
|
||||
return ",", err
|
||||
}
|
||||
|
||||
return log, err
|
||||
}
|
||||
|
||||
func gitVersionTagBefore(ref string) (string, error) {
|
||||
return gitShort("describe", "--tags", "--abbrev=0", "--always", "--match", "v[0-9]*", ref+"^")
|
||||
}
|
||||
|
||||
func gitShort(args ...string) (output string, err error) {
|
||||
output, err = git(args...)
|
||||
return strings.Replace(strings.Split(output, "\n")[0], "'", "", -1), err
|
||||
}
|
||||
|
||||
func tagExists(tag string) (bool, error) {
|
||||
out, err := git("tag", "-l", tag)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if strings.Contains(out, tag) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestGitInfos(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
skipIfCI(t)
|
||||
infos, err := getGitInfos("v0.20", "hugo", "", false)
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(infos) > 0, qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestIssuesRe(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
body := `
|
||||
This is a commit message.
|
||||
|
||||
Updates #123
|
||||
Fix #345
|
||||
closes #543
|
||||
See #456
|
||||
`
|
||||
|
||||
issues := extractIssues(body)
|
||||
|
||||
c.Assert(len(issues), qt.Equals, 4)
|
||||
c.Assert(issues[0], qt.Equals, 123)
|
||||
c.Assert(issues[2], qt.Equals, 543)
|
||||
|
||||
bodyNoIssues := `
|
||||
This is a commit message without issue refs.
|
||||
|
||||
But it has e #10 to make old regexp confused.
|
||||
Streets #20.
|
||||
`
|
||||
|
||||
emptyIssuesList := extractIssues(bodyNoIssues)
|
||||
c.Assert(len(emptyIssuesList), qt.Equals, 0)
|
||||
}
|
||||
|
||||
func TestGitVersionTagBefore(t *testing.T) {
|
||||
skipIfCI(t)
|
||||
c := qt.New(t)
|
||||
v1, err := gitVersionTagBefore("v0.18")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(v1, qt.Equals, "v0.17")
|
||||
}
|
||||
|
||||
func TestTagExists(t *testing.T) {
|
||||
skipIfCI(t)
|
||||
c := qt.New(t)
|
||||
b1, err := tagExists("v0.18")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(b1, qt.Equals, true)
|
||||
|
||||
b2, err := tagExists("adfagdsfg")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(b2, qt.Equals, false)
|
||||
}
|
||||
|
||||
func skipIfCI(t *testing.T) {
|
||||
if isCI() {
|
||||
// Travis has an ancient git with no --invert-grep: https://github.com/travis-ci/travis-ci/issues/6328
|
||||
// Also Travis clones very shallowly, making some of the tests above shaky.
|
||||
t.Skip("Skip git test on Linux to make Travis happy.")
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
package releaser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
gitHubCommitsAPI = "https://api.github.com/repos/gohugoio/REPO/commits/%s"
|
||||
gitHubRepoAPI = "https://api.github.com/repos/gohugoio/REPO"
|
||||
gitHubContributorsAPI = "https://api.github.com/repos/gohugoio/REPO/contributors"
|
||||
)
|
||||
|
||||
type gitHubAPI struct {
|
||||
commitsAPITemplate string
|
||||
repoAPI string
|
||||
contributorsAPITemplate string
|
||||
}
|
||||
|
||||
func newGitHubAPI(repo string) *gitHubAPI {
|
||||
return &gitHubAPI{
|
||||
commitsAPITemplate: strings.Replace(gitHubCommitsAPI, "REPO", repo, -1),
|
||||
repoAPI: strings.Replace(gitHubRepoAPI, "REPO", repo, -1),
|
||||
contributorsAPITemplate: strings.Replace(gitHubContributorsAPI, "REPO", repo, -1),
|
||||
}
|
||||
}
|
||||
|
||||
type gitHubCommit struct {
|
||||
Author gitHubAuthor `json:"author"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
}
|
||||
|
||||
type gitHubAuthor struct {
|
||||
ID int `json:"id"`
|
||||
Login string `json:"login"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
type gitHubRepo struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Stars int `json:"stargazers_count"`
|
||||
Contributors []gitHubContributor
|
||||
}
|
||||
|
||||
type gitHubContributor struct {
|
||||
ID int `json:"id"`
|
||||
Login string `json:"login"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Contributions int `json:"contributions"`
|
||||
}
|
||||
|
||||
func (g *gitHubAPI) fetchCommit(ref string) (gitHubCommit, error) {
|
||||
var commit gitHubCommit
|
||||
|
||||
u := fmt.Sprintf(g.commitsAPITemplate, ref)
|
||||
|
||||
req, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return commit, err
|
||||
}
|
||||
|
||||
err = doGitHubRequest(req, &commit)
|
||||
|
||||
return commit, err
|
||||
}
|
||||
|
||||
func (g *gitHubAPI) fetchRepo() (gitHubRepo, error) {
|
||||
var repo gitHubRepo
|
||||
|
||||
req, err := http.NewRequest("GET", g.repoAPI, nil)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
err = doGitHubRequest(req, &repo)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
var contributors []gitHubContributor
|
||||
page := 0
|
||||
for {
|
||||
page++
|
||||
var currPage []gitHubContributor
|
||||
url := fmt.Sprintf(g.contributorsAPITemplate+"?page=%d", page)
|
||||
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
err = doGitHubRequest(req, &currPage)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
if len(currPage) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
contributors = append(contributors, currPage...)
|
||||
|
||||
}
|
||||
|
||||
repo.Contributors = contributors
|
||||
|
||||
return repo, err
|
||||
}
|
||||
|
||||
func doGitHubRequest(req *http.Request, v any) error {
|
||||
addGitHubToken(req)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if isError(resp) {
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
return fmt.Errorf("GitHub lookup failed: %s", string(b))
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(v)
|
||||
}
|
||||
|
||||
func isError(resp *http.Response) bool {
|
||||
return resp.StatusCode < 200 || resp.StatusCode > 299
|
||||
}
|
||||
|
||||
func addGitHubToken(req *http.Request) {
|
||||
gitHubToken := os.Getenv("GITHUB_TOKEN")
|
||||
if gitHubToken != "" {
|
||||
req.Header.Add("Authorization", "token "+gitHubToken)
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestGitHubLookupCommit(t *testing.T) {
|
||||
skipIfNoToken(t)
|
||||
c := qt.New(t)
|
||||
client := newGitHubAPI("hugo")
|
||||
commit, err := client.fetchCommit("793554108763c0984f1a1b1a6ee5744b560d78d0")
|
||||
c.Assert(err, qt.IsNil)
|
||||
fmt.Println(commit)
|
||||
}
|
||||
|
||||
func TestFetchRepo(t *testing.T) {
|
||||
skipIfNoToken(t)
|
||||
c := qt.New(t)
|
||||
client := newGitHubAPI("hugo")
|
||||
repo, err := client.fetchRepo()
|
||||
c.Assert(err, qt.IsNil)
|
||||
fmt.Println(">>", len(repo.Contributors))
|
||||
}
|
||||
|
||||
func skipIfNoToken(t *testing.T) {
|
||||
if os.Getenv("GITHUB_TOKEN") == "" {
|
||||
t.Skip("Skip test against GitHub as no GITHUB_TOKEN set.")
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package releaser implements a set of utilities and a wrapper around Goreleaser
|
||||
// to help automate the Hugo release process.
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
issueLinkTemplate = "#%d"
|
||||
linkTemplate = "[%s](%s)"
|
||||
releaseNotesMarkdownTemplatePatchRelease = `
|
||||
{{ if eq (len .All) 1 }}
|
||||
This is a bug-fix release with one important fix.
|
||||
{{ else }}
|
||||
This is a bug-fix release with a couple of important fixes.
|
||||
{{ end }}
|
||||
{{ range .All }}
|
||||
{{- if .GitHubCommit -}}
|
||||
* {{ .Subject }} {{ .Hash }} {{ . | author }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ else -}}
|
||||
* {{ .Subject }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ end -}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
`
|
||||
releaseNotesMarkdownTemplate = `
|
||||
{{- $contribsPerAuthor := .All.ContribCountPerAuthor -}}
|
||||
{{- $docsContribsPerAuthor := .Docs.ContribCountPerAuthor -}}
|
||||
|
||||
This release represents **{{ len .All }} contributions by {{ len $contribsPerAuthor }} contributors** to the main Hugo code base.
|
||||
|
||||
{{- if gt (len $contribsPerAuthor) 3 -}}
|
||||
{{- $u1 := index $contribsPerAuthor 0 -}}
|
||||
{{- $u2 := index $contribsPerAuthor 1 -}}
|
||||
{{- $u3 := index $contribsPerAuthor 2 -}}
|
||||
{{- $u4 := index $contribsPerAuthor 3 -}}
|
||||
{{- $u1.AuthorLink }} leads the Hugo development with a significant amount of contributions, but also a big shoutout to {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their ongoing contributions.
|
||||
And thanks to [@digitalcraftsman](https://github.com/digitalcraftsman) for his ongoing work on keeping the themes site in pristine condition.
|
||||
{{ end }}
|
||||
Many have also been busy writing and fixing the documentation in [hugoDocs](https://github.com/gohugoio/hugoDocs),
|
||||
which has received **{{ len .Docs }} contributions by {{ len $docsContribsPerAuthor }} contributors**.
|
||||
{{- if gt (len $docsContribsPerAuthor) 3 -}}
|
||||
{{- $u1 := index $docsContribsPerAuthor 0 -}}
|
||||
{{- $u2 := index $docsContribsPerAuthor 1 -}}
|
||||
{{- $u3 := index $docsContribsPerAuthor 2 -}}
|
||||
{{- $u4 := index $docsContribsPerAuthor 3 }} A special thanks to {{ $u1.AuthorLink }}, {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their work on the documentation site.
|
||||
{{ end }}
|
||||
|
||||
Hugo now has:
|
||||
|
||||
{{ with .Repo -}}
|
||||
* {{ .Stars }}+ [stars](https://github.com/gohugoio/hugo/stargazers)
|
||||
* {{ len .Contributors }}+ [contributors](https://github.com/gohugoio/hugo/graphs/contributors)
|
||||
{{- end -}}
|
||||
{{ with .ThemeCount }}
|
||||
* {{ . }}+ [themes](http://themes.gohugo.io/)
|
||||
{{ end }}
|
||||
{{ with .Notes }}
|
||||
## Notes
|
||||
{{ template "change-section" . }}
|
||||
{{- end -}}
|
||||
{{ with .All }}
|
||||
## Changes
|
||||
{{ template "change-section" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "change-section" }}
|
||||
{{ range . }}
|
||||
{{- if .GitHubCommit -}}
|
||||
* {{ .Subject }} {{ .Hash }} {{ . | author }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ else -}}
|
||||
* {{ .Subject }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ end -}}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
`
|
||||
)
|
||||
|
||||
var templateFuncs = template.FuncMap{
|
||||
"isPatch": func(c changeLog) bool {
|
||||
return !strings.HasSuffix(c.Version, "0")
|
||||
},
|
||||
"issue": func(id int) string {
|
||||
return fmt.Sprintf(issueLinkTemplate, id)
|
||||
},
|
||||
"commitURL": func(info gitInfo) string {
|
||||
if info.GitHubCommit.HTMLURL == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HTMLURL)
|
||||
},
|
||||
"author": func(info gitInfo) string {
|
||||
return "@" + info.GitHubCommit.Author.Login
|
||||
},
|
||||
}
|
||||
|
||||
func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error {
|
||||
client := newGitHubAPI("hugo")
|
||||
changes := newChangeLog(infosMain, infosDocs)
|
||||
changes.Version = version
|
||||
repo, err := client.fetchRepo()
|
||||
if err == nil {
|
||||
changes.Repo = &repo
|
||||
}
|
||||
themeCount, err := fetchThemeCount()
|
||||
if err == nil {
|
||||
changes.ThemeCount = themeCount
|
||||
}
|
||||
|
||||
mtempl := releaseNotesMarkdownTemplate
|
||||
|
||||
if !strings.HasSuffix(version, "0") {
|
||||
mtempl = releaseNotesMarkdownTemplatePatchRelease
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Funcs(templateFuncs).Parse(mtempl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tmpl.Execute(to, changes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchThemeCount() (int, error) {
|
||||
resp, err := http.Get("https://raw.githubusercontent.com/gohugoio/hugoThemesSiteBuilder/main/themes.txt")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
return bytes.Count(b, []byte("\n")) - bytes.Count(b, []byte("#")), nil
|
||||
}
|
||||
|
||||
func getReleaseNotesFilename(version string) string {
|
||||
return filepath.FromSlash(fmt.Sprintf("temp/%s-relnotes-ready.md", version))
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, isPatch bool, infosMain, infosDocs gitInfos) (string, error) {
|
||||
filename := getReleaseNotesFilename(version)
|
||||
|
||||
var w io.WriteCloser
|
||||
|
||||
if !r.try {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
w = f
|
||||
|
||||
} else {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
if err := writeReleaseNotes(version, infosMain, infosDocs, w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package commands defines and implements command-line commands and flags
|
||||
// used by Hugo. Commands and flags are implemented using Cobra.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func _TestReleaseNotesWriter(t *testing.T) {
|
||||
skipIfNoToken(t)
|
||||
if os.Getenv("CI") != "" {
|
||||
// Travis has an ancient git with no --invert-grep: https://github.com/travis-ci/travis-ci/issues/6328
|
||||
t.Skip("Skip git test on CI to make Travis happy..")
|
||||
}
|
||||
|
||||
c := qt.New(t)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
// TODO(bep) consider to query GitHub directly for the gitlog with author info, probably faster.
|
||||
infos, err := getGitInfosBefore("HEAD", "v0.89.0", "hugo", "", false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
c.Assert(writeReleaseNotes("0.89.0", infos, infos, &b), qt.IsNil)
|
||||
|
||||
fmt.Println(b.String())
|
||||
}
|
|
@ -17,7 +17,6 @@ package releaser
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -25,22 +24,56 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
)
|
||||
|
||||
const commitPrefix = "releaser:"
|
||||
|
||||
// New initialises a ReleaseHandler.
|
||||
func New(skipPush, try bool, step int) (*ReleaseHandler, error) {
|
||||
if step < 1 || step > 2 {
|
||||
return nil, fmt.Errorf("step must be 1 or 2")
|
||||
}
|
||||
|
||||
prefix := "release-"
|
||||
branch, err := git("rev-parse", "--abbrev-ref", "HEAD")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !strings.HasPrefix(branch, prefix) {
|
||||
return nil, fmt.Errorf("branch %q is not a release branch", branch)
|
||||
}
|
||||
|
||||
logf("Branch: %s\n", branch)
|
||||
|
||||
version := strings.TrimPrefix(branch, prefix)
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
rh := &ReleaseHandler{branchVersion: version, skipPush: skipPush, try: try, step: step}
|
||||
|
||||
if try {
|
||||
rh.git = func(args ...string) (string, error) {
|
||||
logln("git", strings.Join(args, " "))
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
rh.git = git
|
||||
}
|
||||
|
||||
return rh, nil
|
||||
}
|
||||
|
||||
// ReleaseHandler provides functionality to release a new version of Hugo.
|
||||
// Test this locally without doing an actual release:
|
||||
// go run -tags release main.go release --skip-publish --try -r 0.90.0
|
||||
// Or a variation of the above -- the skip-publish flag makes sure that any changes are performed to the local Git only.
|
||||
type ReleaseHandler struct {
|
||||
cliVersion string
|
||||
branchVersion string
|
||||
|
||||
skipPublish bool
|
||||
// 1 or 2.
|
||||
step int
|
||||
|
||||
// No remote pushes.
|
||||
skipPush bool
|
||||
|
||||
// Just simulate, no actual changes.
|
||||
try bool
|
||||
|
@ -48,111 +81,17 @@ type ReleaseHandler struct {
|
|||
git func(args ...string) (string, error)
|
||||
}
|
||||
|
||||
func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) {
|
||||
newVersion := hugo.MustParseVersion(r.cliVersion)
|
||||
finalVersion := newVersion.Next()
|
||||
finalVersion.PatchLevel = 0
|
||||
|
||||
if newVersion.Suffix != "-test" {
|
||||
newVersion.Suffix = ""
|
||||
}
|
||||
|
||||
finalVersion.Suffix = "-DEV"
|
||||
|
||||
return newVersion, finalVersion
|
||||
}
|
||||
|
||||
// New initialises a ReleaseHandler.
|
||||
func New(version string, skipPublish, try bool) *ReleaseHandler {
|
||||
// When triggered from CI release branch
|
||||
version = strings.TrimPrefix(version, "release-")
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
rh := &ReleaseHandler{cliVersion: version, skipPublish: skipPublish, try: try}
|
||||
|
||||
if try {
|
||||
rh.git = func(args ...string) (string, error) {
|
||||
fmt.Println("git", strings.Join(args, " "))
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
rh.git = git
|
||||
}
|
||||
|
||||
return rh
|
||||
}
|
||||
|
||||
// Run creates a new release.
|
||||
func (r *ReleaseHandler) Run() error {
|
||||
if os.Getenv("GITHUB_TOKEN") == "" {
|
||||
return errors.New("GITHUB_TOKEN not set, create one here with the repo scope selected: https://github.com/settings/tokens/new")
|
||||
}
|
||||
|
||||
fmt.Printf("Start release from %q\n", wd())
|
||||
|
||||
newVersion, finalVersion := r.calculateVersions()
|
||||
|
||||
version := newVersion.String()
|
||||
tag := "v" + version
|
||||
isPatch := newVersion.PatchLevel > 0
|
||||
mainVersion := newVersion
|
||||
mainVersion.PatchLevel = 0
|
||||
|
||||
// Exit early if tag already exists
|
||||
exists, err := tagExists(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
return fmt.Errorf("tag %q already exists", tag)
|
||||
}
|
||||
|
||||
var changeLogFromTag string
|
||||
|
||||
if newVersion.PatchLevel == 0 {
|
||||
// There may have been patch releases between, so set the tag explicitly.
|
||||
changeLogFromTag = "v" + newVersion.Prev().String()
|
||||
exists, _ := tagExists(changeLogFromTag)
|
||||
if !exists {
|
||||
// fall back to one that exists.
|
||||
changeLogFromTag = ""
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
gitCommits gitInfos
|
||||
gitCommitsDocs gitInfos
|
||||
)
|
||||
|
||||
defer r.gitPush() // TODO(bep)
|
||||
|
||||
gitCommits, err = getGitInfos(changeLogFromTag, "hugo", "", !r.try)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(bep) explicit tag?
|
||||
gitCommitsDocs, err = getGitInfos("", "hugoDocs", "../hugoDocs", !r.try)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
releaseNotesFile, err := r.writeReleaseNotesToTemp(version, isPatch, gitCommits, gitCommitsDocs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := r.git("add", releaseNotesFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commitMsg := fmt.Sprintf("%s Add release notes for %s", commitPrefix, newVersion)
|
||||
commitMsg += "\n[ci skip]"
|
||||
|
||||
if _, err := r.git("commit", "-m", commitMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.gitPush()
|
||||
|
||||
if r.step == 1 {
|
||||
if err := r.bumpVersions(newVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -161,31 +100,29 @@ func (r *ReleaseHandler) Run() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := r.git("tag", "-a", tag, "-m", fmt.Sprintf("%s %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil {
|
||||
// The above commit will be the target for this release, so print it to the console in a env friendly way.
|
||||
sha, err := git("rev-parse", "HEAD")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.skipPublish {
|
||||
if _, err := r.git("push", "origin", tag); err != nil {
|
||||
// Hugoreleaser will do the actual release using these values.
|
||||
if err := r.replaceInFile("hugoreleaser.env",
|
||||
`HUGORELEASER_TAG=(\S*)`, "HUGORELEASER_TAG="+tag,
|
||||
`HUGORELEASER_COMMITISH=(\S*)`, "HUGORELEASER_COMMITISH="+sha,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
logf("HUGORELEASER_TAG=%s\n", tag)
|
||||
logf("HUGORELEASER_COMMITISH=%s\n", sha)
|
||||
|
||||
if err := r.release(releaseNotesFile); err != nil {
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.bumpVersions(finalVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.try {
|
||||
// No longer needed.
|
||||
if err := os.Remove(releaseNotesFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Prepare repository for %s\n\n[ci skip]", commitPrefix, finalVersion)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -193,36 +130,6 @@ func (r *ReleaseHandler) Run() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) gitPush() {
|
||||
if r.skipPublish {
|
||||
return
|
||||
}
|
||||
if _, err := r.git("push", "origin", "HEAD"); err != nil {
|
||||
log.Fatal("push failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) release(releaseNotesFile string) error {
|
||||
if r.try {
|
||||
fmt.Println("Skip goreleaser...")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := []string{"--parallelism", "2", "--timeout", "120m", "--rm-dist", "--release-notes", releaseNotesFile}
|
||||
if r.skipPublish {
|
||||
args = append(args, "--skip-publish")
|
||||
}
|
||||
|
||||
cmd, _ := hexec.SafeCommand("goreleaser", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("goreleaser failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error {
|
||||
toDev := ""
|
||||
|
||||
|
@ -264,6 +171,29 @@ func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) {
|
||||
newVersion := hugo.MustParseVersion(r.branchVersion)
|
||||
finalVersion := newVersion.Next()
|
||||
finalVersion.PatchLevel = 0
|
||||
|
||||
if newVersion.Suffix != "-test" {
|
||||
newVersion.Suffix = ""
|
||||
}
|
||||
|
||||
finalVersion.Suffix = "-DEV"
|
||||
|
||||
return newVersion, finalVersion
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) gitPush() {
|
||||
if r.skipPush {
|
||||
return
|
||||
}
|
||||
if _, err := r.git("push", "origin", "HEAD"); err != nil {
|
||||
log.Fatal("push failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error {
|
||||
filename = filepath.FromSlash(filename)
|
||||
fi, err := os.Stat(filename)
|
||||
|
@ -272,11 +202,11 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error
|
|||
}
|
||||
|
||||
if r.try {
|
||||
fmt.Printf("Replace in %q: %q\n", filename, oldNew)
|
||||
logf("Replace in %q: %q\n", filename, oldNew)
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -287,18 +217,22 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error
|
|||
newContent = re.ReplaceAllString(newContent, oldNew[i+1])
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, []byte(newContent), fi.Mode())
|
||||
return os.WriteFile(filename, []byte(newContent), fi.Mode())
|
||||
}
|
||||
|
||||
func isCI() bool {
|
||||
return os.Getenv("CI") != ""
|
||||
}
|
||||
|
||||
func wd() string {
|
||||
p, err := os.Getwd()
|
||||
func git(args ...string) (string, error) {
|
||||
cmd, _ := hexec.SafeCommand("git", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)
|
||||
}
|
||||
return p
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func logf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
}
|
||||
|
||||
func logln(args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, args...)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue