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
30 changes: 30 additions & 0 deletions .github/workflows/test-ct-sh.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: test-ct-sh

on:
pull_request:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions: {}

jobs:
test_ct_sh:
runs-on: ubuntu-latest
permissions:
contents: read # Clone the repository

name: ct.sh failure-path tests
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Run ct.sh tests
run: tests/ct_test.sh

- name: Shellcheck
run: shellcheck ct.sh tests/ct_test.sh
70 changes: 60 additions & 10 deletions ct.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@ DEFAULT_CHART_TESTING_VERSION=3.14.0
DEFAULT_YAMLLINT_VERSION=1.33.0
DEFAULT_YAMALE_VERSION=6.0.0

# Set once a download is staged, so cleanup() can remove it on any exit path.
staging_dir=

cleanup() {
if [[ -n "${staging_dir}" ]]; then
rm -rf "${staging_dir}"
fi
}

# Version strings are interpolated into filesystem paths and into the
# $GITHUB_PATH / $GITHUB_ENV files, so restrict them to characters that can
# neither traverse directories nor add extra lines to those files.
validate_version() {
local flag="$1"
local value="$2"

if [[ ! "${value}" =~ ^[0-9]+(\.[0-9]+)*([-+][A-Za-z0-9.]+)?$ ]]; then
echo "ERROR: '${flag}' must be a version number, got: '${value}'" >&2
exit 1
fi
}

show_help() {
cat << EOF
Usage: $(basename "$0") <options>
Expand All @@ -22,8 +44,14 @@ main() {
local yamllint_version="${DEFAULT_YAMLLINT_VERSION}"
local yamale_version="${DEFAULT_YAMALE_VERSION}"

trap cleanup EXIT

parse_command_line "$@"

validate_version '-v|--version' "${version}"
validate_version '--yamllint-version' "${yamllint_version}"
validate_version '--yamale-version' "${yamale_version}"

install_chart_testing
}

Expand Down Expand Up @@ -88,23 +116,45 @@ install_chart_testing() {
local cache_dir="${RUNNER_TOOL_CACHE}/ct/${version}/${arch}"
local venv_dir="${cache_dir}/venv"

if [[ ! -d "${cache_dir}" ]]; then
mkdir -p "${cache_dir}"

# Only treat the cache as populated when the binary itself is present. An
# empty or partially populated directory -- left behind by an earlier run
# that failed after mkdir but before extraction -- must never suppress
# signature verification.
if [[ ! -x "${cache_dir}/ct" ]]; then
echo "Installing chart-testing v${version}..."
CT_CERT=https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version#v}_linux_${arch}.tar.gz.pem
CT_SIG=https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version#v}_linux_${arch}.tar.gz.sig
local ct_cert="https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version}_linux_${arch}.tar.gz.pem"
local ct_sig="https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version}_linux_${arch}.tar.gz.sig"

# Stage everything outside the cache, and publish to ${cache_dir} only
# after the download, signature verification and extraction have all
# succeeded. This keeps a failed run from leaving anything behind that
# a later run could mistake for a verified install.
staging_dir="$(mktemp -d)"

# --fail so an HTTP error is reported as a download failure rather than
# being saved as the "tarball" and surfacing later as a bogus
# signature-verification error.
if ! curl --fail --retry 5 --retry-delay 1 -sSLo "${staging_dir}/ct.tar.gz" \
"https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version}_linux_${arch}.tar.gz"; then
echo "ERROR: Unable to download chart-testing version: v${version}" >&2
exit 1
fi

curl --retry 5 --retry-delay 1 -sSLo ct.tar.gz "https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version#v}_linux_${arch}.tar.gz"
if ! cosign verify-blob --certificate "${CT_CERT}" --signature "${CT_SIG}" \
if ! cosign verify-blob --certificate "${ct_cert}" --signature "${ct_sig}" \
--certificate-identity "https://github.com/helm/chart-testing/.github/workflows/release.yaml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" ct.tar.gz; then
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" "${staging_dir}/ct.tar.gz"; then
echo "ERROR: Unable to validate chart-testing version: v${version}" >&2
exit 1
fi

tar -xzf ct.tar.gz -C "${cache_dir}"
rm -f ct.tar.gz
mkdir -p "${staging_dir}/extracted"
tar -xzf "${staging_dir}/ct.tar.gz" -C "${staging_dir}/extracted"

# Safe because validate_version has already rejected anything that
# could make ${cache_dir} point outside ${RUNNER_TOOL_CACHE}.
rm -rf "${cache_dir}"
mkdir -p "$(dirname "${cache_dir}")"
mv "${staging_dir}/extracted" "${cache_dir}"

echo 'Creating virtual Python environment...'
export UV_LINK_MODE=copy
Expand Down
Loading
Loading