-
Notifications
You must be signed in to change notification settings - Fork 521
local emulator image optimization #1318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -176,6 +176,46 @@ prepare_bundle_artifacts() { | |||||||||||||||||||||||
| printf "%s" "$current_ids" > "$bundle_meta" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| contains_provision_marker() { | ||||||||||||||||||||||||
| local provision_log="$1" | ||||||||||||||||||||||||
| local serial_log="$2" | ||||||||||||||||||||||||
| local marker="$3" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if [ -f "$provision_log" ] && grep -Fqx "$marker" "$provision_log" 2>/dev/null; then | ||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if [ -f "$serial_log" ] && LC_ALL=C strings -a "$serial_log" 2>/dev/null | grep -Fqx "$marker" 2>/dev/null; then | ||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| line_count() { | ||||||||||||||||||||||||
| local file="$1" | ||||||||||||||||||||||||
| local count=0 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if [ -f "$file" ]; then | ||||||||||||||||||||||||
| count="$(wc -l < "$file" | tr -d '[:space:]')" || count=0 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| case "$count" in | ||||||||||||||||||||||||
| ''|*[!0-9]*) count=0 ;; | ||||||||||||||||||||||||
| esac | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| printf '%s\n' "$count" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| persist_provision_logs() { | ||||||||||||||||||||||||
| local arch="$1" | ||||||||||||||||||||||||
| local serial_log="$2" | ||||||||||||||||||||||||
| local provision_log="$3" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cp "$serial_log" "$IMAGE_DIR/provision-emulator-${arch}.log" 2>/dev/null || true | ||||||||||||||||||||||||
| cp "$provision_log" "$IMAGE_DIR/provision-emulator-${arch}.progress.log" 2>/dev/null || true | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| build_one() { | ||||||||||||||||||||||||
| local arch="$1" | ||||||||||||||||||||||||
| local base_img="$IMAGE_DIR/debian-${DEBIAN_VERSION}-base-${arch}.qcow2" | ||||||||||||||||||||||||
|
|
@@ -192,8 +232,11 @@ build_one() { | |||||||||||||||||||||||
| local bundle_iso="$tmp_dir/bundle.iso" | ||||||||||||||||||||||||
| local bundle_dir="$tmp_dir/bundle" | ||||||||||||||||||||||||
| local serial_log="$tmp_dir/serial.log" | ||||||||||||||||||||||||
| local provision_log="$tmp_dir/provision.log" | ||||||||||||||||||||||||
| local pidfile="$tmp_dir/qemu.pid" | ||||||||||||||||||||||||
| local qemu_base pid elapsed | ||||||||||||||||||||||||
| local qemu_base pid elapsed total_build_lines | ||||||||||||||||||||||||
| local last_build_lines=0 | ||||||||||||||||||||||||
| local guest_exited=false | ||||||||||||||||||||||||
| local start_time=$SECONDS | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cp "$base_img" "$tmp_img" | ||||||||||||||||||||||||
|
|
@@ -209,21 +252,24 @@ build_one() { | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| mkdir -p "$bundle_dir" | ||||||||||||||||||||||||
| cp "$bundle_tgz" "$bundle_dir/img.tgz" | ||||||||||||||||||||||||
| cp "$BUILD_ENV_FILE" "$bundle_dir/build.env" | ||||||||||||||||||||||||
| make_iso_from_dir "$bundle_iso" "STACKBUNDLE" "$bundle_dir" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| : > "$serial_log" | ||||||||||||||||||||||||
| : > "$provision_log" | ||||||||||||||||||||||||
| qemu_base="$(qemu_cmd_prefix_for_arch "$arch")" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # shellcheck disable=SC2086 | ||||||||||||||||||||||||
| $qemu_base \ | ||||||||||||||||||||||||
| -boot order=c \ | ||||||||||||||||||||||||
| -m "$RAM" \ | ||||||||||||||||||||||||
| -smp "$CPUS" \ | ||||||||||||||||||||||||
| -drive "file=$tmp_img,format=qcow2,if=virtio" \ | ||||||||||||||||||||||||
| -drive "file=$tmp_img,format=qcow2,if=virtio,discard=on,detect-zeroes=unmap" \ | ||||||||||||||||||||||||
| -drive "file=$seed_iso,format=raw,if=virtio,readonly=on" \ | ||||||||||||||||||||||||
| -drive "file=$bundle_iso,format=raw,if=virtio,readonly=on" \ | ||||||||||||||||||||||||
| -netdev user,id=net0 \ | ||||||||||||||||||||||||
| -device virtio-net-pci,netdev=net0 \ | ||||||||||||||||||||||||
| -virtfs "local,path=$tmp_dir,mount_tag=hostfs,security_model=none" \ | ||||||||||||||||||||||||
| -serial "file:$serial_log" \ | ||||||||||||||||||||||||
| -display none \ | ||||||||||||||||||||||||
| -daemonize \ | ||||||||||||||||||||||||
|
|
@@ -232,23 +278,55 @@ build_one() { | |||||||||||||||||||||||
| pid="$(cat "$pidfile")" | ||||||||||||||||||||||||
| elapsed=0 | ||||||||||||||||||||||||
| while [ "$elapsed" -lt "$PROVISION_TIMEOUT" ]; do | ||||||||||||||||||||||||
| if grep -q "STACK_CLOUD_INIT_DONE" "$serial_log" 2>/dev/null; then | ||||||||||||||||||||||||
| if contains_provision_marker "$provision_log" "$serial_log" "STACK_CLOUD_INIT_DONE"; then | ||||||||||||||||||||||||
| break | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if [ -f "$provision_log" ]; then | ||||||||||||||||||||||||
| total_build_lines="$(line_count "$provision_log")" | ||||||||||||||||||||||||
| if [ "$total_build_lines" -gt "$last_build_lines" ]; then | ||||||||||||||||||||||||
| echo "" | ||||||||||||||||||||||||
| sed -n "$((last_build_lines + 1)),${total_build_lines}p" "$provision_log" 2>/dev/null | while IFS= read -r msg; do | ||||||||||||||||||||||||
| if [ "$msg" = "STACK_CLOUD_INIT_DONE" ]; then | ||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| printf " [%3ds] %s\n" "$elapsed" "$msg" | ||||||||||||||||||||||||
| done | ||||||||||||||||||||||||
| last_build_lines="$total_build_lines" | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if ! kill -0 "$pid" 2>/dev/null; then | ||||||||||||||||||||||||
| guest_exited=true | ||||||||||||||||||||||||
| break | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| sleep 5 | ||||||||||||||||||||||||
| elapsed=$((SECONDS - start_time)) | ||||||||||||||||||||||||
| printf "\r [%3ds / %ds] provisioning emulator..." "$elapsed" "$PROVISION_TIMEOUT" | ||||||||||||||||||||||||
| done | ||||||||||||||||||||||||
| echo "" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if ! grep -q "STACK_CLOUD_INIT_DONE" "$serial_log" 2>/dev/null; then | ||||||||||||||||||||||||
| err "Provisioning timed out for emulator (${arch})" | ||||||||||||||||||||||||
| tail -50 "$serial_log" >&2 || true | ||||||||||||||||||||||||
| if ! contains_provision_marker "$provision_log" "$serial_log" "STACK_CLOUD_INIT_DONE"; then | ||||||||||||||||||||||||
| if [ "$guest_exited" = true ]; then | ||||||||||||||||||||||||
| err "Provisioning exited before completion for emulator (${arch})" | ||||||||||||||||||||||||
| else | ||||||||||||||||||||||||
| err "Provisioning timed out for emulator (${arch})" | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if [ -s "$provision_log" ]; then | ||||||||||||||||||||||||
| tail -50 "$provision_log" >&2 || true | ||||||||||||||||||||||||
| else | ||||||||||||||||||||||||
| LC_ALL=C strings -a "$serial_log" 2>/dev/null | tail -50 >&2 || tail -50 "$serial_log" >&2 || true | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if kill -0 "$pid" 2>/dev/null; then | ||||||||||||||||||||||||
| kill "$pid" 2>/dev/null || true | ||||||||||||||||||||||||
| sleep 1 | ||||||||||||||||||||||||
| kill -9 "$pid" 2>/dev/null || true | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| persist_provision_logs "$arch" "$serial_log" "$provision_log" | ||||||||||||||||||||||||
| rm -rf "$tmp_dir" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
@@ -266,19 +344,21 @@ build_one() { | |||||||||||||||||||||||
| kill -9 "$pid" 2>/dev/null || true | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cp "$tmp_img" "$final_img" | ||||||||||||||||||||||||
| cp "$serial_log" "$IMAGE_DIR/provision-emulator-${arch}.log" | ||||||||||||||||||||||||
| rm -rf "$tmp_dir" | ||||||||||||||||||||||||
| persist_provision_logs "$arch" "$serial_log" "$provision_log" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| log "Compressing final image (this may take several minutes)..." | ||||||||||||||||||||||||
| qemu-img convert -p -O qcow2 -c "$final_img" "$final_img.tmp" | ||||||||||||||||||||||||
| mv "$final_img.tmp" "$final_img" | ||||||||||||||||||||||||
| qemu-img convert -p -O qcow2 -c "$tmp_img" "$final_img" | ||||||||||||||||||||||||
| rm -rf "$tmp_dir" | ||||||||||||||||||||||||
|
Comment on lines
358
to
+360
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check the file size and read lines around 358-360
wc -l docker/local-emulator/qemu/build-image.shRepository: stack-auth/stack-auth Length of output: 110 🏁 Script executed: # Read the relevant section
sed -n '350,375p' docker/local-emulator/qemu/build-image.shRepository: stack-auth/stack-auth Length of output: 951 🏁 Script executed: # Search for node invocations and check_deps function
rg -n "node " docker/local-emulator/qemu/build-image.sh | head -20Repository: stack-auth/stack-auth Length of output: 137 🏁 Script executed: # Find the check_deps function to see if node is validated
rg -n "check_deps\|qemu-img\|node" docker/local-emulator/qemu/build-image.sh | head -30Repository: stack-auth/stack-auth Length of output: 47 🏁 Script executed: # Search for check_deps function more broadly
rg -n "check_deps|declare -a|qemu-img" docker/local-emulator/qemu/build-image.shRepository: stack-auth/stack-auth Length of output: 274 🏁 Script executed: # Get the full check for dependencies/requirements
sed -n '1,100p' docker/local-emulator/qemu/build-image.sh | rg -n "check|qemu|curl|docker|gzip|node"Repository: stack-auth/stack-auth Length of output: 453 🏁 Script executed: # Search for any validation of dependencies in the entire file
rg -B2 -A5 "command -v|which " docker/local-emulator/qemu/build-image.shRepository: stack-auth/stack-auth Length of output: 610 Avoid writing the final qcow2 in place. Line 359 converts directly into Additionally, Suggested fix- qemu-img convert -p -O qcow2 -c "$tmp_img" "$final_img"
- rm -rf "$tmp_dir"
+ local tmp_final="${final_img}.tmp"
+ rm -f "$tmp_final"
+ qemu-img convert -p -O qcow2 -c "$tmp_img" "$tmp_final"
+ mv "$tmp_final" "$final_img"
+ rm -rf "$tmp_dir"Also add 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| local size | ||||||||||||||||||||||||
| size="$(du -h "$final_img" | cut -f1)" | ||||||||||||||||||||||||
| log "━━━ Emulator image ready: $final_img (${size}) ━━━" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| log "Generating emulator build env file..." | ||||||||||||||||||||||||
| node "$REPO_ROOT/docker/local-emulator/generate-env-development.mjs" | ||||||||||||||||||||||||
| BUILD_ENV_FILE="$REPO_ROOT/docker/local-emulator/.env.development" | ||||||||||||||||||||||||
|
Comment on lines
+367
to
+369
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's examine the script file around lines 367-369
cat -n docker/local-emulator/qemu/build-image.sh | sed -n '360,375p'Repository: stack-auth/stack-auth Length of output: 718 🏁 Script executed: # Now let's find the check_deps() function
rg -A 15 "check_deps\(\)" docker/local-emulator/qemu/build-image.shRepository: stack-auth/stack-auth Length of output: 576 🏁 Script executed: # Let's also search for all occurrences of "node" in the script to see if it's used elsewhere
rg -n "node" docker/local-emulator/qemu/build-image.shRepository: stack-auth/stack-auth Length of output: 137 Add This script unconditionally calls Suggested fix- for cmd in qemu-img curl docker gzip; do
+ for cmd in qemu-img curl docker gzip node; do
command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd")
done🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| for arch in "${TARGET_ARCHS[@]}"; do | ||||||||||||||||||||||||
| local_base="$IMAGE_DIR/debian-${DEBIAN_VERSION}-base-${arch}.qcow2" | ||||||||||||||||||||||||
| download_cloud_image "$arch" "$local_base" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cpfailure creates an emptynode_modules.standaloneThe
2>/dev/null || mkdir -p /app/node_modules.standalonefallback means that if thecp -asilently fails (e.g. disk full, source missing), the build continues with an emptynode_modules.standalone. Whenslim-docker-imagelater swaps it in, the container would have no node_modules and fail at runtime — likely caught only by the smoke test. Consider removing the suppression and fallback to surface the failure fast:Prompt To Fix With AI