build: process for automatic release tags (#10133)
Make the release tagging consistent. Push to release branch to create a stable release; push to release-rc to release a new candidate.
This commit is contained in:
@@ -173,7 +173,7 @@ jobs:
|
|||||||
|
|
||||||
codesign-windows:
|
codesign-windows:
|
||||||
name: Codesign for Windows
|
name: Codesign for Windows
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs:
|
needs:
|
||||||
@@ -280,7 +280,7 @@ jobs:
|
|||||||
|
|
||||||
package-macos:
|
package-macos:
|
||||||
name: Package for macOS
|
name: Package for macOS
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -380,7 +380,7 @@ jobs:
|
|||||||
|
|
||||||
notarize-macos:
|
notarize-macos:
|
||||||
name: Notarize for macOS
|
name: Notarize for macOS
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
needs:
|
needs:
|
||||||
- package-macos
|
- package-macos
|
||||||
@@ -524,7 +524,7 @@ jobs:
|
|||||||
|
|
||||||
sign-for-upgrade:
|
sign-for-upgrade:
|
||||||
name: Sign for upgrade
|
name: Sign for upgrade
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
needs:
|
needs:
|
||||||
- codesign-windows
|
- codesign-windows
|
||||||
@@ -723,6 +723,8 @@ jobs:
|
|||||||
name: Publish release files
|
name: Publish release files
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
needs:
|
needs:
|
||||||
- sign-for-upgrade
|
- sign-for-upgrade
|
||||||
- package-debian
|
- package-debian
|
||||||
@@ -782,13 +784,35 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
|
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
|
||||||
|
|
||||||
|
- name: Create GitHub release and push binaries
|
||||||
|
run: |
|
||||||
|
maybePrerelease=""
|
||||||
|
if [[ $VERSION == *-* ]]; then
|
||||||
|
maybePrerelease="--prerelease"
|
||||||
|
fi
|
||||||
|
export GH_PROMPT_DISABLED=1
|
||||||
|
if ! gh release view --json name "$VERSION" >/dev/null 2>&1 ; then
|
||||||
|
gh release create \
|
||||||
|
"$VERSION" \
|
||||||
|
$maybePrerelease \
|
||||||
|
--title "$VERSION" \
|
||||||
|
--notes-from-tag
|
||||||
|
fi
|
||||||
|
gh release upload "$VERSION" \
|
||||||
|
packages/*.asc packages/*.json \
|
||||||
|
packages/syncthing-*.tar.gz \
|
||||||
|
packages/syncthing-*.zip \
|
||||||
|
packages/syncthing*.deb
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Push Debian/APT archive
|
# Push Debian/APT archive
|
||||||
#
|
#
|
||||||
|
|
||||||
publish-apt:
|
publish-apt:
|
||||||
name: Publish APT
|
name: Publish APT
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
needs:
|
needs:
|
||||||
- package-debian
|
- package-debian
|
||||||
@@ -867,7 +891,7 @@ jobs:
|
|||||||
docker-syncthing:
|
docker-syncthing:
|
||||||
name: Build and push Docker images
|
name: Build and push Docker images
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release-nightly' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: docker
|
environment: docker
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
name: Release Syncthing
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- release
|
||||||
|
- release-rc*
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-release-tag:
|
||||||
|
name: Create release tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: stable
|
||||||
|
|
||||||
|
- name: Get svu
|
||||||
|
run: |
|
||||||
|
go install github.com/caarlos0/svu@latest
|
||||||
|
|
||||||
|
- name: Determine version to release
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_REF_NAME" == "release" ]] ; then
|
||||||
|
next=$(svu next)
|
||||||
|
else
|
||||||
|
next=$(svu prerelease --pre-release rc)
|
||||||
|
fi
|
||||||
|
echo "NEXT=$next" >> $GITHUB_ENV
|
||||||
|
echo "Next version is $next"
|
||||||
|
|
||||||
|
prev=$(git describe --exclude "*-*" --abbrev=0)
|
||||||
|
echo "PREV=$prev" >> $GITHUB_ENV
|
||||||
|
echo "Previous version is $prev"
|
||||||
|
|
||||||
|
- name: Determine release notes
|
||||||
|
run: |
|
||||||
|
go run ./script/relnotes.go --new-ver "$NEXT" --branch "$GITHUB_REF_NAME" --prev-ver "$PREV" > notes.md
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create and push tag
|
||||||
|
run: |
|
||||||
|
git config --global user.name 'Syncthing Release Automation'
|
||||||
|
git config --global user.email 'release@syncthing.net'
|
||||||
|
git tag -a -F notes.md "$NEXT"
|
||||||
|
git push origin "$NEXT"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Release Notes
|
||||||
|
|
||||||
|
Files in this directory constitute manual release notes for a given release.
|
||||||
|
When relevant, they should be created prior to that release so that they can
|
||||||
|
be included in the corresponding tag message, etc.
|
||||||
|
|
||||||
|
To add release notes for a release 1.2.3, create a file named `v1.2.3.md`
|
||||||
|
consisting of an initial H2-level header and further notes as desired. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Major changes in v1.2.3
|
||||||
|
|
||||||
|
- Files are now synchronized twice as fast on Tuesdays
|
||||||
|
```
|
||||||
|
|
||||||
|
The release notes will also be included in candidate releases (e.g.
|
||||||
|
v1.2.3-rc.1).
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (C) 2025 The Syncthing Authors.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmp"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
githubToken = os.Getenv("GITHUB_TOKEN")
|
||||||
|
githubRepo = cmp.Or(os.Getenv("GITHUB_REPOSITORY"), "syncthing/syncthing")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ver := flag.String("new-ver", "", "New version tag")
|
||||||
|
prevVer := flag.String("prev-ver", "", "Previous version tag")
|
||||||
|
branch := flag.String("branch", "HEAD", "Branch to release from")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
if *ver == "" {
|
||||||
|
log.Fatalln("Must set --new-ver")
|
||||||
|
}
|
||||||
|
if githubToken == "" {
|
||||||
|
log.Fatalln("Must set $GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
|
||||||
|
addl, err := additionalNotes(*ver)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Gathering additional notes:", err)
|
||||||
|
}
|
||||||
|
notes, err := generatedNotes(*ver, *branch, *prevVer)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Gathering github notes:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addl != "" {
|
||||||
|
fmt.Println(addl)
|
||||||
|
}
|
||||||
|
fmt.Println(notes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load potential additional release notes from within the repo
|
||||||
|
func additionalNotes(newVer string) (string, error) {
|
||||||
|
ver, _, _ := strings.Cut(newVer, "-")
|
||||||
|
bs, err := os.ReadFile(fmt.Sprintf("relnotes/%s.md", ver))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return string(bs), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load generated release notes (list of pull requests and contributors)
|
||||||
|
// from GitHub.
|
||||||
|
func generatedNotes(newVer, targetCommit, prevVer string) (string, error) {
|
||||||
|
fields := map[string]string{
|
||||||
|
"tag_name": newVer,
|
||||||
|
"target_commitish": targetCommit,
|
||||||
|
"previous_tag_name": prevVer,
|
||||||
|
}
|
||||||
|
bs, err := json.Marshal(fields)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, "https://api.github.com/repos/"+githubRepo+"/releases/generate-notes", bytes.NewReader(bs)) //nolint:noctx
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/vnd.github+json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+githubToken)
|
||||||
|
req.Header.Set("X-Github-Api-Version", "2022-11-28")
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
bs, _ := io.ReadAll(res.Body)
|
||||||
|
log.Print(string(bs))
|
||||||
|
return "", errors.New(res.Status) //nolint:err113
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
var resJSON struct {
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&resJSON); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return resJSON.Body, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user