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

chore: automate release process #966

Merged
merged 3 commits into from
Jul 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
93 changes: 85 additions & 8 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ version: 2.1
GARDEN_TASK_CONCURRENCY_LIMIT: "10"
resource_class: large

# Configuration for release jobs
release-config: &release-config
docker:
# Image that contains ghr for publishing releases to Github
- image: cibuilds/github:0.10

# Attach's the current saved workspace
attach-workspace: &attach-workspace
attach_workspace:
Expand Down Expand Up @@ -161,6 +167,29 @@ commands:
gcloud --quiet config set project $GOOGLE_PROJECT_ID && gcloud --quiet config set compute/zone $GOOGLE_COMPUTE_ZONE
gcloud --quiet container clusters get-credentials $GOOGLE_CLUSTER_ID --zone $GOOGLE_COMPUTE_ZONE
gcloud --quiet auth configure-docker

build_service_dist:
description: Package built code into executables and persist to garden-service/dist directory
parameters:
version:
description: |
The version tag used when building. Use to set the version string in the generated zip file names,
e.g. when creating unstable releases. The script defaults to using the version from garden-service/package.json.
type: string
default: ""
steps:
- checkout
- npm_install
- run: sudo apt-get update && sudo apt-get -y install rsync
- *attach-workspace
- include_dashboard
- run:
name: Run dist command with the appropriate argument
command: npm run dist -- -- <<parameters.version>>
- persist_to_workspace:
root: ./
paths:
- garden-service/dist/
#
# Jobs section
#
Expand Down Expand Up @@ -278,15 +307,53 @@ jobs:
build-service-dist:
<<: *node-config
steps:
- build_service_dist:
version: $(./garden-service/bin/garden --version)
build-service-dist-next:
<<: *node-config
steps:
- build_service_dist:
version: next
release-service-dist:
<<: *release-config
steps:
# Need to checkout to read version from garden-service/package.json
- checkout
- npm_install
- run: sudo apt-get update && sudo apt-get -y install rsync
- *attach-workspace
- include_dashboard
- run: npm run dist
- store_artifacts:
path: garden-service/dist/
destination: /downloads
- run:
name: Create a release on GitHub. If the release is a pre-release we publish it right away, otherwise we make a draft.
command: |
VERSION="v$(cat garden-service/package.json | jq -r .version)"
PRERELEASE=""
DRAFT=""
if [[ $VERSION == *"-"* ]]; then DRAFT=-draft; PRERELEASE=-prerelease; fi
ghr \
-t ${GITHUB_TOKEN} \
-u ${CIRCLE_PROJECT_USERNAME} \
-r ${CIRCLE_PROJECT_REPONAME} \
-c ${CIRCLE_SHA1} \
-n ${VERSION} \
-delete \
${DRAFT} \
${PRERELEASE} \
${VERSION} ./garden-service/dist
release-service-dist-next:
<<: *release-config
steps:
- *attach-workspace
- run:
name: Publish a pre-release on GitHub with the tag 'next'
command: |
VERSION=next
ghr \
-t ${GITHUB_TOKEN} \
-u ${CIRCLE_PROJECT_USERNAME} \
-r ${CIRCLE_PROJECT_REPONAME} \
-c ${CIRCLE_SHA1} \
-n ${VERSION} \
-delete \
-prerelease \
${VERSION} ./garden-service/dist

workflows:
version: 2
Expand All @@ -310,6 +377,7 @@ workflows:
- test-dashboard:
requires:
- build-service

master:
jobs:
# Duplicated here so we can reference steps that depends on it
Expand All @@ -335,10 +403,14 @@ workflows:
context: docker
requires:
- release-service-docker
- build-service-dist:
- build-service-dist-next:
<<: *only-master
requires:
- build-service
- release-service-dist-next:
<<: *only-master
requires:
- build-service-dist-next

tags:
jobs:
Expand Down Expand Up @@ -369,3 +441,8 @@ workflows:
<<: *only-tags
requires:
- build-service
- release-service-dist:
<<: *only-tags
requires:
- build-service-dist

