vault/scripts/crt-builder.sh
Ryan Cragun fc9dfa2a7d
[QT-358] Unify CRT and local builder workflows (#17766)
Here we make the following major changes:

* Centralize CRT builder logic into a script utility so that we can share the
  logic for building artifacts in CI or locally.
* Simplify the build workflow by calling a reusable workflow many times
  instead of repeating the contents.
* Create a workflow that validates whether or not the build workflow and all
  child workflows have succeeded to allow for merge protection.

Motivation

* We need branch requirements for the build workflow and all subsequent
  integration tests (QT-353)
* We need to ensure that the Enos local builder works (QT-558)
* Debugging build failures can be difficult because one has to hand craft the
  steps to recreate the build
* Merge conflicts between Vault OSS and Vault ENT build workflows are quite
  painful. As the build workflow must be the same file and name we'll reduce
  what is contained in each that is unique. Implementations of building
  will be unique per edition so we don't have to worry about conflict
  resolution.
* Since we're going to be touching the build workflow to do the first two
  items we might as well try and improve those other issues at the same time
  to reduce the overhead of backports and conflicts.

Considerations

* Build logic for Vault OSS and Vault ENT differs
* The Enos local builder was duplicating a lot of what we did in the CRT build
  workflow
* Version and other artifact metadata has been an issue before. Debugging it
  has been tedious and error prone.
* The build workflow is full of brittle copy and paste that is hard to
  understand, especially for all of the release editions in Vault Enterprise
* Branch check requirements for workflows are incredibly painful to use for
  workflows that are dynamic or change often. The required workflows have to be
  configured in Github settings by administrators. They would also prevent us
  from having simple docs PRs since required integration workflows always have
  to run to satisfy branch requirements.
* Doormat credentials requirements that are coming will require us to modify
  which event types trigger workflows. This changes those ahead of time since
  we're doing so much to build workflow. The only noticeable impact will be
  that the build workflow no longer runs on pushes to non-main or release
  branches. In order to test other branches it requires a workflow_dispatch
  from the Actions tab or a pull request.

Solutions

* Centralize the logic that determines build metadata and creates releasable
  Vault artifacts. Instead of cargo-culting logic multiple times in the build
  workflow and the Enos local modules, we now have a crt-builder script which
  determines build metadata and also handles building the UI, Vault, and the
  package bundle. There are make targets for all of the available sub-commands.
  Now what we use in the pipeline is the same thing as the local builder, and
  it can be executed locally by developers. The crt-builder script works in OSS
  and Enterprise so we will never have to deal with them being divergent or with
  special casing things in the build workflow.
* Refactor the bulk of the Vault building into a reusable workflow that we can
  call multiple times. This allows us to define Vault builds in a much simpler
  manner and makes resolving merge conflicts much easier.
* Rather than trying to maintain a list and manually configure the branch check
  requirements for build, we'll trigger a single workflow that uses the github
  event system to determine if the build workflow (all of the sub-workflows
  included) have passed. We'll then create branch restrictions on that single
  workflow down the line.

Signed-off-by: Ryan Cragun me@ryan.ec
2022-11-11 13:14:43 -07:00

235 lines
5.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# The crt-builder is used to detemine build metadata and create Vault builds.
# We use it in build-vault.yml for building release artifacts with CRT. It is
# also used by Enos for artifact_source:local scenario variants.
set -euo pipefail
# We don't want to get stuck in some kind of interactive pager
export GIT_PAGER=cat
# Get the full version information
function version() {
local version
local prerelease
local metadata
version=$(version_base)
prerelease=$(version_pre)
metadata=$(version_metadata)
if [ -n "$metadata" ] && [ -n "$prerelease" ]; then
echo "$version-$prerelease+$metadata"
elif [ -n "$metadata" ]; then
echo "$version+$metadata"
elif [ -n "$prerelease" ]; then
echo "$version-$prerelease"
else
echo "$version"
fi
}
# Get the base version
function version_base() {
: "${VAULT_VERSION:=""}"
if [ -n "$VAULT_VERSION" ]; then
echo "$VAULT_VERSION"
return
fi
: "${VERSION_FILE:=$(repo_root)/sdk/version/version_base.go}"
awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < "$VERSION_FILE"
}
# Get the version pre-release
function version_pre() {
: "${VAULT_PRERELEASE:=""}"
if [ -n "$VAULT_PRERELEASE" ]; then
echo "$VAULT_PRERELEASE"
return
fi
: "${VERSION_FILE:=$(repo_root)/sdk/version/version_base.go}"
awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < "$VERSION_FILE"
}
# Get the version metadata, which is commonly the edition
function version_metadata() {
: "${VAULT_METADATA:=""}"
if [ -n "$VAULT_METADATA" ]; then
echo "$VAULT_METADATA"
return
fi
: "${VERSION_FILE:=$(repo_root)/sdk/version/version_base.go}"
awk '$1 == "VersionMetadata" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < "$VERSION_FILE"
}
# Get the build date from the latest commit since it can be used across all
# builds
function build_date() {
# It's tricky to do an RFC3339 format in a cross platform way, so we hardcode UTC
: "${DATE_FORMAT:="%Y-%m-%dT%H:%M:%SZ"}"
git show --no-show-signature -s --format=%cd --date=format:"$DATE_FORMAT" HEAD
}
# Get the revision, which is the latest commit SHA
function build_revision() {
git rev-parse HEAD
}
# Determine our repository by looking at our origin URL
function repo() {
basename -s .git "$(git config --get remote.origin.url)"
}
# Determine the root directory of the repository
function repo_root() {
git rev-parse --show-toplevel
}
# Determine the artifact basename based on metadata
function artifact_basename() {
: "${PKG_NAME:="vault"}"
: "${GOOS:=$(go env GOOS)}"
: "${GOARCH:=$(go env GOARCH)}"
echo "${PKG_NAME}_$(version)_${GOOS}_${GOARCH}"
}
# Build the UI
function build_ui() {
local repo_root
repo_root=$(repo_root)
pushd "$repo_root"
mkdir -p http/web_ui
popd
pushd "$repo_root/ui"
yarn install --ignore-optional
npm rebuild node-sass
yarn --verbose run build
popd
}
# Build Vault
function build() {
local version
local revision
local prerelease
local build_date
local ldflags
local msg
# Get or set our basic build metadata
version=$(version_base)
revision=$(build_revision)
metadata=$(version_metadata)
prerelease=$(version_pre)
build_date=$(build_date)
: "${GO_TAGS:=""}"
: "${KEEP_SYMBOLS:=""}"
# Build our ldflags
msg="--> Building Vault v$version, revision $revision, built $build_date"
# Strip the symbol and dwarf information by default
if [ -n "$KEEP_SYMBOLS" ]; then
ldflags=""
else
ldflags="-s -w "
fi
ldflags="${ldflags}-X github.com/hashicorp/vault/sdk/version.Version=$version -X github.com/hashicorp/vault/sdk/version.GitCommit=$revision -X github.com/hashicorp/vault/sdk/version.BuildDate=$build_date"
if [ -n "$prerelease" ]; then
msg="${msg}, prerelease ${prerelease}"
ldflags="${ldflags} -X github.com/hashicorp/vault/sdk/version.VersionPrerelease=$prerelease"
fi
if [ -n "$metadata" ]; then
msg="${msg}, metadata ${VAULT_METADATA}"
ldflags="${ldflags} -X github.com/hashicorp/vault/sdk/version.VersionMetadata=$metadata"
fi
# Build vault
echo "$msg"
pushd "$(repo_root)"
mkdir -p dist
mkdir -p out
set -x
go build -v -tags "$GO_TAGS" -ldflags "$ldflags" -o dist/
set +x
popd
}
# Bundle the dist directory
function bundle() {
: "${BUNDLE_PATH:=$(repo_root)/vault.zip}"
echo "--> Bundling dist/* to $BUNDLE_PATH"
zip -r -j "$BUNDLE_PATH" dist/
}
# Prepare legal requirements for packaging
function prepare_legal() {
: "${PKG_NAME:="vault"}"
pushd "$(repo_root)"
mkdir -p dist
curl -o dist/EULA.txt https://eula.hashicorp.com/EULA.txt
curl -o dist/TermsOfEvaluation.txt https://eula.hashicorp.com/TermsOfEvaluation.txt
mkdir -p ".release/linux/package/usr/share/doc/$PKG_NAME"
cp dist/EULA.txt ".release/linux/package/usr/share/doc/$PKG_NAME/EULA.txt"
cp dist/TermsOfEvaluation.txt ".release/linux/package/usr/share/doc/$PKG_NAME/TermsOfEvaluation.txt"
popd
}
# Run the CRT Builder
function main() {
case $1 in
artifact-basename)
artifact_basename
;;
build)
build
;;
build-ui)
build_ui
;;
bundle)
bundle
;;
date)
build_date
;;
prepare-legal)
prepare_legal
;;
revision)
build_revision
;;
version)
version
;;
version-base)
version_base
;;
version-pre)
version_pre
;;
version-meta)
version_metadata
;;
*)
echo "unknown sub-command" >&2
exit 1
;;
esac
}
main "$@"