From 520be250955e528145d37d704c8615380e751d92 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 22 Mar 2026 14:11:47 +0300 Subject: [PATCH] Remove obsolete OSV scan script and add new CI scripts for Docker, Java, Node.js, Python, and Task setup --- scripts/ci/checkout.sh | 38 +++++++++++++++ scripts/ci/docker-tags.sh | 43 +++++++++++++++++ scripts/ci/setup-docker.sh | 24 ++++++++++ scripts/ci/setup-java.sh | 65 +++++++++++++++++++++++++ scripts/ci/setup-node.sh | 50 ++++++++++++++++++++ scripts/ci/setup-pnpm.sh | 11 +++++ scripts/ci/setup-python.sh | 97 ++++++++++++++++++++++++++++++++++++++ scripts/ci/setup-task.sh | 37 +++++++++++++++ scripts/osv_scan.sh | 38 --------------- 9 files changed, 365 insertions(+), 38 deletions(-) create mode 100755 scripts/ci/checkout.sh create mode 100755 scripts/ci/docker-tags.sh create mode 100755 scripts/ci/setup-docker.sh create mode 100755 scripts/ci/setup-java.sh create mode 100755 scripts/ci/setup-node.sh create mode 100755 scripts/ci/setup-pnpm.sh create mode 100755 scripts/ci/setup-python.sh create mode 100755 scripts/ci/setup-task.sh delete mode 100644 scripts/osv_scan.sh diff --git a/scripts/ci/checkout.sh b/scripts/ci/checkout.sh new file mode 100755 index 0000000..5c2fa39 --- /dev/null +++ b/scripts/ci/checkout.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# Clone and checkout using Gitea Actions environment variables. +# Source: self-contained, no third-party dependencies. +# +# Usage: checkout.sh [fetch_depth] +# fetch_depth: number of commits (default 1), or 0 for full history. +# +# Required env: GITEA_SERVER_URL, GITEA_REPOSITORY, GITHUB_SHA +# Optional env: GITEA_TOKEN (for private repos), GITHUB_WORKSPACE +set -eu + +FETCH_DEPTH="${1:-1}" +SERVER="${GITEA_SERVER_URL:-${GITHUB_SERVER_URL:?GITEA_SERVER_URL not set}}" +REPO="${GITEA_REPOSITORY:-${GITHUB_REPOSITORY:?GITEA_REPOSITORY not set}}" +SHA="${GITHUB_SHA:?GITHUB_SHA not set}" +TOKEN="${GITEA_TOKEN:-${GITHUB_TOKEN:-}}" +WORKSPACE="${GITHUB_WORKSPACE:-.}" + +cd "$WORKSPACE" + +if [ -n "$TOKEN" ]; then + git config --global credential.helper \ + "!f() { echo username=x-access-token; echo \"password=${TOKEN}\"; }; f" +fi + +ORIGIN="${SERVER}/${REPO}.git" + +if [ "$FETCH_DEPTH" = "0" ]; then + git clone -q "$ORIGIN" . +else + git init -q + git remote add origin "$ORIGIN" + git fetch -q --depth="$FETCH_DEPTH" origin "$SHA" +fi + +git checkout -q "$SHA" 2>/dev/null || git checkout -q FETCH_HEAD + +echo "Checked out ${REPO} at $(git rev-parse --short HEAD)" diff --git a/scripts/ci/docker-tags.sh b/scripts/ci/docker-tags.sh new file mode 100755 index 0000000..0d5a66a --- /dev/null +++ b/scripts/ci/docker-tags.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Generate Docker image tags from git context. +# +# Usage: docker-tags.sh [output_file] +# Environment: GITEA_REF / GITHUB_REF, GITEA_REF_NAME / GITHUB_REF_NAME +# +# The output file contains one `-t registry/image:tag` per line, +# suitable for passing directly to `docker buildx build`. +set -eu + +IMAGE="$1" +OUTPUT="${2:-/tmp/docker-tags.txt}" +: > "$OUTPUT" + +SHA="$(git rev-parse --short HEAD)" +REF="${GITEA_REF:-${GITHUB_REF:-}}" +BRANCH="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-$(git rev-parse --abbrev-ref HEAD)}}" + +echo "-t ${IMAGE}:sha-${SHA}" >> "$OUTPUT" + +case "$BRANCH" in + master|main) + echo "-t ${IMAGE}:latest" >> "$OUTPUT" + ;; +esac + +case "$REF" in + refs/tags/v*) + VERSION="${REF#refs/tags/v}" + echo "-t ${IMAGE}:${VERSION}" >> "$OUTPUT" + MAJOR_MINOR="$(echo "$VERSION" | cut -d. -f1-2)" + if [ "$MAJOR_MINOR" != "$VERSION" ]; then + echo "-t ${IMAGE}:${MAJOR_MINOR}" >> "$OUTPUT" + fi + ;; + refs/tags/*) + TAG="${REF#refs/tags/}" + echo "-t ${IMAGE}:${TAG}" >> "$OUTPUT" + ;; +esac + +echo "Generated tags:" +cat "$OUTPUT" diff --git a/scripts/ci/setup-docker.sh b/scripts/ci/setup-docker.sh new file mode 100755 index 0000000..6e2ba12 --- /dev/null +++ b/scripts/ci/setup-docker.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# Set up QEMU and Docker Buildx for multi-platform builds. +# Optionally log in to a container registry. +# +# Usage: setup-docker.sh [registry] [username] [password] +# or set REGISTRY, REGISTRY_USERNAME, REGISTRY_PASSWORD env vars. +set -eu + +REGISTRY="${1:-${REGISTRY:-}}" +USERNAME="${2:-${REGISTRY_USERNAME:-}}" +PASSWORD="${3:-${REGISTRY_PASSWORD:-}}" + +echo "Registering QEMU binfmt handlers" +sudo apt-get update -qq +sudo apt-get install -y -qq qemu-user-static binfmt-support + +echo "Creating Docker Buildx builder" +docker buildx create --name multiarch --driver docker-container --use +docker buildx inspect --bootstrap + +if [ -n "$REGISTRY" ] && [ -n "$USERNAME" ] && [ -n "$PASSWORD" ]; then + echo "Logging in to ${REGISTRY}" + echo "$PASSWORD" | docker login "$REGISTRY" -u "$USERNAME" --password-stdin +fi diff --git a/scripts/ci/setup-java.sh b/scripts/ci/setup-java.sh new file mode 100755 index 0000000..d22c2ba --- /dev/null +++ b/scripts/ci/setup-java.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Install Adoptium Temurin JDK with SHA256 verification. +# Uses the Adoptium assets API to obtain the checksum and download URL +# directly from the source, avoiding opaque redirects. +# Source: https://adoptium.net (Eclipse Foundation) +# Usage: setup-java.sh [major_version] +set -eu + +JAVA_VERSION="${1:-17}" + +ARCH="$(uname -m)" +case "$ARCH" in + x86_64) ARCH="x64" ;; + aarch64) ARCH="aarch64" ;; + *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; +esac + +echo "Installing Temurin JDK ${JAVA_VERSION} (${ARCH})" + +ASSETS_API="https://api.adoptium.net/v3/assets/latest/${JAVA_VERSION}/hotspot?architecture=${ARCH}&image_type=jdk&os=linux&vendor=eclipse" +API_JSON="$(curl -fsSL "$ASSETS_API")" + +DOWNLOAD_URL="$(echo "$API_JSON" | sed -n 's/.*"link" *: *"\([^"]*\)".*/\1/p' | head -1)" +CHECKSUM_URL="$(echo "$API_JSON" | sed -n 's/.*"checksum_link" *: *"\([^"]*\)".*/\1/p' | head -1)" +EXPECTED="$(echo "$API_JSON" | sed -n 's/.*"checksum" *: *"\([0-9a-f]*\)".*/\1/p' | head -1)" + +if [ -z "$DOWNLOAD_URL" ] || [ -z "$EXPECTED" ]; then + echo "Failed to resolve Temurin JDK ${JAVA_VERSION} for ${ARCH}" >&2 + exit 1 +fi + +curl -fsSL "$DOWNLOAD_URL" -o /tmp/jdk.tar.gz + +ACTUAL="$(sha256sum /tmp/jdk.tar.gz | cut -d' ' -f1)" +if [ "$EXPECTED" != "$ACTUAL" ]; then + echo "SHA256 verification failed for JDK ${JAVA_VERSION}" >&2 + echo " expected: ${EXPECTED}" >&2 + echo " got: ${ACTUAL}" >&2 + rm -f /tmp/jdk.tar.gz + exit 1 +fi +echo "SHA256 verified: ${ACTUAL}" + +if [ -n "$CHECKSUM_URL" ]; then + EXPECTED_LINK="$(curl -fsSL "$CHECKSUM_URL" | cut -d' ' -f1)" + if [ "$EXPECTED_LINK" != "$ACTUAL" ]; then + echo "Cross-check against checksum_link also failed" >&2 + rm -f /tmp/jdk.tar.gz + exit 1 + fi + echo "Cross-verified against checksum_link" +fi + +sudo mkdir -p /opt/java +sudo tar -xzf /tmp/jdk.tar.gz -C /opt/java --strip-components=1 +rm -f /tmp/jdk.tar.gz + +CI_ENV="${GITEA_ENV:-${GITHUB_ENV:-/dev/null}}" +CI_PATH="${GITEA_PATH:-${GITHUB_PATH:-/dev/null}}" +echo "JAVA_HOME=/opt/java" >> "$CI_ENV" +echo "/opt/java/bin" >> "$CI_PATH" + +export JAVA_HOME=/opt/java +export PATH="/opt/java/bin:$PATH" +java -version diff --git a/scripts/ci/setup-node.sh b/scripts/ci/setup-node.sh new file mode 100755 index 0000000..5625d5d --- /dev/null +++ b/scripts/ci/setup-node.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# Install Node.js from the official binary distribution with SHA256 verification. +# Source: https://nodejs.org +# Usage: setup-node.sh [major_version] +set -eu + +NODE_MAJOR="${1:-24}" + +ARCH="$(uname -m)" +case "$ARCH" in + x86_64) ARCH="x64" ;; + aarch64) ARCH="arm64" ;; + armv7l) ARCH="armv7l" ;; + *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; +esac + +DIST_URL="https://nodejs.org/dist/latest-v${NODE_MAJOR}.x" + +curl -fsSL "${DIST_URL}/SHASUMS256.txt" -o /tmp/node-shasums.txt + +VERSION="$(grep -o "node-v[0-9.]*-linux-${ARCH}" /tmp/node-shasums.txt \ + | head -1 \ + | sed "s/-linux-${ARCH}//" \ + | sed 's/node-//')" + +if [ -z "$VERSION" ]; then + echo "Failed to resolve Node.js v${NODE_MAJOR} for ${ARCH}" >&2 + exit 1 +fi + +TARBALL="node-${VERSION}-linux-${ARCH}.tar.xz" +echo "Installing Node.js ${VERSION} (${ARCH})" +curl -fsSL "${DIST_URL}/${TARBALL}" -o /tmp/node.tar.xz + +EXPECTED="$(grep " ${TARBALL}\$" /tmp/node-shasums.txt | cut -d' ' -f1)" +ACTUAL="$(sha256sum /tmp/node.tar.xz | cut -d' ' -f1)" +if [ -z "$EXPECTED" ] || [ "$EXPECTED" != "$ACTUAL" ]; then + echo "SHA256 verification failed for ${TARBALL}" >&2 + echo " expected: ${EXPECTED}" >&2 + echo " got: ${ACTUAL}" >&2 + rm -f /tmp/node.tar.xz /tmp/node-shasums.txt + exit 1 +fi +echo "SHA256 verified: ${ACTUAL}" + +sudo tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 +rm -f /tmp/node.tar.xz /tmp/node-shasums.txt + +node --version +npm --version diff --git a/scripts/ci/setup-pnpm.sh b/scripts/ci/setup-pnpm.sh new file mode 100755 index 0000000..ce2ddba --- /dev/null +++ b/scripts/ci/setup-pnpm.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Activate pnpm via corepack. +# Usage: setup-pnpm.sh [version] +set -eu + +PNPM_VERSION="${1:-10.30.0}" + +corepack enable +corepack prepare "pnpm@${PNPM_VERSION}" --activate + +pnpm --version diff --git a/scripts/ci/setup-python.sh b/scripts/ci/setup-python.sh new file mode 100755 index 0000000..03ee371 --- /dev/null +++ b/scripts/ci/setup-python.sh @@ -0,0 +1,97 @@ +#!/bin/sh +# Build and install Python from official python.org source with GPG verification. +# Source: https://www.python.org +# +# Usage: setup-python.sh [version] +# version: exact (3.13.9) or minor (3.13, resolved to latest patch). +set -eu + +PY_INPUT="${1:-3.13}" + +CURRENT="$(python3 --version 2>/dev/null | sed 's/Python //')" || true + +case "$PY_INPUT" in + *.*.*) + PY_VERSION="$PY_INPUT" + ;; + *) + echo "Resolving latest Python ${PY_INPUT}.x from python.org" + PY_VERSION="$(curl -fsSL "https://www.python.org/ftp/python/" \ + | grep -o "href=\"${PY_INPUT}\.[0-9]*/" \ + | sed 's/href="//;s/\///' \ + | sort -t. -k3 -n \ + | tail -1)" + if [ -z "$PY_VERSION" ]; then + echo "Failed to resolve Python ${PY_INPUT}.x" >&2 + exit 1 + fi + echo "Resolved to ${PY_VERSION}" + ;; +esac + +if [ "$CURRENT" = "$PY_VERSION" ]; then + echo "Python ${PY_VERSION} already installed" + python3 --version + exit 0 +fi + +echo "Building Python ${PY_VERSION} from source (python.org)" + +sudo apt-get update -qq +sudo apt-get install -y -qq \ + build-essential gnupg curl \ + libssl-dev zlib1g-dev libbz2-dev libreadline-dev \ + libsqlite3-dev libffi-dev liblzma-dev libncurses-dev > /dev/null 2>&1 + +TARBALL="Python-${PY_VERSION}.tar.xz" +SRC_URL="https://www.python.org/ftp/python/${PY_VERSION}/${TARBALL}" +SIG_URL="${SRC_URL}.asc" + +curl -fsSL "$SRC_URL" -o "/tmp/${TARBALL}" +curl -fsSL "$SIG_URL" -o "/tmp/${TARBALL}.asc" + +GPG_KEYS="" +case "$PY_VERSION" in + 3.13.*|3.14.*) + GPG_KEYS="A035C8C19219BA821ECEA86B64E628F8D684696D 7169605F62C751356D054A26A821E680E5FA6305" + ;; + 3.11.*|3.12.*) + GPG_KEYS="A035C8C19219BA821ECEA86B64E628F8D684696D 7169605F62C751356D054A26A821E680E5FA6305" + ;; + 3.9.*|3.10.*) + GPG_KEYS="E3FF2839C048B25C084DEBE9B26995E310250568" + ;; + *) + echo "No known GPG key for Python ${PY_VERSION}; skipping signature check" >&2 + ;; +esac + +if [ -n "$GPG_KEYS" ]; then + export GNUPGHOME="$(mktemp -d)" + for key in $GPG_KEYS; do + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" 2>/dev/null || \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" 2>/dev/null || true + done + gpg --batch --verify "/tmp/${TARBALL}.asc" "/tmp/${TARBALL}" + rm -rf "$GNUPGHOME" + unset GNUPGHOME + echo "GPG signature verified" +fi + +cd /tmp +tar -xJf "${TARBALL}" +cd "Python-${PY_VERSION}" + +BUILD_LOG="/tmp/python-build.log" +./configure --prefix=/usr/local --with-ensurepip=install > "$BUILD_LOG" 2>&1 +make -j"$(nproc)" >> "$BUILD_LOG" 2>&1 +sudo make install >> "$BUILD_LOG" 2>&1 + +sudo ln -sf /usr/local/bin/python3 /usr/local/bin/python +sudo ln -sf /usr/local/bin/pip3 /usr/local/bin/pip + +cd / +rm -rf "/tmp/${TARBALL}" "/tmp/${TARBALL}.asc" "/tmp/Python-${PY_VERSION}" "$BUILD_LOG" + +python3 --version +pip3 --version diff --git a/scripts/ci/setup-task.sh b/scripts/ci/setup-task.sh new file mode 100755 index 0000000..f2534e5 --- /dev/null +++ b/scripts/ci/setup-task.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Install go-task from GitHub releases with SHA256 verification. +# Source: https://github.com/go-task/task +# Usage: setup-task.sh [version] +set -eu + +TASK_VERSION="${1:-3.49.1}" + +ARCH="$(uname -m)" +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; +esac + +BASE_URL="https://github.com/go-task/task/releases/download/v${TASK_VERSION}" +TARBALL="task_linux_${ARCH}.tar.gz" + +echo "Installing Task v${TASK_VERSION} (${ARCH})" +curl -fsSL "${BASE_URL}/task_checksums.txt" -o /tmp/task-checksums.txt +curl -fsSL "${BASE_URL}/${TARBALL}" -o /tmp/task.tar.gz + +EXPECTED="$(grep " ${TARBALL}\$" /tmp/task-checksums.txt | cut -d' ' -f1)" +ACTUAL="$(sha256sum /tmp/task.tar.gz | cut -d' ' -f1)" +if [ -z "$EXPECTED" ] || [ "$EXPECTED" != "$ACTUAL" ]; then + echo "SHA256 verification failed for ${TARBALL}" >&2 + echo " expected: ${EXPECTED}" >&2 + echo " got: ${ACTUAL}" >&2 + rm -f /tmp/task.tar.gz /tmp/task-checksums.txt + exit 1 +fi +echo "SHA256 verified: ${ACTUAL}" + +sudo tar -xzf /tmp/task.tar.gz -C /usr/local/bin task +rm -f /tmp/task.tar.gz /tmp/task-checksums.txt + +task --version diff --git a/scripts/osv_scan.sh b/scripts/osv_scan.sh deleted file mode 100644 index d62e1f0..0000000 --- a/scripts/osv_scan.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -OSV_VERSION="${OSV_VERSION:-v2.3.1}" - -echo "Installing OSV-Scanner ${OSV_VERSION}..." -curl -sSL "https://github.com/google/osv-scanner/releases/download/${OSV_VERSION}/osv-scanner_linux_amd64" -o /tmp/osv-scanner -chmod +x /tmp/osv-scanner -sudo mv /tmp/osv-scanner /usr/local/bin/osv-scanner - -echo "Running OSV-Scanner recursively..." -OSV_JSON="$(mktemp)" -trap 'rm -f "$OSV_JSON"' EXIT - -osv-scanner --recursive ./ --format json > "$OSV_JSON" || true - -if ! command -v jq >/dev/null 2>&1; then - echo "Error: jq is not installed. Please install jq to parse OSV results." - exit 1 -fi - -VULNS=$(jq -r ' - .results[]? | - .source as $src | - .vulns[]? | - "\(.id) (source: \($src))" -' "$OSV_JSON") - -if [ -n "$VULNS" ]; then - echo "OSV scan found vulnerabilities:" - echo "$VULNS" | while IFS= read -r line; do - echo " - $line" - done - exit 1 -else - echo "OSV scan: no vulnerabilities found." -fi -