Skip to content

Commit

Permalink
Adds initial release automator (#633)
Browse files Browse the repository at this point in the history
* Adds initial release automator

Signed-off-by: Chuck Ha <[email protected]>

* Update docs/releasing.md
  • Loading branch information
chuckha authored and k8s-ci-robot committed Mar 8, 2019
1 parent 9f5518b commit 1235f41
Show file tree
Hide file tree
Showing 3 changed files with 304 additions and 8 deletions.
14 changes: 14 additions & 0 deletions cmd/release/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "sigs.k8s.io/cluster-api-provider-aws/cmd/release",
visibility = ["//visibility:private"],
)

go_binary(
name = "release",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
274 changes: 274 additions & 0 deletions cmd/release/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/*
Copyright 2019 The Kubernetes Authors.
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 main

import (
"flag"
"fmt"
"os"
"os/exec"
"path"
)

/*
required parameters
https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line
github token for modifying release
# dependencies
* requires a build environment (make artifact-release must work)
* github.com/itchio/gothub
*/

const (
// TODO(chuckha): figure this out based on directory name
repository = "cluster-api-provider-aws"
)

func main() {
var remote, user, version string

fs := flag.NewFlagSet("main", flag.ExitOnError)
fs.Usage = documentation
fs.StringVar(&remote, "remote", "origin", "name of the remote in local git")
fs.StringVar(&user, "user", "kubernetes-sigs", "the github user/organization of the repo to release a version to")

// TODO(chuckha): it would be ideal if we could release major/minor/patch and have it
// automatically bump the latest tag git finds
// until then, hard code it
fs.StringVar(&version, "version", "", "the version number, for example v0.2.0")
if err := fs.Parse(os.Args[1:]); err != nil {
fmt.Println(err)
os.Exit(1)
}
required("version", version)

checkDependencies()

cfg := config{
version: version,
artifactDir: "out",
artifacts: []string{
"cluster-api-provider-aws-examples.tar",
"clusterawsadm-darwin-amd64",
"clusterawsadm-linux-amd64",
"clusterctl-darwin-amd64",
"clusterctl-linux-amd64",
},
}

run := &runner{
releaser: gothubReleaser{
artifactsDir: cfg.artifactDir,
user: user,
repository: repository,
},
tagger: git{
repository: repository,
remote: remote,
},
config: cfg,
}

if err := run.run(); err != nil {
fmt.Println(err)
os.Exit(1)
}

fmt.Println("The next steps are:")
fmt.Println()
// TODO(chuckha): automate writing the release notes
fmt.Println("- Write the release notes. It is a manual process")
// TODO(chuckha): something something docker container
fmt.Println("- push the container images")
// TODO(chuckha): send an email or at least print out the contents of an
// email to
fmt.Println("- Email [email protected] to announce that a release happened")
}

func required(arg, val string) {
if val == "" {
fmt.Printf("%v is a required parameter\n", arg)
os.Exit(1)
}
}

func checkDependencies() {
_, err := exec.LookPath("gothub")
if err != nil {
fmt.Println("Please install gothub:")
fmt.Println()
fmt.Println(" go get github.com/itchio/gothub")
os.Exit(1)
}

if ght := os.Getenv("GITHUB_TOKEN"); len(ght) == 0 {
fmt.Println("Please set the GITHUB_TOKEN environment variable.")
fmt.Println("Read this guide for more information on creating Personal Access Tokens")
fmt.Println()
fmt.Println("https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line")
os.Exit(1)
}
}

func documentation() {
fmt.Println(`
This tool is designed to help automate the release process.
Usage:
release [-remote <remote>] [-user <user>] -version <version>
Options:
-remote
The local name of the remote repository.
The default is origin.
-user
The github username or organization of the repository to publish a release to.
The default is kubernetes-sigs.
-version
The name of the version to release.
Examples:
1.
origin is github.com/kubernetes-sigs/cluster-api-provider-aws
local master points to origin/master branch
To release:
./release -remote origin -version v1.1.1
2.
origin is github.com/myuser/cluster-api-provider-aws
upstream is github.com/kubernetes-sigs/cluster-api-provider-aws
local master points to upstream/master branch
To release:
./release -remote upstream -version v1.1.1
3.
To test release
./release -remote YOUR_FORK -user YOUR_GITHUB_USER_NAME -version v1.1.1`)
}

type config struct {
version string
artifacts []string
artifactDir string
}

type runner struct {
releaser releaser
tagger tagger
config config
}

func (r runner) run() error {
fmt.Print("tagging repository ")
if err := r.tagger.tag(r.config.version); err != nil {
return err
}
fmt.Println("🐲")
fmt.Print("pushing tag ")
if err := r.tagger.pushTag(r.config.version); err != nil {
return err
}
fmt.Println("🐲")
fmt.Print("drafting a release ")
if err := r.releaser.draft(r.config.version); err != nil {
return err
}
fmt.Println("🐲")
for _, artifact := range r.config.artifacts {
fmt.Printf("uploading %q ", artifact)
if err := r.releaser.upload(r.config.version, artifact); err != nil {
return err
}
fmt.Println("🐲")
}
return nil
}

type releaser interface {
draft(string) error
upload(string, string) error
}

type gothubReleaser struct {
// github user or organization (kubernetes-sigs, heptiolabs, chuckha, etc)
user string
// repository is the name of the repository
repository string

artifactsDir string
}

func (g gothubReleaser) draft(version string) error {
cmd := exec.Command("gothub", "release", "--tag", version, "--user", g.user, "--repo", g.repository, "--draft")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
}
return err
}
func (g gothubReleaser) upload(version, file string) error {
cmd := exec.Command("gothub", "upload", "--tag", version, "--user", g.user, "--repo", g.repository, "--file", path.Join(g.artifactsDir, file), "--name", file)
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
}
return err
}

type tagger interface {
tag(string) error
pushTag(string) error
}

type git struct {
// repository is the name of the repository
repository string

// remote is the local name of the remote
remote string
}

func (g git) tag(version string) error {
cmd := exec.Command("git", "tag", "-s", "-m", fmt.Sprintf("A release of %q for version %q", g.repository, version), version)
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
}
return err
}

func (g git) pushTag(version string) error {
// TODO(chuckha): this shouldn't exit if it fails because the tag already
cmd := exec.Command("git", "push", g.remote, version)
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
}
return err
}
24 changes: 16 additions & 8 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
# Release process

## Semi-automatic

1. make the release artifacts `make release-artifacts`
2. Run the release tool found in `cmd/release`
3. Pick up the manual steps starting with step 6

## Manual

1. Create a draft release in github
3. Tag the repository and push the tag `git tag -s $VERSION `
4. Run `make release-artifacts`
5. Attach the tarball to the drafted release
6. Attach `clusterawsadm` and `clusterctl` to the drafted release (for darwin
2. Tag the repository and push the tag `git tag -s $VERSION `
3. Run `make release-artifacts`
4. Attach the tarball to the drafted release
5. Attach `clusterawsadm` and `clusterctl` to the drafted release (for darwin
and linux architectures)
7. Write the release notes (see note below on release notes)
8. Get someone with permission to copy the container image from your own
6. Write the release notes (see note below on release notes)
7. Get someone with permission to copy the container image from your own
personal gcr registry to the production one, or have them build and push the
container image themselves.
9. Publish release
10. Email `[email protected]` to announce the release
8. Publish release
9. Email `[email protected]` to announce the release

## Expected artifacts

Expand Down

0 comments on commit 1235f41

Please sign in to comment.