Files
state-corp-backend/scripts/ci/release-service.sh
Aleksandr Meshchriakov 9194ff3d0d
Some checks failed
State Corp Backend CI/CD / Quality gate (push) Successful in 5m25s
State Corp Backend CI/CD / Build linux/amd64 images once (push) Successful in 2m44s
State Corp Backend CI/CD / Release dev (push) Failing after 8s
State Corp Backend CI/CD / Refresh and release internal main (push) Has been skipped
State Corp Backend CI/CD / Release customer main (push) Has been skipped
feat: add sanitized internal main deployment
2026-08-27 12:49:43 +03:00

142 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
fail() {
printf 'release-service client: %s\n' "$*" >&2
exit 1
}
require_safe_value() {
local name="$1"
local value="$2"
local pattern="$3"
[[ -n "${value}" ]] || fail "${name} is required"
[[ "${value}" =~ ${pattern} ]] || fail "${name} contains unsupported characters"
}
decode_required_file() {
local output_path="$1"
local encoded_value="$2"
local description="$3"
[[ -n "${encoded_value}" ]] || fail "${description} base64 secret is required"
printf '%s' "${encoded_value}" | base64 -d >"${output_path}" || fail \
"${description} base64 secret is invalid"
[[ -s "${output_path}" ]] || fail "${description} decoded to an empty file"
}
[[ $# -ge 3 ]] || fail \
"usage: release-service.sh <service-group> <run-id> <exact-image-ref...> [--refresh-data]"
service_group="$1"
run_id="$2"
shift 2
refresh_data=false
if [[ "${!#}" == "--refresh-data" ]]; then
refresh_data=true
set -- "${@:1:$(($# - 1))}"
fi
case "${service_group}" in
state-corp-backend)
expected_refs=2
;;
state-corp-frontend)
expected_refs=1
;;
*)
fail "unsupported service group: ${service_group}"
;;
esac
[[ $# -eq ${expected_refs} ]] || fail \
"${service_group} requires exactly ${expected_refs} image reference(s)"
require_safe_value "run id" "${run_id}" '^[A-Za-z0-9._:-]+$'
release_host="${RELEASE_HOST:-}"
release_user="${RELEASE_USER:-}"
release_proxy="${RELEASE_PROXY:-}"
release_service_path="${RELEASE_SERVICE_PATH:-}"
require_safe_value "RELEASE_HOST" "${release_host}" '^[A-Za-z0-9._:-]+$'
require_safe_value "RELEASE_USER" "${release_user}" '^[A-Za-z0-9._-]+$'
require_safe_value \
"RELEASE_SERVICE_PATH" "${release_service_path}" '^/[A-Za-z0-9._/-]+$'
case "${release_service_path}" in
/opt/ecos-dev/release-service)
image_ref_pattern='^[A-Za-z0-9._:/-]+:dev-[a-f0-9]{7,40}$'
[[ "${refresh_data}" == false ]] || fail "dev releases cannot refresh data"
;;
/opt/ecos-main/release-service)
image_ref_pattern='^[A-Za-z0-9._:/-]+@sha256:[a-f0-9]{64}$'
;;
/ecos/release-service)
image_ref_pattern='^[A-Za-z0-9._:/-]+@sha256:[a-f0-9]{64}$'
[[ "${refresh_data}" == false ]] || fail "customer releases cannot refresh data"
;;
*)
fail "unsupported RELEASE_SERVICE_PATH"
;;
esac
if [[ "${refresh_data}" == true && "${service_group}" != state-corp-backend ]]; then
fail "only an internal-main backend release can refresh data"
fi
image_refs=("$@")
for image_ref in "${image_refs[@]}"; do
[[ "${image_ref}" =~ ${image_ref_pattern} ]] || fail \
"image reference does not match the selected release target contract"
done
if [[ -n "${release_proxy}" ]]; then
require_safe_value "RELEASE_PROXY" "${release_proxy}" '^[A-Za-z0-9._@:-]+$'
fi
ssh_tmp_dir="$(mktemp -d)"
trap 'rm -rf "${ssh_tmp_dir}"' EXIT
key_path="${ssh_tmp_dir}/deploy-key"
known_hosts_path="${ssh_tmp_dir}/known-hosts"
decode_required_file \
"${key_path}" \
"${RELEASE_SSH_KEY_B64:-}" \
"deploy SSH key"
decode_required_file \
"${known_hosts_path}" \
"${RELEASE_KNOWN_HOSTS_B64:-}" \
"pinned SSH known_hosts"
chmod 0600 "${key_path}" "${known_hosts_path}"
ssh_options=(
-i "${key_path}"
-o BatchMode=yes
-o ConnectTimeout=15
-o GlobalKnownHostsFile=/dev/null
-o IdentitiesOnly=yes
-o LogLevel=ERROR
-o StrictHostKeyChecking=yes
-o "UserKnownHostsFile=${known_hosts_path}"
)
if [[ -n "${release_proxy}" ]]; then
proxy_command="ssh -i ${key_path} -o BatchMode=yes -o ConnectTimeout=15"
proxy_command+=" -o GlobalKnownHostsFile=/dev/null -o IdentitiesOnly=yes"
proxy_command+=" -o LogLevel=ERROR -o StrictHostKeyChecking=yes"
proxy_command+=" -o UserKnownHostsFile=${known_hosts_path}"
proxy_command+=" -W %h:%p ${release_proxy}"
ssh_options+=(-o "ProxyCommand=${proxy_command}")
fi
remote_args=("${service_group}" "${run_id}" "${image_refs[@]}")
if [[ "${refresh_data}" == true ]]; then
remote_args+=(--refresh-data)
fi
printf -v remote_command '%q ' "${release_service_path}" "${remote_args[@]}"
# The command is deliberately assembled and validated on the client.
# shellcheck disable=SC2029
ssh "${ssh_options[@]}" \
"${release_user}@${release_host}" \
"${remote_command}"