Skip to content
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
14 changes: 10 additions & 4 deletions shell/lib/github.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ github_token() {
}

# Determines the latest release version of a GitHub repository.
#
# $1: The slug of the repo to query, e.g. "getoutreach/stencil".
# $2: Whether to include pre-releases. `gh release list` returns
# releases newest-first, so "true" selects the newest release of any
# kind, which can be a stable one.
latest_github_release_version() {
local slug="$1"
local use_pre_releases="$2"
Expand All @@ -63,10 +68,11 @@ latest_github_release_version() {
#
# $1: The slug of the repo to download from. This is the same as the
# repo name, e.g. "github/hub".
# $2: Whether or not to use pre-releases. If "true", will download the
# latest pre-release. If "false", or empty, will download the
# latest stable release. "Pre-release", for Outreach releasing, is
# any release that is not marked stable (e.g., unstable or rc).
# $2: Whether or not to include pre-releases. If "true", will download
# the newest release of any kind, stable or not; see
# latest_github_release_version. If "false", or empty, will download
# the latest stable release. "Pre-release", for Outreach releasing,
# is any release that is not marked stable (e.g., unstable or rc).
# $3: The name of the binary to extract from the downloaded archive. If
# empty, will use the basename of the slug.
install_latest_github_release() {
Expand Down
46 changes: 46 additions & 0 deletions shell/lib/github_test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ setup() {
echo 'github-cli = "latest"'
echo 'wait-for-gh-rate-limit = "1.1.1"'
} >>"$MISE_GLOBAL_CONFIG_FILE"

setup_command_stubs
}

teardown() {
Expand All @@ -35,13 +37,48 @@ teardown() {
unset MISE_GLOBAL_CONFIG_ROOT
unset MISE_GLOBAL_CONFIG_FILE
unset MISE_OVERRIDE_CONFIG_FILENAMES

teardown_command_stubs
}

@test "separate mise install" {
run mise doctor
assert_output --partial "config: $MISE_CONFIG_DIR"
}

@test "latest_github_release_version asks gh to exclude pre-releases when they are not wanted" {
stub_command gh "v1.45.0"

run latest_github_release_version getoutreach/stencil false
assert_success
assert_output "v1.45.0"

run stub_argv gh
assert_line "getoutreach/stencil"
assert_line "--exclude-drafts"
assert_line "--exclude-pre-releases"
}

@test "latest_github_release_version takes the newest release of any kind when pre-releases are wanted" {
stub_command gh "v1.45.0"

run latest_github_release_version getoutreach/stencil true
assert_success
assert_output "v1.45.0"

run stub_argv gh
assert_line "--exclude-drafts"
refute_line "--exclude-pre-releases"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nitpick: This test only asserts --exclude-drafts/--exclude-pre-releases, not the repo slug like the test above it. Not a real gap — just flagging for symmetry, feel free to ignore.

  • Generated using AI

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The split is deliberate. The test above pins the full argument shape, including the slug, and this one pins only what differs between the two modes. Repeating the slug assertion here would not catch anything the other test misses.


Generated by Claude Code


@test "install_latest_github_release fails when there is no release to install" {
stub_command gh ""

run install_latest_github_release getoutreach/stencil false stencil
assert_failure
assert_output --partial "Failed to determine version for getoutreach/stencil"
}

@test "install_latest_github_release should be able to download and install the latest release of a repo" {
if circleci_pr_is_fork; then
skip "Skipping test in fork PR, no GitHub token available to utilize gh."
Expand All @@ -59,6 +96,15 @@ teardown() {
if circleci_pr_is_fork; then
skip "Skipping test in fork PR, no GitHub token available to utilize gh."
fi

# The newest release is the stable one between a release and the next
# commit to the repo, so a pre-release is not always available.
local tag
tag="$(latest_github_release_version getoutreach/stencil true)"
if [[ ! $tag =~ (rc|unstable) ]]; then
skip "Newest getoutreach/stencil release ($tag) is stable, not a pre-release."
fi

install_latest_github_release getoutreach/stencil true stencil

# We expect the stencil binary to be installed in the install dir.
Expand Down
46 changes: 5 additions & 41 deletions shell/lib/metrics_test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,19 @@ bats_load_library "bats-assert/load.bash"
load logging.sh
load shell.sh
load metrics.sh
load test_helper.sh

setup() {
STUB_DIR="$(mktemp -d -t metrics-stubs-XXXXXX)"
STUB_CALLS_FILE="$STUB_DIR/calls"
STUB_OUTPUTS_DIR="$STUB_DIR/outputs"
STUB_ARGS_DIR="$STUB_DIR/args"
mkdir -p "$STUB_OUTPUTS_DIR" "$STUB_ARGS_DIR"
: >"$STUB_CALLS_FILE"
export STUB_CALLS_FILE STUB_OUTPUTS_DIR STUB_ARGS_DIR
export PATH="$STUB_DIR:$PATH"
setup_command_stubs
}

teardown() {
rm -rf "$STUB_DIR"
unset STUB_CALLS_FILE STUB_OUTPUTS_DIR STUB_ARGS_DIR
}

# stub_command NAME OUTPUT [EXIT_CODE]
#
# Install an executable stub on PATH that records its invocation to
# $STUB_CALLS_FILE (one line: "NAME ARGS..."), records its full argv to
# $STUB_ARGS_DIR/NAME.argv (one arg per line, latest invocation only),
# prints OUTPUT to stdout, and exits with EXIT_CODE (default 0).
stub_command() {
local name="$1" output="$2" exitCode="${3:-0}"
printf '%s' "$output" >"$STUB_OUTPUTS_DIR/$name"
printf '%s' "$exitCode" >"$STUB_OUTPUTS_DIR/$name.exit"
cat >"$STUB_DIR/$name" <<'EOF'
#!/usr/bin/env bash
name="$(basename "$0")"
echo "$name $*" >>"$STUB_CALLS_FILE"
printf '%s\n' "$@" >"$STUB_ARGS_DIR/$name.argv"
cat "$STUB_OUTPUTS_DIR/$name"
exit "$(cat "$STUB_OUTPUTS_DIR/$name.exit")"
EOF
chmod +x "$STUB_DIR/$name"
}

assert_stub_not_called() {
local name="$1"
# `run` swallows grep's non-zero exit when the count is 0, so we can
# assert the count directly without an explicit failure-path check.
run grep -c "^$name " "$STUB_CALLS_FILE"
assert_output "0"
teardown_command_stubs
}

# Extract the argument passed to curl's `--data` flag (the JSON payload).
curl_payload() {
awk '/^--data$/ { getline; print; exit }' "$STUB_ARGS_DIR/curl.argv"
stub_argv curl | awk '/^--data$/ { getline; print; exit }'
}

@test "report_gh_rate_limit_to_datadog fails when tokenType is empty" {
Expand Down Expand Up @@ -125,7 +89,7 @@ curl_payload() {
assert_output "1"

# Verify endpoint and headers.
run cat "$STUB_ARGS_DIR/curl.argv"
run stub_argv curl
assert_output --partial "https://api.datadoghq.com/api/v2/series"
assert_output --partial "DD-API-KEY: fake-key"
assert_output --partial "Content-Type: application/json"
Expand Down
58 changes: 58 additions & 0 deletions shell/lib/test_helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,61 @@ mktempdir() {
local dir="$tmpdir/$suffix"
mktemp -d "$dir"
}

# setup_command_stubs creates the directories that stub_command writes
# to, and puts the stub directory first on PATH so that stubs shadow the
# real tools. Call it from `setup()` and teardown_command_stubs from
# `teardown()`.
setup_command_stubs() {
STUB_DIR="$(mktempdir devbase-stubs-XXXXXX)"
STUB_CALLS_FILE="$STUB_DIR/calls"
STUB_OUTPUTS_DIR="$STUB_DIR/outputs"
STUB_ARGS_DIR="$STUB_DIR/args"
mkdir -p "$STUB_OUTPUTS_DIR" "$STUB_ARGS_DIR"
: >"$STUB_CALLS_FILE"
export STUB_DIR STUB_CALLS_FILE STUB_OUTPUTS_DIR STUB_ARGS_DIR
export PATH="$STUB_DIR:$PATH"
}

# teardown_command_stubs removes the directory that setup_command_stubs
# created, and unsets the variables that point into it.
teardown_command_stubs() {
rm -rf "$STUB_DIR"
unset STUB_DIR STUB_CALLS_FILE STUB_OUTPUTS_DIR STUB_ARGS_DIR
}

# stub_command NAME OUTPUT [EXIT_CODE]
#
# Install an executable stub on PATH that records its invocation to
# $STUB_CALLS_FILE (one line: "NAME ARGS..."), records its full argv to
# $STUB_ARGS_DIR/NAME.argv (one arg per line, latest invocation only),
# prints OUTPUT to stdout, and exits with EXIT_CODE (default 0).
stub_command() {
local name="$1" output="$2" exitCode="${3:-0}"
printf '%s' "$output" >"$STUB_OUTPUTS_DIR/$name"
printf '%s' "$exitCode" >"$STUB_OUTPUTS_DIR/$name.exit"
cat >"$STUB_DIR/$name" <<'EOF'
#!/usr/bin/env bash
name="$(basename "$0")"
echo "$name $*" >>"$STUB_CALLS_FILE"
printf '%s\n' "$@" >"$STUB_ARGS_DIR/$name.argv"
cat "$STUB_OUTPUTS_DIR/$name"
exit "$(cat "$STUB_OUTPUTS_DIR/$name.exit")"
EOF
chmod +x "$STUB_DIR/$name"
}

# stub_argv NAME prints the arguments of the latest invocation of the
# named stub, one per line.
stub_argv() {
cat "$STUB_ARGS_DIR/$1.argv"
}

# assert_stub_not_called NAME asserts that the named stub never ran.
assert_stub_not_called() {
local name="$1"
# `run` swallows grep's non-zero exit when the count is 0, so we can
# assert the count directly without an explicit failure-path check.
run grep -c "^$name " "$STUB_CALLS_FILE"
assert_output "0"
}