1
This is a work in progress. Next steps:

1. Add instructions to the wiki.
2. Try the new tarball on a real repository.
3. Cut the actual release.

Test output for an upcoming `v1.0.2`:

    $ bazel run //tools/releaser -- -skip_upgrades=true -tag v1.0.2
    INFO: Analyzed target //tools/releaser:releaser (1 packages loaded, 29 targets configured).
    INFO: Found 1 target...
    Target //tools/releaser:releaser up-to-date:
      bazel-bin/tools/releaser/releaser_/releaser
    INFO: Elapsed time: 1.978s, Critical Path: 1.81s
    INFO: 3 processes: 1 internal, 2 linux-sandbox.
    INFO: Build completed successfully, 3 total actions
    INFO: Running command line: bazel-bin/tools/releaser/releaser_/releaser '-skip_upgrades=true' -tag v1.0.2
    Running pre-release checks:
    - SKIPPING: go update commands
    - gazelle
    - checking if repository is clean
    Creating tag v1.0.2
    Creating archive bazel-zig-cc-v1.0.2.tar
    Compressing bazel-zig-cc-v1.0.2.tar
    Written /code/bazel-zig-cc/bazel-zig-cc-v1.0.2.tar.gz
    Release boilerplate:
    -----
    load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

    http_archive(
        name = "bazel-zig-cc",
        sha256 = "b0e857f8b32062a112305931437c5a7e1762287e27379c6d2d7173f0fa74e270",
        urls = [
            "https://mirror.bazel.build/github.com/uber/bazel-zig-cc/releases/download/v1.0.2/bazel-zig-cc-v1.0.2.tar.gz",
            "https://github.com/uber/bazel-zig-cc/releases/download/v1.0.2/bazel-zig-cc-v1.0.2.tar.gz",
        ],
    )

    load("@bazel-zig-cc//toolchain:defs.bzl", zig_toolchains = "toolchains")

    # Argument-free will pick reasonable defaults.
    zig_toolchains()

    # version, url_formats and host_platform_sha256 are can be set for those who
    # wish to control their Zig SDK version and where it is downloaded from
    zig_toolchains(
        version = "<...>",
        url_formats = [
            "https://example.org/zig/zig-{host_platform}-{version}.{_ext}",
        ],
        host_platform_sha256 = { ... },
    )
This commit is contained in:
Motiejus Jakštys 2023-03-10 10:45:02 +02:00 committed by Motiejus Jakštys
parent 4a42b46a99
commit d3a2d43356
8 changed files with 292 additions and 1 deletions

1
BUILD
View File

@ -8,6 +8,7 @@ load("@bazel_gazelle//:def.bzl", "gazelle")
# gazelle:build_file_name BUILD
# gazelle:prefix github.com/uber/bazel-zig-cc
# gazelle:exclude tools.go
# gazelle:exclude tools/releaser/zopfli.go
gazelle(name = "gazelle")

View File

@ -82,3 +82,13 @@ http_archive(
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
ZOPFLI_COMMIT = "831773bc28e318b91a3255fa12c9fcde1606058b"
http_archive(
name = "zopfli",
build_file = "//:third_party/BUILD.zopfli",
sha256 = "dbc695ae30c815973dc38b39e2f95dc2b249263b7222427d96e5e785092b0f78",
strip_prefix = "zopfli-{}".format(ZOPFLI_COMMIT),
urls = ["https://github.com/google/zopfli/archive/{}.tar.gz".format(ZOPFLI_COMMIT)],
)

20
third_party/BUILD.zopfli vendored Normal file
View File

@ -0,0 +1,20 @@
# Copyright 2023 Uber Technologies, Inc.
# Licensed under the Apache License, Version 2.0
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_zopfli",
srcs = glob(
[
"go/zopfli/zopfli.go",
"src/zopfli/*.c",
"src/zopfli/*.h",
],
exclude = ["src/zopfli/zopfli_bin.c"],
),
cgo = True,
copts = ["-O2"],
importpath = "github.com/google/zopfli/go/zopfli",
visibility = ["//visibility:public"],
)

