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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- `install.sh` reported a refused request as a repository with no releases. The
fetch was piped straight into `sed`, so its exit status was hidden behind
`head` and an empty parse looked like an empty repository -- the message sent
whoever read it looking in the wrong place. It now reads the response before
parsing it, and says what is usually true: GitHub allows 60 unauthenticated
API requests an hour per address, CI runners share addresses, and the fix is
a token or a pinned version.
- Downloads retry. A reset connection on the way to a release is not a reason
to fail a build.
- A failed installation says so. In a checks list an install that could not
happen and a scan that found something are both a red job ending in `exit
code 1`; the action now annotates the first as an installation failure, and
points at the `token` input when rate limiting is the cause.

### Added

- `site/` — the source for [straitjacket.dev](https://straitjacket.dev), moved
Expand Down
16 changes: 13 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ inputs:
default: "true"
token:
description: >-
Token used to download the release. Needed only while the Straitjacket
repository is private; a public release needs none.
Token used to read the release. Worth setting even for a public release:
GitHub allows 60 unauthenticated API requests an hour per address and CI
runners share addresses, so without one this eventually fails to install
at all. `${{ github.token }}` is enough; it needs no access to the
Straitjacket repository while that repository is public.
required: false
default: ""

Expand All @@ -89,6 +92,7 @@ runs:
using: composite
steps:
- name: Install straitjacket
id: install
shell: bash
env:
VERSION: ${{ inputs.version }}
Expand All @@ -100,7 +104,13 @@ runs:
if [ "$VERSION" != "latest" ]; then
export STRAITJACKET_VERSION="$VERSION"
fi
sh "${ACTION_PATH}/install.sh"
# Failing to install is not the same as finding something, and at a
# glance in the checks list the two look identical: both are a red job
# whose log ends in `exit code 1`. Say which one this is.
if ! sh "${ACTION_PATH}/install.sh"; then
echo "::error title=Straitjacket could not be installed::The scan did not run. This is an installation failure, not a finding -- see the log above, and set the action's \`token\` input if it mentions rate limiting." >&2
exit 1
fi
echo "$STRAITJACKET_INSTALL_DIR" >> "$GITHUB_PATH"

# Inputs reach the script through the environment rather than through
Expand Down
32 changes: 25 additions & 7 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,33 @@ detect_target() {
esac
}

# Retry a few times, because a reset connection on the way to a release is
# not a reason to fail a build. Rate limiting is not retried away -- see the
# message in `main` -- but a dropped connection usually is.
RETRY="--retry 3 --retry-delay 2"

# Fetch a URL to stdout, sending credentials only when they were provided.
fetch() {
# shellcheck disable=SC2086
if [ -n "${GITHUB_TOKEN:-}" ]; then
curl -sSfL -H "Authorization: Bearer ${GITHUB_TOKEN}" "$1"
curl -sSfL $RETRY -H "Authorization: Bearer ${GITHUB_TOKEN}" "$1"
else
curl -sSfL "$1"
curl -sSfL $RETRY "$1"
fi
}

# The tag of the most recent release.
#
# Parsed out of the API response with sed rather than a JSON library, because
# the whole point of this script is to run before anything is installed.
#
# The response is captured before it is parsed. Piping the fetch straight into
# sed hides its exit status behind `head`, so a refused request looked exactly
# like a repository with no releases -- and the message said so, which sent
# whoever read it looking in the wrong place.
latest_version() {
fetch "${API}/releases/latest" |
response=$(fetch "${API}/releases/latest") || return 1
printf '%s\n' "$response" |
sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' |
head -n 1
}
Expand Down Expand Up @@ -135,10 +147,12 @@ download_asset() {
elif [ -n "${GITHUB_TOKEN:-}" ]; then
asset_api=$(fetch "${API}/releases/tags/${version}" | asset_url "$asset_name")
[ -n "$asset_api" ] || die "release ${version} has no asset named ${asset_name}"
curl -sSfL -H "Authorization: Bearer ${GITHUB_TOKEN}" \
# shellcheck disable=SC2086
curl -sSfL $RETRY -H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/octet-stream" "$asset_api" >"$asset_output"
else
curl -sSfL "${DOWNLOAD}/${version}/${asset_name}" >"$asset_output"
# shellcheck disable=SC2086
curl -sSfL $RETRY "${DOWNLOAD}/${version}/${asset_name}" >"$asset_output"
fi
}

Expand All @@ -150,8 +164,12 @@ main() {
target=$(detect_target)
version=${STRAITJACKET_VERSION:-}
if [ -z "$version" ]; then
version=$(latest_version) ||
die "could not read the latest release. Set STRAITJACKET_VERSION, or GITHUB_TOKEN if the repository is private"
version=$(latest_version) || die "could not read the latest release.
GitHub allows 60 unauthenticated API requests an hour per address, and CI
runners share addresses, so this is usually rate limiting rather than a
missing release. Set GITHUB_TOKEN -- any valid token raises the limit, and
it need not have access to this repository while it is public. Or pin
STRAITJACKET_VERSION, which skips the API entirely."
[ -n "$version" ] || die "no published release found. Set STRAITJACKET_VERSION to install a specific tag"
fi

Expand Down