From e926c5e8c8cba6cafffaee49acac2454a8b88470 Mon Sep 17 00:00:00 2001 From: "E. S." Date: Sun, 18 May 2025 00:33:25 +0300 Subject: [PATCH] deploy: incremental static rebuilds --- Makefile | 2 +- config.yaml.example | 1 + deploy/deploy.sh | 14 ++- deploy/static.sh | 15 ++- deploy/static_incremental.sh | 84 +++++++++++++ deploy/util/build_common.sh | 13 +- deploy/util/detect_changes.sh | 75 +++++++++++ deploy/util/gen_runtime_config.php | 11 +- .../util/gen_runtime_config_incremental.php | 117 ++++++++++++++++++ deploy/util/get_projects.sh | 25 ++++ 10 files changed, 341 insertions(+), 16 deletions(-) create mode 100755 deploy/static_incremental.sh create mode 100755 deploy/util/detect_changes.sh mode change 100644 => 100755 deploy/util/gen_runtime_config.php create mode 100755 deploy/util/gen_runtime_config_incremental.php create mode 100755 deploy/util/get_projects.sh diff --git a/Makefile b/Makefile index 45a2909..654b3e8 100644 --- a/Makefile +++ b/Makefile @@ -11,4 +11,4 @@ deploy: ./deploy/deploy.sh static: - ./deploy/static.sh -o "$$(realpath .)" + ./deploy/static_incremental.sh -o "$$(realpath .)" diff --git a/config.yaml.example b/config.yaml.example index 31990d7..c6aefd3 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -3,6 +3,7 @@ cookie_host: .example.org admin_email: admin@example.org ic_email: idb.kniga@gmail.com +projects: ['ic', 'foreignone', 'omnia'] default_project: 'foreignone' subdomains: dev: foreignone diff --git a/deploy/deploy.sh b/deploy/deploy.sh index badda38..3014693 100755 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -1,18 +1,22 @@ #!/bin/sh die() { - >&2 echo "error: $@" + >&2 echo "error: $*" exit 1 } set -e +if ! command -v yq >/dev/null 2>&1; then + die "yq is not installed. Please install yq to parse YAML files." +fi + DIR=$(cd "$(dirname "$(readlink -f "$0")")" && pwd) DEV_DIR="$(realpath "$DIR/../")" STAGING_DIR="$HOME/staging" PROD_DIR="$HOME/www" -REPO_URI=$(cat "$DEV_DIR/config.yaml" | grep ^git_repo | head -1 | awk '{print $2}') +REPO_URI=$(yq -r '.git_repo' "$DEV_DIR/config.yaml") [ "$(hostname)" = "4in1" ] || die "unexpected hostname. are you sure you're running it on the right machine?" @@ -29,6 +33,10 @@ if [ ! -d .git ]; then fi git reset --hard + +# Store the current commit hash before pulling +PREV_COMMIT=$(git rev-parse HEAD) + git pull origin master composer install --no-dev --optimize-autoloader --ignore-platform-reqs @@ -39,7 +47,7 @@ if [ ! -d node_modules ]; then fi cp "$DEV_DIR/config.yaml" . -"$DIR/static.sh" -o "$STAGING_DIR" +"$DIR/static_incremental.sh" -o "$STAGING_DIR" -p "$PREV_COMMIT" cd "$DIR" diff --git a/deploy/static.sh b/deploy/static.sh index df8f636..51649af 100755 --- a/deploy/static.sh +++ b/deploy/static.sh @@ -1,12 +1,16 @@ #!/bin/sh die() { - >&2 echo "error: $@" + >&2 echo "error: $*" exit 1 } set -e +if ! command -v yq >/dev/null 2>&1; then + die "yq is not installed. Please install yq to parse YAML files." +fi + PHP="$(which php)" SCRIPT_DIR=$(cd "$(dirname "$(readlink -f "$0")")" && pwd) APP_DIR="$(realpath "$SCRIPT_DIR/../")" @@ -22,11 +26,12 @@ while [ $# -gt 0 ]; do done [ -z "$OUTPUT_ROOT_DIR" ] && die "you must specify output directory" -for project in ic foreignone omnia; do - "$SCRIPT_DIR"/util/build_css.sh -i "$APP_DIR/public/$project/scss" -o "$OUTPUT_ROOT_DIR/public/$project/dist-css" || die "build_css failed" - "$SCRIPT_DIR"/util/build_js.sh -i "$APP_DIR/public/common/js" -o "$OUTPUT_ROOT_DIR/public/$project/dist-js" || die "build_js failed" +PROJECTS=$("$SCRIPT_DIR"/util/get_projects.sh "$APP_DIR/config.yaml") +for project in $PROJECTS; do + "$SCRIPT_DIR"/util/build_css.sh -i "$APP_DIR/public/$project/scss" -o "$OUTPUT_ROOT_DIR/public/$project/dist-css" || die "build_css failed" + "$SCRIPT_DIR"/util/build_js.sh -i "$APP_DIR/public/common/js" -o "$OUTPUT_ROOT_DIR/public/$project/dist-js" || die "build_js failed" $PHP "$SCRIPT_DIR"/util/gen_runtime_config.php \ --app-root "$OUTPUT_ROOT_DIR" \ - --commit-hash "$(git rev-parse --short=8 HEAD)" \ + --commit-hash "$(git rev-parse HEAD)" \ > "$OUTPUT_ROOT_DIR/config-runtime.php" || die "gen_runtime_config failed" done diff --git a/deploy/static_incremental.sh b/deploy/static_incremental.sh new file mode 100755 index 0000000..0c887a1 --- /dev/null +++ b/deploy/static_incremental.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +die() { + >&2 echo "error: $*" + exit 1 +} + +set -e + +PHP="$(which php)" +SCRIPT_DIR=$(cd "$(dirname "$(readlink -f "$0")")" && pwd) +APP_DIR="$(realpath "$SCRIPT_DIR/../")" +OUTPUT_ROOT_DIR= +PREV_COMMIT= + +usage() { + cat < "$OUTPUT_ROOT_DIR/config-runtime.php.new" || die "gen_runtime_config_incremental failed" + mv "$OUTPUT_ROOT_DIR/config-runtime.php.new" "$OUTPUT_ROOT_DIR/config-runtime.php" + exit 0 +fi + +echo "Rebuilding static for projects: $CHANGED_PROJECTS" + +# Create output directories if they don't exist +for project in $CHANGED_PROJECTS; do + mkdir -p "$OUTPUT_ROOT_DIR/public/$project/dist-css" + mkdir -p "$OUTPUT_ROOT_DIR/public/$project/dist-js" +done + +# Build static for changed projects +for project in $CHANGED_PROJECTS; do + echo "Building CSS for $project..." + "$SCRIPT_DIR"/util/build_css.sh -i "$APP_DIR/public/$project/scss" -o "$OUTPUT_ROOT_DIR/public/$project/dist-css" || die "build_css failed for $project" + + echo "Building JS for $project..." + "$SCRIPT_DIR"/util/build_js.sh -i "$APP_DIR/public/common/js" -o "$OUTPUT_ROOT_DIR/public/$project/dist-js" || die "build_js failed for $project" +done + +# Generate runtime config with integrity hashes +echo "Generating runtime config..." +$PHP "$SCRIPT_DIR"/util/gen_runtime_config_incremental.php \ + --app-root "$OUTPUT_ROOT_DIR" \ + --commit-hash "$CURR_COMMIT" \ + --changed-projects "$CHANGED_PROJECTS" \ + --config-file "$OUTPUT_ROOT_DIR/config-runtime.php" \ + > "$OUTPUT_ROOT_DIR/config-runtime.php.new" || die "gen_runtime_config_incremental failed" +mv "$OUTPUT_ROOT_DIR/config-runtime.php.new" "$OUTPUT_ROOT_DIR/config-runtime.php" + +echo "Static build completed successfully" diff --git a/deploy/util/build_common.sh b/deploy/util/build_common.sh index 90de471..223b061 100755 --- a/deploy/util/build_common.sh +++ b/deploy/util/build_common.sh @@ -5,17 +5,18 @@ set -e INDIR= OUTDIR= +PRESERVE_OUTPUT=0 error() { - >&2 echo "error: $@" + >&2 echo "error: $*" } warning() { - >&2 echo "warning: $@" + >&2 echo "warning: $*" } die() { - error "$@" + error "$*" exit 1 } @@ -28,6 +29,7 @@ usage: $PROGNAME [OPTIONS] Options: -o output directory -i input directory + -p preserve output directory (don't clear it) -h show this help EOF exit "$code" @@ -39,6 +41,7 @@ input_args() { case $1 in -o) OUTDIR="$2"; shift ;; -i) INDIR="$2"; shift ;; + -p) PRESERVE_OUTPUT=1 ;; -h) usage ;; *) die "unexpected argument: $1" ;; esac @@ -57,7 +60,7 @@ check_args() { } if [ ! -d "$OUTDIR" ]; then mkdir "$OUTDIR" - else + elif [ "$PRESERVE_OUTPUT" -eq 0 ]; then find "$OUTDIR" -mindepth 1 -delete fi -} \ No newline at end of file +} diff --git a/deploy/util/detect_changes.sh b/deploy/util/detect_changes.sh new file mode 100755 index 0000000..c2e7342 --- /dev/null +++ b/deploy/util/detect_changes.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +# This script detects changes in static files after git pull +# It outputs a list of subprojects that need to be rebuilt + +set -e + +die() { + >&2 echo "error: $*" + exit 1 +} + +if ! command -v yq >/dev/null 2>&1; then + die "yq is not installed. Please install yq to parse YAML files." +fi + +APP_DIR= +PREV_COMMIT= +CURR_COMMIT= + +usage() { + cat < [] ]; -foreach (['ic', 'foreignone', 'omnia'] as $project) { +foreach ($config['projects'] as $project) { foreach (['js', 'css'] as $type) { $dist_dir = $app_root.'/public/'.$project.'/dist-'.$type; $entries = glob_recursive($dist_dir.'/*.'.$type); @@ -81,4 +88,4 @@ function glob_recursive(string $pattern, int $flags = 0): array { $files = array_merge($files, glob_recursive($dir.'/'.basename($pattern), $flags)); } return $files; -} \ No newline at end of file +} diff --git a/deploy/util/gen_runtime_config_incremental.php b/deploy/util/gen_runtime_config_incremental.php new file mode 100755 index 0000000..423d5b3 --- /dev/null +++ b/deploy/util/gen_runtime_config_incremental.php @@ -0,0 +1,117 @@ +#!/usr/bin/env php + $commit_hash, + 'assets' => [] +]; + +if (!empty($config_file) && file_exists($config_file)) { + $existing_hashes = include $config_file; + if (is_array($existing_hashes) && isset($existing_hashes['assets'])) { + $hashes['assets'] = $existing_hashes['assets']; + } +} + +// Process only changed projects or all if none specified +$projects = !empty($changed_projects) ? $changed_projects : $all_projects; + +foreach ($projects as $project) { + foreach (['js', 'css'] as $type) { + $dist_dir = $app_root.'/public/'.$project.'/dist-'.$type; + $entries = glob_recursive($dist_dir.'/*.'.$type); + if (empty($entries)) { + fwrite(STDERR, "warning: no files found in $dist_dir\n"); + continue; + } + foreach ($entries as $file) { + $asset_key = $type.'/'.basename($file); + $hashes['assets'][$project][$asset_key] = [ + 'integrity' => [] + ]; + foreach (\engine\skin\FeaturedSkin::RESOURCE_INTEGRITY_HASHES as $hash_type) { + $hashes['assets'][$project][$asset_key]['integrity'][$hash_type] = base64_encode(hash_file($hash_type, $file, true)); + } + } + } +} + +echo "&2 echo "error: $*" + exit 1 +} + +if ! command -v yq >/dev/null 2>&1; then + die "yq is not installed. Please install yq to parse YAML files." +fi + +CONFIG_FILE="$1" +[ -z "$CONFIG_FILE" ] && die "config file not specified" +[ -f "$CONFIG_FILE" ] || die "config file not found: $CONFIG_FILE" + +# Parse projects list from config.yaml +PROJECTS=$(yq -r '.projects | join(" ")' "$CONFIG_FILE") + +# Check if projects list is empty +[ -z "$PROJECTS" ] && die "projects list is empty in $CONFIG_FILE" + +echo "$PROJECTS" \ No newline at end of file