View File

@ -6,7 +6,7 @@ set -xeu
cd "$(git rev-parse --show-toplevel)"
echo "--- go mod tidy"
tools/bazel run @go_sdk//:bin/go -- mod tidy
tools/bazel run @go_sdk//:bin/go -- mod tidy "$@"
echo "--- gazelle-update-repos"
exec tools/bazel run //:gazelle-update-repos

28
tools/releaser/BUILD Normal file
View File

@ -0,0 +1,28 @@
# Copyright 2023 Uber Technologies, Inc.
# Licensed under the Apache License, Version 2.0
load("//rules:rules_go.bzl", "go_binary")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "releaser_lib",
srcs = [
"main.go",
"zopfli.go", # keep
],
importpath = "github.com/uber/bazel-zig-cc/tools/releaser",
visibility = ["//visibility:private"],
deps = ["@zopfli//:go_zopfli"], # keep
)
go_binary(
name = "releaser",
embed = [":releaser_lib"],
visibility = ["//visibility:public"],
)
go_test(
name = "releaser_test",
srcs = ["main_test.go"],
embed = [":releaser_lib"],
)

186
tools/releaser/main.go Normal file
View File

@ -0,0 +1,186 @@
// Copyright 2023 Uber Technologies, Inc.
// Licensed under the Apache License, Version 2.0
// releaser is a tool for managing part of the process to release a new version of bazel-zig-cc.
package main
import (
"crypto/sha256"
"errors"
"flag"
"fmt"
"os"
"os/exec"
"path"
"regexp"
"strconv"
"strings"
)
var (
// Paths to be included to the release
_paths = []string{
"LICENSE",
"NOTICE",
"README.md",
"toolchain/*",
}
// regexp for valid tags
tagRegexp = regexp.MustCompile(`^v([0-9]+)\\.([0-9]+)(\\.([0-9]+))(-rc([0-9]+))?$`)
errTag = errors.New("tag accepts the following formats: v1.0.0 v1.0.1-rc1")
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func log(msg string, format ...any) {
fmt.Fprintf(flag.CommandLine.Output(), msg+"\n", format...)
}
func run() error {
var (
goVersion string
repoRoot string
skipUpgrades bool
tag string
)
flag.StringVar(&goVersion, "go_version", "", "go version for go.mod")
flag.StringVar(&repoRoot, "repo_root", os.Getenv("BUILD_WORKSPACE_DIRECTORY"), "root directory of bazel-zig-cc repo")
flag.StringVar(&tag, "tag", "", "tag for this release")
flag.BoolVar(&skipUpgrades, "skip_upgrades", false, "skip upgrade checks (testing only)")
flag.Usage = func() {
fmt.Fprint(flag.CommandLine.Output(), `usage: bazel run //tools/releaser -- -go_version <version> -tag <tag>
This utility is intended to handle many of the steps to release a new version.
`)
flag.PrintDefaults()
}
flag.Parse()
if tag == "" {
return fmt.Errorf("ERROR: tag is required")
}
if !tagRegexp.MatchString(tag) {
return errTag
}
var goVersionArgs []string
if goVersion != "" {
versionParts := strings.Split(goVersion, ".")
if len(versionParts) < 2 {
flag.Usage()
return errors.New("please provide a valid Go version")
}
if minorVersion, err := strconv.Atoi(versionParts[1]); err != nil {
return fmt.Errorf("%q is not a valid Go version", goVersion)
} else if minorVersion > 0 {
versionParts[1] = strconv.Itoa(minorVersion - 1)
}
goVersionArgs = append(goVersionArgs, "-go", goVersion, "-compat", strings.Join(versionParts, "."))
}
// external dependency checks
depChecks := [][]string{
{"go", "get", "-t", "-u", "./..."},
append([]string{"tools/mod-tidy"}, goVersionArgs...),
}
// commands that Must Not Fail
cmds := [][]string{
{"tools/bazel", "run", "//:gazelle"},
{"git", "diff", "--stat", "--exit-code"},
{"git", "tag", tag},
}
log("Cutting a release:")
if skipUpgrades {
log("SKIPPING: go update commands")
} else {
cmds = append(depChecks, cmds...)
}
for _, c := range cmds {
cmd := exec.Command(c[0], c[1:]...)
cmd.Dir = repoRoot
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf(
"ERROR: running %s:%w\n%s",
strings.Join(c, " "),
err,
out,
)
}
}
log("Creating archive bazel-zig-cc-%s.tar", tag)
cmd := exec.Command(
"git",
append([]string{"archive", "--format=tar", tag}, _paths...)...,
)
cmd.Dir = repoRoot
out, err := cmd.Output()
if err != nil {
var exitError *exec.ExitError
errors.As(err, &exitError)
return fmt.Errorf("ERROR: failed to create git archive: %w\n%s", err, exitError.Stderr)
}
log("Compressing bazel-zig-cc-%s.tar", tag)
tgz := Gzip(out)
fpath := path.Join(repoRoot, fmt.Sprintf("bazel-zig-cc-%s.tar.gz", tag))
if err := os.WriteFile(fpath, tgz, 0o644); err != nil {
return fmt.Errorf("ERROR: write %q: %w", fpath, err)
}
log("Wrote %s", fpath)
shasum := sha256.Sum256(tgz)
log("Release boilerplate:\n-----\n" + genBoilerplate(tag, fmt.Sprintf("%x", shasum)))
return nil
}
func genBoilerplate(version, shasum string) string {
return fmt.Sprintf(`load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "bazel-zig-cc",
sha256 = "%[2]s",
urls = [
"https://mirror.bazel.build/github.com/uber/bazel-zig-cc/releases/download/%[1]s/bazel-zig-cc-%[1]s.tar.gz",
"https://github.com/uber/bazel-zig-cc/releases/download/%[1]s/bazel-zig-cc-%[1]s.tar.gz",
],
)
load("@bazel-zig-cc//toolchain:defs.bzl", zig_toolchains = "toolchains")
# Argument-free will pick reasonable defaults.
zig_toolchains()
# version, url_formats and host_platform_sha256 are can be set for those who
# wish to control their Zig SDK version and where it is downloaded from
zig_toolchains(
version = "<...>",
url_formats = [
"https://example.org/zig/zig-{host_platform}-{version}.{_ext}",
],
host_platform_sha256 = { ... },
)`, version, shasum)
}

