Remove goreleaser in favor of build scripts (#2143)

This commit is contained in:
Dean Sheather
2022-06-19 05:47:10 +10:00
committed by GitHub
parent a9c166491d
commit 075e891f28
21 changed files with 1697 additions and 285 deletions
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
# This script creates an archive containing the given binary renamed to
# `coder(.exe)?`, as well as the README.md and LICENSE files from the repo root.
#
# Usage: ./archive.sh --format tar.gz [--output path/to/output.tar.gz] [--sign-darwin] path/to/binary
#
# The --format parameter must be set, and must either be "zip" or "tar.gz".
#
# If the --output parameter is not set, the default output path is the binary
# path (minus any .exe suffix) plus the format extension ".zip" or ".tar.gz".
#
# If --sign-darwin is specified, the zip file is signed with the `codesign`
# utility and then notarized using the `gon` utility, which may take a while.
# $AC_APPLICATION_IDENTITY must be set and the signing certificate must be
# imported for this to work. Also, the input binary must already be signed with
# the `codesign` tool.
#
# The absolute output path is printed on success.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
format=""
output_path=""
sign_darwin=0
args="$(getopt -o "" -l format:,output:,sign-darwin -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--format)
format="${2#.}"
if [[ "$format" != "zip" ]] && [[ "$format" != "tar.gz" ]]; then
error "Invalid --format parameter '$format', must be 'zip' or 'tar.gz'"
fi
shift 2
;;
--output)
# realpath fails if the dir doesn't exist.
mkdir -p "$(dirname "$2")"
output_path="$(realpath "$2")"
shift 2
;;
--sign-darwin)
if [[ "${AC_APPLICATION_IDENTITY:-}" == "" ]]; then
error "AC_APPLICATION_IDENTITY must be set when --sign-darwin is supplied"
fi
sign_darwin=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
if [[ "$format" == "" ]]; then
error "--format is a required parameter"
fi
if [[ "$#" != 1 ]]; then
error "Exactly one argument must be provided to this script, $# were supplied"
fi
if [[ ! -f "$1" ]]; then
error "File '$1' does not exist or is not a regular file"
fi
input_file="$(realpath "$1")"
# Check dependencies
if [[ "$format" == "zip" ]]; then
dependencies zip
fi
if [[ "$format" == "tar.gz" ]]; then
dependencies tar
fi
if [[ "$sign_darwin" == 1 ]]; then
dependencies jq codesign gon
fi
# Determine default output path.
if [[ "$output_path" == "" ]]; then
output_path="${input_file%.exe}"
output_path+=".$format"
fi
# Determine the filename of the binary inside the archive.
output_file="coder"
if [[ "$input_file" == *".exe" ]]; then
output_file+=".exe"
fi
# Make temporary dir where all source files intended to be in the archive will
# be symlinked from.
cdroot
temp_dir="$(mktemp -d)"
ln -s "$input_file" "$temp_dir/$output_file"
ln -s "$(realpath README.md)" "$temp_dir/"
ln -s "$(realpath LICENSE)" "$temp_dir/"
# Ensure parent output dir and non-existent output file.
mkdir -p "$(dirname "$output_path")"
if [[ -e "$output_path" ]]; then
rm "$output_path"
fi
cd "$temp_dir"
if [[ "$format" == "zip" ]]; then
zip "$output_path" ./* 1>&2
else
tar --dereference -czvf "$output_path" ./* 1>&2
fi
cdroot
rm -rf "$temp_dir"
if [[ "$sign_darwin" == 1 ]]; then
log "Notarizing archive..."
execrelative ./sign_darwin.sh "$output_path"
fi
echo "$output_path"
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
# This script builds a Docker image of Coder containing the given binary, for
# the given architecture. Only linux binaries are supported at this time.
#
# Usage: ./build_docker.sh --arch amd64 [--version 1.2.3] [--push] path/to/coder
#
# The --arch parameter is required and accepts a Golang arch specification. It
# will be automatically mapped to a suitable architecture that Docker accepts
# before being passed to `docker buildx build`.
#
# The image will be built and tagged against the image tag returned by
# ./image_tag.sh.
#
# If no version is specified, defaults to the version from ./version.sh.
#
# If the --push parameter is supplied, the image will be pushed.
#
# Prints the image tag on success.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
arch=""
version=""
push=0
args="$(getopt -o "" -l arch:,version:,push -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--arch)
arch="$2"
shift 2
;;
--version)
version="$2"
shift 2
;;
--push)
push=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
if [[ "$arch" == "" ]]; then
error "The --arch parameter is required"
fi
# Check dependencies
dependencies docker
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
image_tag="$(execrelative ./image_tag.sh --arch "$arch" --version="$version")"
if [[ "$#" != 1 ]]; then
error "Exactly one argument must be provided to this script, $# were supplied"
fi
if [[ ! -f "$1" ]]; then
error "File '$1' does not exist or is not a regular file"
fi
input_file="$(realpath "$1")"
# Remap the arch from Golang to Docker.
declare -A arch_map=(
[amd64]="linux/amd64"
[arm64]="linux/arm64"
[arm]="linux/arm/v7"
[armv7]="linux/arm/v7"
)
if [[ "${arch_map[$arch]+exists}" != "" ]]; then
arch="${arch_map[$arch]}"
fi
# Make temporary dir where all source files intended to be in the image will be
# hardlinked from.
cdroot
temp_dir="$(TMPDIR="$(dirname "$input_file")" mktemp -d)"
ln -P "$input_file" "$temp_dir/coder"
ln -P Dockerfile "$temp_dir/"
cd "$temp_dir"
build_args=(
--platform "$arch"
--build-arg "CODER_VERSION=$version"
--tag "$image_tag"
)
log "--- Building Docker image for $arch ($image_tag)"
docker buildx build "${build_args[@]}" . 1>&2
cdroot
rm -rf "$temp_dir"
if [[ "$push" == 1 ]]; then
log "--- Pushing Docker image for $arch ($image_tag)"
docker push "$image_tag"
fi
echo "$image_tag"
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# This script merges Coder Docker images of different architectures together
# into the specified target image+tag, or the arch-less image tag returned by
# ./image_tag.sh.
#
# Usage: ./build_docker_multiarch.sh [--version 1.2.3] [--target image:tag] [--push] image1:tag1 image2:tag2
#
# The supplied images must already be pushed to the registry or this will fail.
# Also, the source images cannot be in a different registry than the target
# image.
#
# If no version is specified, defaults to the version from ./version.sh.
#
# If no target tag is supplied, the arch-less image tag returned by
# ./image_tag.sh will be used.
#
# If the --push parameter is supplied, all supplied tags will be pushed.
#
# Returns the merged image tag.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
version=""
target=""
push=0
args="$(getopt -o "" -l version:,target:,push -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--version)
version="$2"
shift 2
;;
--target)
target="$2"
shift 2
;;
--push)
push=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
if [[ "$#" == 0 ]]; then
error "At least one argument must be provided to this script, $# were supplied"
fi
# Check dependencies
dependencies docker
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
if [[ "$target" == "" ]]; then
target="$(execrelative ./image_tag.sh --version "$version")"
fi
create_args=()
for image_tag in "$@"; do
create_args+=(--amend "$image_tag")
done
# Sadly, multi-arch images don't seem to support labels.
log "--- Creating multi-arch Docker image ($target)"
docker manifest create \
"$target" \
"${create_args[@]}"
if [[ "$push" == 1 ]]; then
log "--- Pushing multi-arch Docker image ($target)"
docker manifest push "$target"
fi
echo "$target"
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env bash
# This script builds a single Go binary of Coder with the given parameters.
#
# Usage: ./build_go.sh [--version 1.2.3-devel+abcdef] [--os linux] [--arch amd64] [--output path/to/output] [--slim]
#
# Defaults to linux:amd64 with slim disabled, but can be controlled with GOOS,
# GOARCH and CODER_SLIM_BUILD=1. If no version is specified, defaults to the
# version from ./version.sh.
#
# GOARM can be controlled by suffixing any arm architecture (i.e. arm or arm64)
# with "vX" (e.g. "v7", "v8").
#
# Unless overridden via --output, the built binary will be dropped in
# "$repo_root/dist/coder_$version_$os_$arch" (with a ".exe" suffix for windows
# builds) and the absolute path to the binary will be printed to stdout on
# completion.
#
# If the --sign-darwin parameter is specified and the OS is darwin, binaries
# will be signed using the `codesign` utility. $AC_APPLICATION_IDENTITY must be
# set and the signing certificate must be imported for this to work.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
cdroot
version=""
os="${GOOS:-linux}"
arch="${GOARCH:-amd64}"
slim="${CODER_SLIM_BUILD:-0}"
sign_darwin=0
output_path=""
args="$(getopt -o "" -l version:,os:,arch:,output:,slim,sign-darwin -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--version)
version="$2"
shift 2
;;
--os)
os="$2"
shift 2
;;
--arch)
arch="$2"
shift 2
;;
--output)
output_path="$(realpath "$2")"
shift 2
;;
--slim)
slim=1
shift
;;
--sign-darwin)
if [[ "${AC_APPLICATION_IDENTITY:-}" == "" ]]; then
error "AC_APPLICATION_IDENTITY must be set when --sign-darwin is supplied"
fi
sign_darwin=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
# Check dependencies
dependencies go
if [[ "$sign_darwin" == 1 ]]; then
dependencies codesign
fi
build_args=(
-ldflags "-s -w -X 'github.com/coder/coder/buildinfo.tag=$version'"
)
if [[ "$slim" == 0 ]]; then
build_args+=(-tags embed)
fi
# Compute default output path.
if [[ "$output_path" == "" ]]; then
dist_dir="dist"
mkdir -p "$dist_dir"
output_path="${dist_dir}/coder_${version}_${os}_${arch}"
if [[ "$os" == "windows" ]]; then
output_path+=".exe"
fi
output_path="$(realpath "$output_path")"
fi
build_args+=(-o "$output_path")
# Determine GOARM.
arm_version=""
if [[ "$arch" == "arm" ]]; then
arm_version="7"
elif [[ "$arch" == "armv"* ]] || [[ "$arch" == "arm64v"* ]]; then
arm_version="${arch//*v/}"
# Remove the v* suffix.
arch="${arch//v*/}"
fi
CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" GOARM="$arm_version" go build \
"${build_args[@]}" \
./cmd/coder 1>&2
if [[ "$sign_darwin" == 1 ]] && [[ "$os" == "darwin" ]]; then
codesign -s "$AC_APPLICATION_IDENTITY" -f -v --timestamp --options runtime "$output_path"
fi
echo "$output_path"
+229
View File
@@ -0,0 +1,229 @@
#!/usr/bin/env bash
# This script builds multiple Go binaries for Coder with the given OS and
# architecture combinations.
#
# Usage: ./build_go_matrix.sh [--version 1.2.3-devel+abcdef] [--output dist/] [--slim] [--sign-darwin] [--archive] [--package-linux] os1:arch1,arch2 os2:arch1 os1:arch3
#
# If no OS:arch combinations are provided, nothing will happen and no error will
# be returned. Slim builds are disabled by default. If no version is specified,
# defaults to the version from ./version.sh
#
# The --output parameter must be a directory with a trailing slash where all
# files will be dropped with the default name scheme
# `coder_$version_$os_$arch(.exe)?`, or must contain the `{os}` and `{arch}`
# template variables. You may also use `{version}`. Note that for windows builds
# the `.exe` suffix will be appended automatically.
#
# Unless overridden via --output, the built binary will be dropped in
# "$repo_root/dist/coder_$version_$os_$arch" (with a ".exe" suffix for windows
# builds).
#
# If the --sign-darwin parameter is specified, all darwin binaries will be
# signed using the `codesign` utility. $AC_APPLICATION_IDENTITY must be set and
# the signing certificate must be imported for this to work.
#
# If the --archive parameter is specified, all binaries will be archived using
# ./archive.sh. The --sign-darwin parameter will be carried through, and all
# archive files will be dropped in the output directory with the same name as
# the binary and the .zip (for windows and darwin) or .tar.gz extension.
#
# If the --package-linux parameter is specified, all linux binaries will be
# packaged using ./package.sh. Requires the nfpm binary.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
version=""
output_path=""
slim=0
sign_darwin=0
archive=0
package_linux=0
args="$(getopt -o "" -l version:,output:,slim,sign-darwin,archive,package-linux -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--version)
version="$2"
shift 2
;;
--output)
output_path="$2"
shift 2
;;
--slim)
slim=1
shift
;;
--sign-darwin)
if [[ "${AC_APPLICATION_IDENTITY:-}" == "" ]]; then
error "AC_APPLICATION_IDENTITY must be set when --sign-darwin is supplied"
fi
sign_darwin=1
shift
;;
--archive)
archive=1
shift
;;
--package-linux)
package_linux=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
# Verify the output path template.
if [[ "$output_path" == "" ]]; then
# Input paths are relative, so we don't cdroot at the top, but for this case
# we want it to be relative to the root.
cdroot
mkdir -p dist
output_path="$(realpath "dist/coder_{version}_{os}_{arch}")"
elif [[ "$output_path" == */ ]]; then
output_path="${output_path}coder_{version}_{os}_{arch}"
elif [[ "$output_path" != *"{os}"* ]] || [[ "$output_path" != *"{arch}"* ]]; then
# If the output path isn't a directory (ends with /) then it must have
# template variables.
error "Templated output path '$output_path' must contain {os} and {arch}"
fi
mkdir -p "$(dirname "$output_path")"
output_path="$(realpath "$output_path")"
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
# Parse the os:arch specs into an array.
specs=()
may_zip=0
may_tar=0
for spec in "$@"; do
spec_os="$(echo "$spec" | cut -d ":" -f 1)"
if [[ "$spec_os" == "" ]] || [[ "$spec_os" == *" "* ]]; then
error "Could not parse matrix build spec '$spec': invalid OS '$spec_os'"
fi
# Determine which dependencies we need.
if [[ "$spec_os" == "windows" ]] || [[ "$spec_os" == "darwin" ]]; then
may_zip=1
else
may_tar=1
fi
# No quoting is important here.
for spec_arch in $(echo "$spec" | cut -d ":" -f 2 | tr "," "\n"); do
if [[ "$spec_arch" == "" ]] || [[ "$spec_os" == *" "* ]]; then
error "Could not parse matrix build spec '$spec': invalid architecture '$spec_arch'"
fi
specs+=("$spec_os:$spec_arch")
done
done
# Remove duplicate specs while maintaining the same order.
specs_str="${specs[*]}"
specs=()
for s in $(echo "$specs_str" | tr " " "\n" | awk '!a[$0]++'); do
specs+=("$s")
done
# Check dependencies
dependencies go
if [[ "$sign_darwin" == 1 ]]; then
dependencies jq codesign gon
fi
if [[ "$archive" == 1 ]]; then
if [[ "$may_zip" == 1 ]]; then
dependencies zip
fi
if [[ "$may_tar" == 1 ]]; then
dependencies tar
fi
fi
if [[ "$package_linux" == 1 ]]; then
dependencies nfpm
fi
bin_name="coder"
build_args=()
if [[ "$slim" == 1 ]]; then
bin_name+="-slim"
build_args+=(--slim)
fi
if [[ "$sign_darwin" == 1 ]]; then
build_args+=(--sign-darwin)
fi
# Build each spec.
for spec in "${specs[@]}"; do
spec_os="$(echo "$spec" | cut -d ":" -f 1)"
spec_arch="$(echo "$spec" | cut -d ":" -f 2)"
# Craft output path from the template.
spec_output="$output_path"
spec_output="${spec_output//\{os\}/"$spec_os"}"
spec_output="${spec_output//\{arch\}/"$spec_arch"}"
spec_output="${spec_output//\{version\}/"$version"}"
spec_output_binary="$spec_output"
if [[ "$spec_os" == "windows" ]]; then
spec_output_binary+=".exe"
fi
# Ensure parent dir.
mkdir -p "$(dirname "$spec_output")"
log "--- Building $bin_name for $spec_os $spec_arch ($spec_output_binary)"
execrelative ./build_go.sh \
--version "$version" \
--os "$spec_os" \
--arch "$spec_arch" \
--output "$spec_output_binary" \
"${build_args[@]}"
log
log
if [[ "$archive" == 1 ]]; then
spec_archive_format="tar.gz"
if [[ "$spec_os" == "windows" ]] || [[ "$spec_os" == "darwin" ]]; then
spec_archive_format="zip"
fi
spec_output_archive="$spec_output.$spec_archive_format"
archive_args=()
if [[ "$sign_darwin" == 1 ]] && [[ "$spec_os" == "darwin" ]]; then
archive_args+=(--sign-darwin)
fi
log "--- Creating archive for $spec_os $spec_arch ($spec_output_archive)"
execrelative ./archive.sh \
--format "$spec_archive_format" \
--output "$spec_output_archive" \
"${archive_args[@]}" \
"$spec_output_binary"
log
log
fi
if [[ "$package_linux" == 1 ]] && [[ "$spec_os" == "linux" ]]; then
execrelative ./package.sh \
--arch "$spec_arch" \
--version "$version" \
"$spec_output_binary"
log
fi
done
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# This script builds multiple "slim" Go binaries for Coder with the given OS and
# architecture combinations. This wraps ./build_go_matrix.sh.
#
# Usage: ./build_go_slim.sh [--version 1.2.3-devel+abcdef] [--output dist/] os1:arch1,arch2 os2:arch1 os1:arch3
#
# If no OS:arch combinations are provided, nothing will happen and no error will
# be returned. If no version is specified, defaults to the version from
# ./version.sh
#
# The --output parameter differs from ./build_go_matrix.sh, in that it does not
# accept variables such as `{os}` and `{arch}` and only accepts a directory
# ending with `/`.
#
# The built binaries are additionally copied to the site output directory so
# they can be packaged into non-slim binaries correctly.
set -euo pipefail
shopt -s nullglob
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
version=""
output_path=""
args="$(getopt -o "" -l version:,output: -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--version)
version="$2"
shift 2
;;
--output)
output_path="$2"
shift 2
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
# Check dependencies
dependencies go
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
# Verify the output path.
if [[ "$output_path" == "" ]]; then
# Input paths are relative, so we don't cdroot at the top, but for this case
# we want it to be relative to the root.
cdroot
mkdir -p dist
output_path="$(realpath "dist/coder-slim_{version}_{os}_{arch}")"
elif [[ "$output_path" != */ ]] || [[ "$output_path" == *"{"* ]]; then
error "The output path '$output_path' cannot contain variables and must end with a slash"
else
mkdir -p "$output_path"
output_path="$(realpath "${output_path}coder-slim_{version}_{os}_{arch}")"
fi
./scripts/build_go_matrix.sh \
--version "$version" \
--output "$output_path" \
--slim \
"$@"
cdroot
dest_dir="./site/out/bin"
mkdir -p "$dest_dir"
dest_dir="$(realpath "$dest_dir")"
# Copy the binaries to the site directory.
cd "$(dirname "$output_path")"
for f in ./coder-slim_*; do
f="${f#./}"
dest="$dest_dir/${f//-slim_$version/}"
cp "$f" "$dest"
done
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# This script prints the image tag to use for the given arch and version
# combination.
#
# Usage: ./image_tag.sh [--arch amd64] [--version 1.2.3]
#
# The --arch parameter accepts a Golang arch specification. If not specified,
# the image tag for the multi-arch image will be returned instead.
#
# If no version is specified, defaults to the version from ./version.sh. If the
# supplied version is "latest", no `v` prefix will be added to the tag.
#
# The returned tag will be sanitized to remove invalid characters like the plus
# sign.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
arch=""
version=""
args="$(getopt -o "" -l arch:,version: -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--arch)
arch="$2"
shift 2
;;
--version)
version="$2"
shift 2
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
# Remove the "v" prefix because we don't want to add it twice.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
image="${CODER_IMAGE_BASE:-ghcr.io/coder/coder}"
tag="v$version"
if [[ "$version" == "latest" ]]; then
tag="latest"
fi
if [[ "$arch" != "" ]]; then
tag+="-$arch"
fi
# Dev versions contain plus signs which are illegal characters in Docker tags.
tag="${tag//+/-}"
echo "$image:$tag"
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# This script is meant to be sourced by other scripts. To source this script:
# # shellcheck source=scripts/lib.sh
# source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
set -euo pipefail
# realpath returns an absolute path to the given relative path. It will fail if
# the parent directory of the path does not exist. Make sure you are in the
# expected directory before running this to avoid errors.
#
# GNU realpath relies on coreutils, which are not installed or the default on
# Macs out of the box, so we have this mostly working bash alternative instead.
#
# Taken from https://stackoverflow.com/a/3915420 (CC-BY-SA 4.0)
realpath() {
local dir
local base
dir="$(dirname "$1")"
base="$(basename "$1")"
if [[ ! -d "$dir" ]]; then
error "Could not change directory to '$dir': directory does not exist"
fi
echo "$(
cd "$dir" || error "Could not change directory to '$dir'"
pwd -P
)"/"$base"
}
# We have to define realpath before these otherwise it fails on Mac's bash.
SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
PROJECT_ROOT="$(cd "$SCRIPT_DIR" && realpath "$(git rev-parse --show-toplevel)")"
# pushd is a silent alternative to the real pushd shell command.
pushd() {
command pushd "$@" >/dev/null
}
# popd is a silent alternative to the real popd shell command.
# shellcheck disable=SC2120
popd() {
command popd "$@" >/dev/null
}
# cdself changes directory to the directory of the current script. This should
# not be used in scripts that may be sourced by other scripts.
cdself() {
cd "$SCRIPT_DIR" || error "Could not change directory to '$SCRIPT_DIR'"
}
# cdroot changes directory to the root of the repository.
cdroot() {
cd "$PROJECT_ROOT" || error "Could not change directory to '$PROJECT_ROOT'"
}
# execrelative can be used to execute scripts as if you were in the parent
# directory of the current script. This should not be used in scripts that may
# be sourced by other scripts.
execrelative() {
pushd "$SCRIPT_DIR" || error "Could not change directory to '$SCRIPT_DIR'"
local rc=0
"$@" || rc=$?
popd
return $rc
}
dependencies() {
local fail=0
for dep in "$@"; do
if ! command -v "$dep" >/dev/null; then
log "ERROR: The '$dep' dependency is required, but is not available."
fail=1
fi
done
if [[ "$fail" == 1 ]]; then
log
error "One or more dependencies are not available, check above log output for more details."
fi
}
# maybedryrun prints the given program and flags, and then, if the first
# argument is 0, executes it. The reason the first argument should be 0 is that
# it is expected that you have a dry_run variable in your script that is set to
# 0 by default (i.e. do not dry run) and set to 1 if the --dry-run flag is
# specified.
#
# Usage: maybedryrun 1 gh release create ...
# Usage: maybedryrun 0 docker push ghcr.io/coder/coder:latest
maybedryrun() {
if [[ "$1" == 1 ]]; then
shift
log "DRYRUN: $*"
else
shift
log $ "$@"
"$@"
fi
}
# log prints a message to stderr.
log() {
echo "$*" 1>&2
}
# error prints an error message and returns an error exit code.
error() {
log "ERROR: $*"
exit 1
}
# isdarwin returns an error if the current platform is not darwin.
isdarwin() {
[[ "${OSTYPE:-darwin}" == *darwin* ]]
}
libsh_bad_dependencies=0
if ((BASH_VERSINFO[0] < 4)); then
libsh_bad_dependencies=1
log "ERROR: You need at least bash 4.0 to run the scripts in the Coder repo."
if isdarwin; then
log "On darwin:"
log "- brew install bash"
log "- Restart your terminal"
fi
log
fi
# BSD getopt (which is installed by default on Macs) is not supported.
if [[ "$(getopt --version)" == *--* ]]; then
libsh_bad_dependencies=1
log "ERROR: You need GNU getopt to run the scripts in the Coder repo."
if isdarwin; then
log "On darwin:"
log "- brew install gnu-getopt"
# shellcheck disable=SC2016
log '- Add "$(brew --prefix)/opt/gnu-getopt/bin" to your PATH'
log "- Restart your terminal"
fi
log
fi
# The bash scripts don't call Make directly, but we want to make (ha ha) sure
# that make supports the features the repo uses. Notably, Macs have an old
# version of Make installed out of the box that doesn't support new features
# like ONESHELL.
make_version="$(make --version 2>/dev/null | head -n1 | grep -oE '([[:digit:]]+\.){1,2}[[:digit:]]+')"
if [ "${make_version//.*/}" -lt 4 ]; then
libsh_bad_dependencies=1
log "ERROR: You need at least make 4.0 to run the scripts in the Coder repo."
if isdarwin; then
log "On darwin:"
log "- brew install make"
# shellcheck disable=SC2016
log '- Add "$(brew --prefix)/opt/make/libexec/gnubin" to your PATH (you should Google this first)'
log "- Restart your terminal"
fi
log
fi
if [[ "$libsh_bad_dependencies" == 1 ]]; then
error "Invalid dependencies, see above for more details."
fi
+28
View File
@@ -0,0 +1,28 @@
name: coder
platform: linux
arch: "${GOARCH}"
version: "${CODER_VERSION}"
version_schema: semver
release: 1
vendor: Coder
homepage: https://coder.com
maintainer: Coder <support@coder.com>
description: |
Provision development environments with infrastructure with code
license: AGPL-3.0
suggests:
- postgresql
scripts:
preinstall: preinstall.sh
contents:
- src: coder
dst: /usr/bin/coder
- src: coder.env
dst: /etc/coder.d/coder.env
type: "config|noreplace"
- src: coder.service
dst: /usr/lib/systemd/system/coder.service
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# This script creates Linux packages for the given binary. It will output a
# .rpm, .deb and .apk file in the same directory as the input file with the same
# filename (except the package format suffix).
#
# ./package.sh --arch amd64 [--version 1.2.3] path/to/coder
#
# The --arch parameter is required. If no version is specified, defaults to the
# version from ./version.sh.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
version=""
arch=""
args="$(getopt -o "" -l arch:,version: -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--arch)
arch="$2"
shift 2
;;
--version)
version="$2"
shift 2
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
if [[ "$arch" == "" ]]; then
error "--arch is a required parameter"
fi
if [[ "$#" != 1 ]]; then
error "Exactly one argument must be provided to this script, $# were supplied"
fi
if [[ ! -f "$1" ]]; then
error "File '$1' does not exist or is not a regular file"
fi
input_file="$(realpath "$1")"
# Check dependencies
dependencies nfpm
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
# Make temporary dir where all source files intended to be in the package will
# be hardlinked from.
cdroot
temp_dir="$(TMPDIR="$(dirname "$input_file")" mktemp -d)"
ln -P "$input_file" "$temp_dir/coder"
ln -P "$(realpath coder.env)" "$temp_dir/"
ln -P "$(realpath coder.service)" "$temp_dir/"
ln -P "$(realpath preinstall.sh)" "$temp_dir/"
ln -P "$(realpath scripts/nfpm.yaml)" "$temp_dir/"
cd "$temp_dir"
formats=(apk deb rpm)
for format in "${formats[@]}"; do
output_path="$input_file.$format"
log "--- Building $format package ($output_path)"
nfpm package \
-f nfpm.yaml \
-p "$format" \
-t "$output_path"
done
cdroot
rm -rf "$temp_dir"
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env bash
# This script generates release notes and publishes all of the given assets to
# GitHub releases. Depends on GitHub CLI.
#
# Usage: ./publish_release.sh [--version 1.2.3] [--dry-run] path/to/asset1 path/to/asset2 ...
#
# The supplied images must already be pushed to the registry or this will fail.
# Also, the source images cannot be in a different registry than the target
# image generated by ./image_tag.sh.
# The supplied assets will be uploaded to the GitHub release as-is, as well as a
# file containing checksums.
#
# If no version is specified, defaults to the version from ./version.sh. The
# script will exit early if the branch is not tagged with the provided version
# (plus the "v" prefix) unless run with --dry-run.
#
# If the --dry-run parameter is supplied, the release will not be published to
# GitHub at all.
#
# Returns the link to the created GitHub release (unless --dry-run was
# specified).
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
version=""
dry_run=0
args="$(getopt -o "" -l version:,dry-run -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--version)
version="$2"
shift 2
;;
--dry-run)
dry_run=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
# Check dependencies
dependencies gh
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
# realpath-ify all input files so we can cdroot below.
files=()
for f in "$@"; do
if [[ ! -e "$f" ]]; then
error "File not found: $f"
fi
files+=("$(realpath "$f")")
done
if [[ "${#files[@]}" == 0 ]]; then
error "No files supplied"
fi
if [[ "$dry_run" == 0 ]] && [[ "$version" == *dev* ]]; then
error "Cannot publish a dev version to GitHub"
fi
# The git commands need to be executed from within the repository.
cdroot
# Verify that we're currently checked out on the supplied tag.
new_tag="v$version"
if [[ "$(git describe --always)" != "$new_tag" ]]; then
if [[ "$dry_run" == 0 ]]; then
error "The provided version '$new_tag' does not match the current git describe output '$(git describe --always)'"
fi
log "The provided version does not match the current git tag, but --dry-run was supplied so continuing..."
fi
# This returns the tag before the current tag.
old_tag="$(git describe --abbrev=0 HEAD^1)"
# For dry-run builds we want to use the SHA instead of the tag, because the new
# tag probably doesn't exist.
changelog_range="$old_tag..$new_tag"
if [[ "$dry_run" == 1 ]]; then
changelog_range="$old_tag..$(git rev-parse --short HEAD)"
fi
# Craft the release notes.
changelog="$(git log --no-merges --pretty=format:"- %h %s" "$changelog_range")"
image_tag="$(execrelative ./image_tag.sh --version "$version")"
release_notes="
## Changelog
$changelog
## Container Image
- \`docker pull $image_tag\`
"
release_notes_file="$(mktemp)"
echo "$release_notes" >"$release_notes_file"
# Create temporary release folder so we can generate checksums. Both the
# sha256sum and gh binaries support symlinks as input files so this works well.
temp_dir="$(mktemp -d)"
for f in "${files[@]}"; do
ln -s "$f" "$temp_dir/"
done
# Generate checksums file which will be uploaded to the GitHub release.
pushd "$temp_dir"
sha256sum ./* | sed -e 's/\.\///' - >"coder_${version}_checksums.txt"
popd
log "--- Creating release $new_tag"
log
log "Description:"
echo "$release_notes" | sed -e 's/^/\t/' - 1>&2
log
log "Contents:"
pushd "$temp_dir"
find ./* 2>&1 | sed -e 's/^/\t/;s/\.\///' - 1>&2
popd
log
log
# We pipe `true` into `gh` so that it never tries to be interactive.
true |
maybedryrun "$dry_run" gh release create \
--title "$new_tag" \
--notes-file "$release_notes_file" \
"$new_tag" \
"$temp_dir"/*
rm -rf "$temp_dir"
rm -rf "$release_notes_file"
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# This script notarizes the provided zip file.
#
# Usage: ./publish_release.sh [--version 1.2.3] [--dry-run] path/to/asset1 path/to/asset2 ...
#
# The provided zip file must contain a coder binary that has already been signed
# using the codesign tool.
#
# On success, the input file will be successfully signed and notarized.
#
# Depends on codesign and gon utilities. Requires the $AC_APPLICATION_IDENTITY
# environment variable to be set.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
if [[ "${AC_APPLICATION_IDENTITY:-}" == "" ]]; then
error "AC_APPLICATION_IDENTITY must be set for ./sign_darwin.sh"
fi
# Check dependencies
dependencies jq codesign gon
output_path="$1"
# Create the gon config.
config="$(mktemp -d)/gon.json"
jq -r --null-input --arg path "$output_path" '{
"notarize": [
{
"path": $path,
"bundle_id": "com.coder.cli"
}
]
}' >"$config"
# Sign the zip file with our certificate.
codesign -s "$AC_APPLICATION_IDENTITY" -f -v --timestamp --options runtime "$output_path"
# Notarize the signed zip file.
#
# The notarization process is very fragile and heavily dependent on Apple's
# notarization server not returning server errors, so we retry this step twice
# with a delay of a minute between attempts.
rc=0
for i in $(seq 1 2); do
gon "$config" && rc=0 && break || rc=$?
log "gon exit code: $rc"
if [ "$i" -lt 5 ]; then
log
log "Retrying notarization in 60 seconds"
log
sleep 60
else
log
log "Giving up :("
fi
done
exit $rc
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
PROJECT_ROOT=$(cd "$SCRIPT_DIR" && git rev-parse --show-toplevel)
(
cd "${PROJECT_ROOT}"
codesign -s "$AC_APPLICATION_IDENTITY" -f -v --timestamp --options runtime "$1"
config=$(mktemp -d)/gon.json
jq -r --null-input --arg path "$(pwd)/$1" '{
"notarize": [
{
"path": $path,
"bundle_id": "com.coder.cli"
}
]
}' >"$config"
gon "$config"
)
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# This script generates the version string used by Coder, including for dev
# versions. Note: the version returned by this script will NOT include the "v"
# prefix that is included in the Git tag.
#
# If $CODER_RELEASE is set to "true", the returned version will equal the
# current git tag. If the current commit is not tagged, this will fail.
#
# If $CODER_RELEASE is not set, the returned version will always be a dev
# version.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
cdroot
last_tag="$(git describe --tags --abbrev=0)"
version="$last_tag"
# If the HEAD has extra commits since the last tag then we are in a dev version.
#
# Dev versions are denoted by the "-devel+" suffix with a trailing commit short
# SHA.
if [[ "${CODER_RELEASE:-}" == *t* ]]; then
# $last_tag will equal `git describe --always` if we currently have the tag
# checked out.
if [[ "$last_tag" != "$(git describe --always)" ]]; then
# make won't exit on $(shell cmd) failures, so we have to kill it :(
if [[ "$(ps -o comm= "$PPID" || true)" == *make* ]]; then
log "ERROR: version.sh attemped to generate a dev version string when CODER_RELEASE was set"
kill "$PPID" || true
exit 1
fi
error "version.sh attemped to generate a dev version string when CODER_RELEASE was set"
fi
else
version+="-devel+$(git rev-parse --short HEAD)"
fi
# Remove the "v" prefix.
echo "${version#v}"