Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds initial release automator #633

Merged
merged 2 commits into from
Mar 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
detiber marked this conversation as resolved.
Show resolved Hide resolved
*/

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`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. make the release artifacts `make release artifacts`
1. make the release artifacts `make clean release-artifacts`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the benefit of adding clean? release-artifacts are a PHONY target and will always overwrite whatever exists.

Clean will simply clean up the dev environment a user may already have

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean will delete existing examples prior to running release-artifacts target. This will ensure that the release will have newly generated examples in it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh.. I see. the release-artifactsdoesn't call other make targets. so clean not required.

chuckha marked this conversation as resolved.
Show resolved Hide resolved
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`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
3. Run `make release-artifacts`
3. Run `make clean 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