View File

@ -0,0 +1,36 @@
// Copyright 2023 Uber Technologies, Inc.
// Licensed under the Apache License, Version 2.0
package main
import (
"fmt"
"testing"
)
func TestRegex(t *testing.T) {
tests := []struct {
tag string
good bool
}{
{good: true, tag: "v1.0.0"},
{good: true, tag: "v99.99.99"},
{good: true, tag: "v1.0.1-rc1"},
{good: true, tag: "v1.0.99-rc99"},
{good: false, tag: ""},
{good: false, tag: "v1.0"},
{good: false, tag: "1.0.0"},
{good: false, tag: "1.0.99-rc99"},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("tag=%s good=%s", tt.tag, tt.good), func(t *testing.T) {
matched := tagRegexp.MatchString(tt.tag)
if tt.good && !matched {
t.Errorf("expected %s to be a valid tag, but it was not", tt.tag)
} else if !tt.good && matched {
t.Errorf("expected %s to be an invalida tag, but it was", tt.tag)
}
})
}
}

10
tools/releaser/zopfli.go Normal file
View File

@ -0,0 +1,10 @@
package main
import "github.com/google/zopfli/go/zopfli"
// Gzip compresses a byte array with zopfli.
//
// We use a separate file, because we can make Gazelle ignore it.
func Gzip(in []byte) []byte {
return zopfli.Gzip(in)
}