27 changes: 15 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,18 @@ Our release process generates the following packages:

### Process

The release process is twofold, first a [release script](https://github.com/garden-io/garden/blob/master/bin/release.ts) is run. The script has the signature: `./bin/release.tsx <minor | patch | preminor | prepatch | prerelease> [--force]` and does the following:
The [release script](https://github.com/garden-io/garden/blob/master/bin/release.ts) has the signature:
```sh
./bin/release.tsx <minor | patch | preminor | prepatch | prerelease> [--force] [--dry-run]
```
and does the following:

* Checks out a branch named `release-<version>`.
* Updates `package.json` and `package-lock.json` for `garden-service` and the changelog.
* Commits the changes, tags the commit and pushes the tag and branch, triggering a CI process the creates the release artifacts.
* Updates `garden-service/package.json`, `garden-service/package-lock.json` and `CHANGELOG.md`.
* Commits the changes, tags the commit, and pushes the tag and branch.
* Pushing the tag triggers a CI process the creates the release artifacts and publishes them to Github. If the the release is not a pre-release, we create a draft instead of actually publishing.

Second, we manually upload the artifacts generated in CI to our Github releases page and then write the release notes.
On every merge to `master` we also publish an **unstable** release with the version `next` that is always flagged as a pre-release.

### Steps

Expand All @@ -202,14 +207,12 @@ To make a new release, set your current working directory to the garden root dir
* `git rebase master`
* `./bin/release.ts prerelease`
* If you’re ready to make a proper release, run `./bin/release.ts minor | patch` from `master`. This way, the version bump commits created by the prereleases are omitted from the final history.
2. Open the [Garden project on CircleCI](https://circleci.com/gh/garden-io/garden) and browse to the job marked `release-service-pkg`. Open the **Artifacts** tab and download the listed artifacts.
3. Go to our Github [Releases tab](https://github.com/garden-io/garden/releases) and click the **Draft a new release** button.
4. Fill in the **Tag version** and **Release title** fields with the new release version (same as you used for the tag).
5. Upload the downloaded artifacts.
6. Write release notes (not necessary for RCs). The notes should give an overview of the release and mention all relevant features. They should also **acknowledge all external contributors** and contain the changelog for that release. (To generate a changelog for just that tag, run `git-chglog <tag-name>`.)
7. Click the **Publish release** button.
8. Make a pull request for the branch that was pushed by the script.
9. If you're making an RC, you're done! Otherwise, you need to update Homebrew package: `gulp update-brew`.
2. If you're making a pre-release you're done, and you can now start testing the binaries that were just published to our Github [Releases tab](https://github.com/garden-io/garden/releases). Otherwise go to **step 3**.
3. Go to our Github [Releases tab](https://github.com/garden-io/garden/releases) and click the **Edit** button for the draft just created from CI. Note that for drafts, a new one is always created instead of replacing a previous one.
4. Write release notes. The notes should give an overview of the release and mention all relevant features. They should also **acknowledge all external contributors** and contain the changelog for that release. (To generate a changelog for just that tag, run `git-chglog <tag-name>`.)
5. Click the **Publish release** button.
6. Make a pull request for the branch that was pushed by the script.
7. Update the Homebrew package: `gulp update-brew`.

## Changelog

Expand Down
36 changes: 24 additions & 12 deletions bin/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import * as semver from "semver"
import * as inquirer from "inquirer"
import chalk from "chalk"
import parseArgs = require("minimist")
import replace = require("replace-in-file")
import deline = require("deline")
import { join, resolve } from "path"
import { ReplaceResults } from "replace-in-file"
const replace = require("replace-in-file")

type ReleaseType = "minor" | "patch" | "preminor" | "prepatch" | "prerelease"
const RELEASE_TYPES = ["minor", "patch", "preminor", "prepatch", "prerelease"]
Expand All @@ -22,23 +23,24 @@ const gardenServiceRoot = join(gardenRoot, "garden-service")
* 5. Update the changelog.
* 6. Add and commit CHANGELOG.md, garden-service/package.json and garden-service/package-lock.json
* 7. Tag the commit.
* 8. Push the tag. This triggers CircleCI process that creates the release artifacts.
* 8. Push the tag. This triggers a CircleCI job that creates the release artifacts and publishes them to Github.
* 9. If we're making a minor release, update links to examples and re-push the tag.
* 10. Pushes the release branch to Github.
*
* Usage: ./bin/release.ts <minor | patch | preminor | prepatch | prerelease> [--force]
* Usage: ./bin/release.ts <minor | patch | preminor | prepatch | prerelease> [--force] [--dry-run]
*/
async function release() {
// Parse arguments
const argv = parseArgs(process.argv.slice(2))
const releaseType = <ReleaseType>argv._[0]
const force = argv.force
const force = !!argv.force
const dryRun = !!argv["dry-run"]

// Check if branch is clean
try {
await execa("git", ["diff", "--exit-code"], { cwd: gardenRoot })
} catch (_) {
// throw new Error("Current branch has unstaged changes, aborting.")
throw new Error("Current branch has unstaged changes, aborting.")
}

if (!RELEASE_TYPES.includes(releaseType)) {
Expand Down Expand Up @@ -127,8 +129,10 @@ async function release() {
], { cwd: gardenRoot })

// Tag the commit and push the tag
console.log("Pushing tag...")
await createTag(version, force)
if (!dryRun) {
console.log("Pushing tag...")
await createTag(version, force)
}

// Reset local tag state (after stripping release tags)
await execa("git", ["fetch", "origin", "--tags"], { cwd: gardenRoot })
Expand All @@ -149,11 +153,19 @@ async function release() {
await execa("git", ["commit", "--amend", "--no-edit"], { cwd: gardenRoot })

// Tag the commit and force push the tag after updating the links (this triggers another CI build)
await createTag(version, true)
if (!dryRun) {
await createTag(version, true)
}
}

console.log("Pushing release branch...")
await execa("git", ["push", "origin", branchName, "--no-verify"], { cwd: gardenRoot })
if (!dryRun) {
console.log("Pushing release branch...")
const pushArgs = ["push", "origin", branchName, "--no-verify"]
if (force) {
pushArgs.push("-f")
}
await execa("git", pushArgs, { cwd: gardenRoot })
}

console.log(deline`
\nVersion ${chalk.bold.cyan(version)} has been ${chalk.bold("tagged")}, ${chalk.bold("committed")},
Expand Down Expand Up @@ -191,8 +203,8 @@ async function updateExampleLinks(version: string) {
from: /github\.com\/garden-io\/garden\/tree\/[^\/]*\/examples/g,
to: `github.com/garden-io/garden/tree/${version}/examples`,
}
const changes = await replace(options)
console.log("Modified files:", changes.join(", "))
const results = await replace(options) as ReplaceResults[]
console.log("Modified files:", results.filter(r => r.hasChanged).map(r => r.file).join(", "))
}

async function rollBack() {
Expand Down
17 changes: 16 additions & 1 deletion garden-service/bin/build-pkg.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
#!/bin/bash -e

# Usage ./build-pkg.sh [version]
#
# Use the optional version argument to override the version that is included in the
# zip file names. Used for setting the version on unstable releases.
# Defaults to the version in garden-service/package.json.
# Note that this is only for the version string used in the file name, not the version of the
# code that is built.

garden_service_root=$(cd `dirname $0` && cd .. && pwd)

cd ${garden_service_root}

commit_hash=$(git rev-parse --short HEAD)
version="v$(cat package.json | jq -r .version)"

# Use version argument if provided, otherwise read version from package.json
if [ -n "$1" ]; then
version=$1
else
version="v$(cat package.json | jq -r .version)"
fi


echo "Packaging version ${version}-${commit_hash}"

Expand Down
Loading