pixels-install
Help the user install a basic Pixels deployment according to docs/INSTALL.md.
Treat docs/INSTALL.md as the source of truth for the installation flow. Use the bundled scripts only as helpers for deterministic, repeatable, or easy-to-misconfigure steps. Do not treat this skill as a one-shot unattended installer.
Resource Paths
- Skill directory: use the directory containing this
SKILL.md.
- Helper scripts:
scripts/.
- Shared helper scripts:
shared-scripts/.
- Runtime state:
STATE_DIR when set; otherwise the scripts resolve it to .agents/state/pixels-install for project use or $HOME/.agents/state/pixels-install for global installs.
- Pixels source tree:
REPO_ROOT when set; otherwise scripts try to discover the current Git repository root and fail with a clear message if they cannot.
Helper scripts
Use these helpers when they directly fit the current environment and the user-approved goal:
prepare_deployment.sh: collect and confirm the Pixels deployment topology before any install writes to disk. With no node arguments it prompts for single-node vs. cluster, PIXELS_HOME, the coordinator node, whether the coordinator also runs a worker, and worker nodes; with arguments, use --coordinator <ssh_target,host_ip,host_name,host_user,host_port> plus repeated --worker <...> (legacy --node still works). Writes STATE_DIR/deployment.env and optionally delegates SSH setup to shared-scripts/setup_cluster.sh. In non-interactive mode, set CONFIRM_PIXELS_DEPLOYMENT=true only after the user has reviewed the chosen topology and paths.
check_prerequisites.sh: validate OS, architecture, memory, disk, ports, host resolution, privilege, and optional SSH reachability. Runs every check and exits with a structured === check_prerequisites result === summary (one ok|warn|fail|skip <check>: <detail> line per check, plus a final summary: ok=N warn=N fail=N skip=N status=pass|fail line) instead of stopping at the first failure, so a single run shows every problem at once.
install_jdk.sh: install a Zulu OpenJDK build (default JDK 23) matching the server's CPU architecture (x86_64 or aarch64) under ~/opt, and persist JAVA_HOME only into the current user's shell profile. Looks for a JDK that already satisfies JDK_VERSION first — checking JAVA_HOME if set, otherwise resolving whatever java is on PATH (e.g. an apt-installed JDK that never exported JAVA_HOME) — and if one is found, skips the download entirely and only fixes up the environment variable to point at it. Only downloads/installs a fresh Zulu build when nothing on the system satisfies the version requirement. Falls back to pointing at the manual .deb method in docs/INSTALL.md if the Azul metadata API lookup fails.
install_maven.sh: install or validate Maven 3.8+ (default pinned version 3.9.8; override with MAVEN_VERSION only if the user explicitly asks for a different one) when the current Maven is missing or incompatible with the selected JDK. Same "skip if already satisfied" logic as install_jdk.sh: if an existing mvn (from apt, a prior manual install, etc.) already meets the minimum version, it reuses that installation's home directory instead of downloading anything. Fresh installs go into ~/opt/apache-maven-<version> with a ~/opt/maven symlink, never into /opt.
init_metadata.sh: default metadata setup. After Pixels is installed and pixels.properties points at the metadata database, it runs pixels-cli INIT-META to create the tables. Derby is the default backend (jdbc:derby:$PIXELS_HOME/var/pixels_metadata;create=true); no extra server or JDBC connector is required because Derby is packaged with pixels-cli/pixels-daemon.
install_mysql.sh: optional alternative to Derby. Use only when the user explicitly wants MySQL as the metadata database. Installs MySQL, then interactively confirm the root password and the pixels metadata-DB-user password (default password for both, never applied silently), create the pixels_metadata database/user, and optionally load pixels-daemon/src/main/resources/pixels_metadata_mysql.sql. It validates DB/user identifiers, quotes SQL values, avoids putting the root password in command-line arguments, and writes shell-quoted STATE_DIR/deployment.secrets.env (mode 600) so configure_pixels.sh can set METADATA_DB_TYPE=mysql. Table creation can still be done with INIT-META.
install_etcd.sh: install and optionally start the bundled etcd 3.3.4 package under ~/opt. Always fixes the shipped conf.yml's hardcoded /home/ubuntu/... data-dir to the real install path. By default (ETCD_ALLOW_REMOTE=false) it keeps etcd localhost-only. For a cluster, set ETCD_ALLOW_REMOTE=true only after confirming private networking/security-group rules; the script then requires CONFIRM_ETCD_REMOTE_ACCESS=true, ASSUME_YES=true, or an interactive confirmation before binding the client/peer listeners for remote access. Can also install and enable a systemd unit so etcd survives reboots and restarts on failure, but only after asking — it never installs this silently. Leave INSTALL_ETCD_SYSTEMD_SERVICE unset to be prompted interactively ([y/N], defaults to no); set it to true/false to answer non-interactively once the user has actually agreed, or ASSUME_YES=true to answer yes to this and other yes/no prompts. Declining (or running non-interactively with no explicit answer) just skips the unit — etcd still starts, as a manually backgrounded process instead. Falls back the same way when systemctl isn't available at all. Restarts/re-enables the installed service automatically when the config actually changed.
build_install_pixels.sh: build and install Pixels into the confirmed PIXELS_HOME from deployment.env. It does not download the MySQL JDBC connector unless the user chose MySQL (METADATA_DB_TYPE=mysql or INSTALL_MYSQL_CONNECTOR=true). It prints the repo, deployment file, and install target before running; in non-interactive mode, set CONFIRM_PIXELS_INSTALL=true only after the user confirms the target. If you must bypass the prepared deployment file, pass USE_DEPLOYMENT_FILE=false PIXELS_HOME=<path> explicitly. AUTO_CONFIRM_INSTALL (default true) auto-answers install.sh's prompts only when that is safe; if PIXELS_HOME/etc/pixels.properties or pixels-cpp.properties already exist, it forces the interactive path instead, because install.sh would otherwise prompt to add/remove config options or overwrite pixels-cpp.properties outright — exactly what Claude agent/SKILL.md say not to do without the user's say-so.
configure_pixels.sh: update PIXELS_HOME/etc/pixels.properties after database, etcd, host, port, topology, and path values are known. Defaults to Derby (METADATA_DB_TYPE=derby, URL jdbc:derby:$PIXELS_HOME/var/pixels_metadata;create=true). Automatically sources deployment.env and deployment.secrets.env (if present). It writes $PIXELS_HOME/etc/workers from PIXELS_WORKERS by default. For the MySQL alternative, set METADATA_DB_TYPE=mysql and reuse credentials from install_mysql.sh; it fails instead of silently writing the documented default DB password unless METADATA_DB_PASSWORD is explicit or ALLOW_DEFAULT_METADATA_PASSWORD=true is set.
install_shell_helpers.sh: optional convenience step that asks first before writing shell functions. By default it installs only start_pixels/stop_pixels/restart_pixels on the Pixels coordinator recorded in deployment.env; when invoked from another host, it copies a minimal installer over SSH and writes the coordinator user's ~/.pixels-shell-helpers.sh plus shell-profile source line there. These wrap $PIXELS_HOME/sbin/start-pixels.sh and $PIXELS_HOME/sbin/stop-pixels.sh. Set PIXELS_SHELL_HELPERS_TARGET=local only when the user explicitly wants the functions on the current host. Leave INSTALL_PIXELS_SHELL_HELPERS unset to be prompted [y/N]; set true/false to answer non-interactively once the user has actually agreed, or ASSUME_YES=true.
smoke_test.sh: verify the installed layout, configuration, Java (CHECK_JAVA=true), metadata access, etcd health, Pixels topology (deployment.env plus $PIXELS_HOME/etc/workers), core service ports (CHECK_CORE_SERVICES=true), $PIXELS_HOME/logs error patterns (CHECK_PIXELS_LOGS=true), and basic CLI behavior when applicable. It also checks Trino layout/catalog files when CHECK_TRINO=true and trino-deployment.env is present, can separately validate the lightweight Trino-side Pixels client config plus shell-profile PIXELS_HOME/PIXELS_CONFIG exports (CHECK_TRINO_PIXELS_CLIENT=true) without requiring a full Pixels runtime layout (CHECK_PIXELS_LAYOUT=false), can check Trino launcher status across the recorded cluster (CHECK_TRINO_CLUSTER_STATUS=true), checks that Trino logs did not fall back to classpath pixels.properties (CHECK_TRINO_LOGS=true), waits for /v1/info to report starting=false (CHECK_TRINO_READY=true), and can run trino --catalog pixels --execute "SHOW SCHEMAS" (CHECK_TRINO_CLI=true). SHOW SCHEMAS only needs to succeed; an empty Pixels catalog is expected before data is loaded. Same structured-summary behavior as check_prerequisites.sh above.
progress.sh: records which of skill.yaml's phases have actually completed, in STATE_DIR/progress.env. progress.sh mark <phase> [note] after a phase succeeds, progress.sh show to see what's already done, progress.sh is-done <phase> to check one phase, progress.sh reset to start over. Exists so a session interrupted partway through a multi-phase install can resume from the actual state instead of guessing or re-running completed steps.
build_pixels_trino_artifacts.sh: optional Trino-prep step. Builds pixelsdb/pixels-trino once on the local machine where Pixels modules have already been mvn installed, copies pixels-trino-connector-*.zip and pixels-trino-listener-*.zip into STATE_DIR/pixels-trino-artifacts/, and writes STATE_DIR/pixels-trino-artifacts.env for install_trino_cluster.sh. In non-interactive mode, set CONFIRM_PIXELS_TRINO_ARTIFACTS=true only after the user confirms the checkout/artifact paths.
install_trino_cluster.sh: run on the coordinator after prepare_trino_cluster.sh. It prints and confirms the Trino deployment file, version, install parent, home link, data dir, coordinator, coordinator scheduling mode, workers, Trino-side Pixels client home/config source, Pixels service endpoints, and any prebuilt pixels-trino plugin zips before installing; in non-interactive mode, set CONFIRM_TRINO_CLUSTER_INSTALL=true only after the user confirms the fan-out. Installs Trino locally for the coordinator's own role, then dispatches install_trino.sh on every worker in parallel over the coordinator -> worker passwordless SSH that prepare_trino_cluster.sh already set up, instead of requiring the user or skill to copy the same command and run it by hand once per node. Copies the current STATE_DIR/trino-deployment.env to each worker first (workers don't need a shared filesystem). If PIXELS_TRINO_CONNECTOR_ZIP and PIXELS_TRINO_LISTENER_ZIP are provided, or STATE_DIR/pixels-trino-artifacts.env exists from build_pixels_trino_artifacts.sh, it also copies a minimal installer plus those prebuilt plugin zips to each worker, so workers do not need a Pixels checkout or Maven build. It also copies the selected Trino-side Pixels client config (TRINO_PIXELS_CONFIG_SOURCE, defaulting to $PIXELS_HOME/etc/pixels.properties when available) and lets install_trino.sh rewrite service endpoints through TRINO_PIXELS_* variables. Remote Trino nodes receive Trino, the two pixels-trino plugin zips, Trino catalog/listener configs, and a lightweight ~/opt/pixels/etc/pixels.properties client config — not a full Pixels installation. Emits the same structured per-node summary as smoke_test.sh/check_prerequisites.sh, with a log file per node under STATE_DIR/logs/ for diagnosing any failure.
prepare_trino_cluster.sh: collects and confirms the Trino topology and install locations: one coordinator, zero or more workers, whether the coordinator also accepts scheduled work, Trino version, HTTP port, install parent, home symlink/current path, and data directory. With no node arguments it prompts interactively; with arguments, use --coordinator <ssh_target,host_ip,host_name,host_user,host_port> plus repeated --worker <...>. Writes STATE_DIR/trino-deployment.env. Optionally delegates to shared-scripts/setup_cluster.sh (--setup-ssh true) so the coordinator and every worker can passwordlessly SSH into each other, which the cluster shell helpers below depend on. In non-interactive mode, set CONFIRM_TRINO_DEPLOYMENT=true only after the user has reviewed the topology and paths.
install_trino.sh: installs Trino (per the 466 deployment docs) on the current node only — run it once per node in the cluster, it does not SSH anywhere itself (use install_trino_cluster.sh to fan it out across the cluster instead of running it by hand on each node). It reads trino-deployment.env, validates this node's role (coordinator or worker) and the coordinator host, and confirms the role plus install/data paths before writing files; in non-interactive mode, set CONFIRM_TRINO_INSTALL=true only after the user confirms the target. Downloads trino-server-<version>.tar.gz (default 466) from Maven Central into the confirmed install parent with the confirmed home symlink for version-switch-friendly installs. Writes etc/node.properties (node.data-dir under the confirmed data dir, outside the versioned install dir so it survives version switches; node.id generated once and preserved across re-runs), etc/jvm.config (official defaults plus the two --add-opens flags pixels-trino requires), etc/log.properties, and etc/config.properties — discovery.uri always points at the coordinator's real IP from trino-deployment.env, never localhost; this node's coordinator/worker role is read from trino-deployment.env or inferred from this host's own IPs, overridable with TRINO_ROLE. For pixelsdb/pixels-trino, either clones/builds locally (requires Pixels already mvn installed locally and JDK 23) or consumes prebuilt PIXELS_TRINO_CONNECTOR_ZIP and PIXELS_TRINO_LISTENER_ZIP so remote Trino nodes do not need a Pixels checkout or Maven build. Installs both documented plugins by directly unzipping their zips under plugin/ and preserving the zip's versioned top-level directory names, e.g. pixels-trino-connector-0.2.0-SNAPSHOT and pixels-trino-listener-0.2.0-SNAPSHOT; do not rename them to unversioned stable directories. Writes Trino's catalog file etc/catalog/pixels.properties with only catalog properties (connector.name=pixels, cloud.function.switch=off, clean.intermediate.result=true by default). This catalog file is not the same thing as Pixels' client/runtime config file $PIXELS_HOME/etc/pixels.properties; never copy Pixels runtime properties such as metadata.server.host or etcd.hosts into the Trino catalog. Also prepares the Trino-side lightweight Pixels client home (TRINO_PIXELS_HOME, default $HOME/opt/pixels, not $HOME/opt/pixels/etc) with etc/pixels.properties and logs/, then writes export PIXELS_HOME=... and export PIXELS_CONFIG=$PIXELS_HOME/etc/pixels.properties directly into the current user's bash/zsh profile (and mirrors them into STATE_DIR/toolchain.env for later helper-script processes). It does not create ~/.pixels-trino-env.sh. Use TRINO_PIXELS_CONFIG_SOURCE to copy a known-good Pixels config into that client home, and use TRINO_PIXELS_METADATA_SERVER_HOST, TRINO_PIXELS_TRANS_SERVER_HOST, TRINO_PIXELS_QUERY_SCHEDULE_SERVER_HOST, and TRINO_PIXELS_ETCD_HOSTS to rewrite endpoints so Trino nodes do not fall back to localhost. Writes etc/event-listener.properties for the listener and defaults its log directory to a Trino-side path under the install parent unless PIXELS_TRINO_LISTENER_LOG_DIR is set. Best-effort installs trino-cli into bin/trino.
install_trino_shell_helpers.sh: optional, and always asks first, because it bakes a specific list of remote hostnames (from trino-deployment.env) into a shell profile and assumes passwordless SSH between them is already set up. By default it installs only start_trino_cluster/stop_trino_cluster/restart_trino_cluster/trino_cli on the Trino coordinator recorded in trino-deployment.env; when invoked from another host, it copies a minimal installer over SSH and writes the coordinator user's ~/.trino-shell-helpers.sh plus shell-profile source line there. In the generated functions, the coordinator is always operated locally and every worker over SSH. stop_trino_cluster stops workers before the coordinator; restart_trino_cluster runs stop then start. The generated start/stop/restart/client helpers explicitly export PIXELS_HOME and PIXELS_CONFIG before invoking Trino locally or over SSH, without wrapping or replacing Trino's bin/launcher. Set TRINO_SHELL_HELPERS_TARGET=local only when the user explicitly wants the functions on the current host. Leave INSTALL_TRINO_SHELL_HELPERS unset to be prompted [y/N]; set true/false to answer non-interactively once the user has actually agreed, or ASSUME_YES=true.
All executable entrypoints in this skill are Bash programs. Invoke them directly
so their #!/usr/bin/env bash shebang is honored; do not run them with zsh
or source them into the current shell. Bash must be installed on every
deployment node even when that account's login shell is Zsh. Each entrypoint
validates the deployment account's configured login shell from passwd/NSS and
accepts only Bash or Zsh. Profile exports go to .bashrc or .zshrc
accordingly, while the generated Pixels and Trino helper files are checked in
both Bash and Zsh syntax before they are sourced.
All of these scripts anchor paths on $HOME (e.g. ~/opt/...) and detect the
current account's login shell from passwd/NSS to pick ~/.bashrc or ~/.zshrc before
persisting any export. Never assume the deployment user is named pixels
or ubuntu, and never write environment variables into a global file such
as /etc/environment — scripts/lib/shell_env.sh
centralizes this logic and is shared by install_jdk.sh, install_maven.sh,
install_etcd.sh, init_metadata.sh, build_install_pixels.sh, configure_pixels.sh,
check_prerequisites.sh, smoke_test.sh, install_trino.sh,
install_trino_cluster.sh, prepare_trino_cluster.sh,
build_pixels_trino_artifacts.sh,
install_trino_shell_helpers.sh, and progress.sh. It also defines the
structured-result (result_record/result_emit_summary) and phase-checkpoint
(mark_phase_done/phase_is_done/print_progress) helpers described above.
Each install_*.sh script runs as its own process, so exporting a variable
in one script does not make it visible to the next one. docs/INSTALL.md's
own walkthrough handles this by running source ~/.bashrc right after
appending to it, but sourcing the user's real rc file from a non-interactive
script is unreliable (Ubuntu's default ~/.bashrc returns immediately for
non-interactive shells, before reaching anything appended at the bottom). So
in addition to persisting into the user's real shell profile,
shell_env.sh also mirrors JAVA_HOME/MAVEN_HOME/ETCD/PIXELS_HOME/PIXELS_CONFIG
into a small STATE_DIR/toolchain.env file with no such guard,
and every script sources it (load_toolchain_env) at startup. This means
running install_jdk.sh then install_maven.sh as two separate tool calls
still works without the skill needing to manually re-source anything in
between — but if you set any of these variables yourself before running a
step, your explicit environment variable still wins.
The skill may run simple commands from docs/INSTALL.md directly when that is clearer than adding another wrapper script.
Workflow
The phase sequence is fixed in skill.yaml's phases list — this section is decision guidance for each phase, not a second copy of that ordering. Before doing anything else, run scripts/progress.sh show to see which phases (if any) already completed in a prior session, and skip straight to the first phase that is not yet marked done instead of re-deriving state from scratch or blindly redoing finished work. After each phase actually succeeds, run scripts/progress.sh mark <phase> (phase names match skill.yaml) so a later session — or this one, after a long gap — can pick up correctly.
- discover_install_target: clarify single node vs. cluster,
PIXELS_HOME, coordinator vs. worker roles, storage/query engine needs, whether to start services now, and whether optional components are in scope. Determine the actual login user (whoami) and home directory ($HOME) instead of assuming pixels or ubuntu; every path this skill writes is anchored on that $HOME. Do not proceed with defaults until the user has confirmed the topology and install locations.
- check_prerequisites: run
check_prerequisites.sh and read its full structured summary before deciding what to install — see "Failure Handling" below for what to do with anything it reports as fail.
- prepare_deployment_config: run for both single-node and cluster deployments so later scripts read the same confirmed
PIXELS_HOME, coordinator service hosts, and worker list from deployment.env. Prefer prepare_deployment.sh with no node arguments for an interactive prompt, or explicit --coordinator ... --worker ... arguments when the user has already provided the topology.
- install_or_verify_jdk:
install_jdk.sh already skips the download when anything on the system (apt-installed JDK, a previous run, etc.) satisfies JDK_VERSION; it only selects and installs a Zulu build under ~/opt when nothing qualifies. JDK 23+ is required for the documented Pixels + Trino 466 path. Ask before installing a downloaded JDK package.
- install_or_verify_maven:
install_maven.sh has the same "skip if already satisfied" behavior for Maven 3.8+ (default pinned 3.9.8, only overridden if the user asks). Ensure mvn -v ends up using the selected JDK.
- install_or_verify_etcd:
install_etcd.sh keeps localhost-only by default, asks before remote etcd access, and asks before installing its systemd auto-start unit.
- build_install_pixels: install Pixels from the repository into the confirmed
PIXELS_HOME from deployment.env. Do not download the MySQL JDBC connector unless the user chose MySQL. In a Pixels cluster, worker nodes need this full installed PIXELS_HOME runtime layout to run $PIXELS_HOME/bin/start-daemon.sh worker, but they do not need to rebuild Pixels locally; build/install once, then distribute the installed PIXELS_HOME to worker nodes (for example with $PIXELS_HOME/sbin/rsync-cluster.sh after etc/workers is correct).
- configure_pixels: update
pixels.properties only once paths, hosts, ports, database, etcd, topology, and cache settings are confirmed; it derives pixels.var.dir from the resolved PIXELS_HOME, writes $PIXELS_HOME/etc/workers from PIXELS_WORKERS, and defaults to Derby. If the user explicitly wants MySQL, run install_mysql.sh first (see Guardrails — never apply the default MySQL password silently), then set METADATA_DB_TYPE=mysql and pass METADATA_DB_PASSWORD or reuse deployment.secrets.env.
- init_metadata: run
init_metadata.sh so pixels-cli INIT-META creates the metadata tables in the configured backend. This is the default table-creation path for both Derby and MySQL.
- start_pixels_optional: only when the user asks to start services or startup is part of the requested task.
- install_shell_helpers_optional: only if the user wants convenience functions. Install Pixels helpers on the Pixels coordinator and Trino helpers on the Trino coordinator; do not install them on the agent's current host unless that host is the corresponding coordinator or the user explicitly asks for a local helper install. Both helper scripts ask by default before writing to the target user's shell profile.
- smoke_test: run after configuration or startup changes; read the full structured summary (see "Failure Handling" below), don't just check the exit code. For a final deployment check, enable the relevant checks from the actual topology, e.g.
CHECK_PIXELS_LOGS=true CHECK_TRINO=true CHECK_TRINO_CLUSTER_STATUS=true CHECK_TRINO_CLI=true. SHOW SCHEMAS success is enough before data is loaded; do not require returned schemas to be non-empty.
- optional_trino_install: only when the user explicitly asks. Clarify the topology first: coordinator vs. worker(s), how many workers, whether the coordinator also accepts scheduled work (
node-scheduler.include-coordinator; official docs recommend false for a dedicated coordinator, true for a single-node test setup), TRINO_INSTALL_PARENT, TRINO_HOME_LINK, and TRINO_DATA_DIR. Never infer a fixed Trino cluster shape; use the user's topology or prepare_trino_cluster.sh's confirmed output. For a Trino cluster whose remote nodes should not build Pixels, run build_pixels_trino_artifacts.sh once where Pixels has already been mvn installed, or pass PIXELS_TRINO_CONNECTOR_ZIP and PIXELS_TRINO_LISTENER_ZIP explicitly to install_trino_cluster.sh; the remote nodes should only receive Trino plus those two plugins, Trino catalog/listener configs, and the lightweight Trino-side Pixels client config under TRINO_PIXELS_HOME (default ~/opt/pixels). They must not be required to receive the full Pixels install, Maven, or source checkout. Run prepare_trino_cluster.sh --setup-ssh true, then run install_trino_cluster.sh on the coordinator to install Trino on the coordinator and fan out to every worker in parallel over SSH, instead of repeating install_trino.sh by hand once per node. Configure Trino's etc/catalog/pixels.properties as a Trino catalog file only; it is not $PIXELS_HOME/etc/pixels.properties. For the Trino-side Pixels client config, set PIXELS_HOME/TRINO_PIXELS_HOME to the parent directory (e.g. ~/opt/pixels, never ~/opt/pixels/etc) and set/export PIXELS_CONFIG=$PIXELS_HOME/etc/pixels.properties; all Trino nodes need this because the pixels-trino connector initializes on workers as well as the coordinator. install_trino.sh writes those two exports directly to the user's bash/zsh profile. When the skill itself starts, stops, checks, or runs CLI against Trino from a non-interactive SSH command, inline PIXELS_HOME and PIXELS_CONFIG in that command because remote non-interactive shells may not read .bashrc/.zshrc. Ensure metadata.server.host, trans.server.host, query.schedule.server.host, and etcd.hosts are reachable from every Trino node and are not accidental localhost values in a separated topology. Configure presto.pixels.jdbc.url only once the coordinator's endpoint and catalog/schema values are known. Offer install_trino_shell_helpers.sh only after the user agrees (it asks by default anyway).
- optional_hadoop_or_monitoring: out of scope unless the user asks — see Optional Components below.
Failure handling
check_prerequisites.sh and smoke_test.sh never stop at the first failed check — they run every check and print a structured summary block (=== <script> result === … summary: ok=N warn=N fail=N skip=N status=pass|fail … === end <script> result ===). When status=fail:
- Read every
fail line in the block, not just the first — they are independent and often have independent causes (e.g. a port conflict and a missing config property are unrelated problems, both worth knowing about before acting).
- For each
fail, decide whether it is something this skill should fix directly (e.g. an expected service genuinely isn't running yet — wait and re-check), something that needs a user decision (e.g. a port is held by an unrelated process — ask whether to stop it or change Pixels' port), or something informational that the user already knows about (e.g. a deliberately non-Ubuntu host).
- Don't re-run the same script in a loop hoping it passes — fix (or get a decision on) the specific
fail line first, then re-run once to confirm.
warn and skip lines never fail the run by themselves, but still read them — a warn is frequently the first sign of an edge case worth flagging to the user even though it didn't block anything.
The same applies to install_trino_cluster.sh's per-node summary: a fail for one worker doesn't mean the whole cluster install failed — check which specific node(s) failed, read that node's log under STATE_DIR/logs/, and retry only that node rather than re-running the whole fan-out.
Optional components
Do not install these unless the user explicitly asks for the corresponding later stage:
- Trino or another query engine — see the dedicated Trino steps above and the scripts list; this skill can now install Trino itself when asked, instead of only pointing at the external
pixels-trino documentation.
- Hadoop or HDFS configuration.
- AWS credentials.
- Prometheus, Grafana, node exporter, or JMX exporter.
- Pixels Cache, Turbo, Retina, or Amphi.
The basic Pixels install flow may configure presto.pixels.jdbc.url only after the Trino endpoint and catalog/schema values are known.
Trino-side Pixels client config is not a full Pixels installation. A Trino node does not need $PIXELS_HOME/bin, $PIXELS_HOME/sbin, $PIXELS_HOME/lib, pixels-daemon-*-full.jar, pixels-cli-*-full.jar, Maven, or the Pixels source tree just to run the pixels-trino connector. It does need a readable PIXELS_CONFIG/$PIXELS_HOME/etc/pixels.properties with reachable Pixels metadata, transaction, query-schedule, etcd, and storage settings, plus access to the underlying data storage (for example shared local paths, HDFS configs, or S3/MinIO credentials depending on table storage).
Examples
Prepare a three-node Pixels cluster config and SSH (run from this skill directory, or invoke the script by absolute path):
./scripts/prepare_deployment.sh \
--ssh-user root \
--setup-ssh true \
--coordinator-is-worker false \
--pixels-home /home/pixels/opt/pixels \
--coordinator 10.0.0.11,10.0.0.11,pixels-coordinator,root,22 \
--worker 10.0.0.12,10.0.0.12,pixels-worker-1,root,22 \
--worker 10.0.0.13,10.0.0.13,pixels-worker-2,root,22
./scripts/check_prerequisites.sh
Prepare a 1-coordinator/2-worker Trino cluster, SSH, then install Trino on every node in parallel (run on the coordinator):
./scripts/prepare_trino_cluster.sh \
--ssh-user root \
--setup-ssh true \
--coordinator 10.0.0.11,10.0.0.11,trino-coordinator,root,22 \
--worker 10.0.0.12,10.0.0.12,trino-worker-1,root,22 \
--worker 10.0.0.13,10.0.0.13,trino-worker-2,root,22
./scripts/install_trino_cluster.sh
Check (and resume from) the recorded install progress:
./scripts/progress.sh show
./scripts/progress.sh mark check_prerequisites
Guardrails
- Prefer
docs/INSTALL.md plus targeted helper scripts over a fixed all-in-one install command.
- Do not run scripts that install packages, change services, update credentials, or start daemons unless the user is asking to perform that installation stage.
- Do not modify Git state.
- When modifying this skill's source, keep changes limited to
skills/pixels-install/ unless the user explicitly requests more.
- Ask before handling secrets, installing downloaded JDK packages, installing MySQL as an alternative metadata backend, changing MySQL root authentication, enabling remote database access, installing the etcd
systemd auto-start unit, or installing the Trino cluster shell helpers.
- Use Derby plus pixels-cli
INIT-META as the default metadata path. Do not install MySQL unless the user explicitly asks for it.
- Ask before installing Pixels shell helpers or Trino shell helpers into the user's shell profile; do not silently add operational functions.
- For management shell helpers, the default target is the relevant coordinator, not the current agent host: Pixels helpers go to
PIXELS_COORDINATOR_SSH_TARGET, and Trino helpers go to TRINO_COORDINATOR_SSH_TARGET. Use PIXELS_SHELL_HELPERS_TARGET=local or TRINO_SHELL_HELPERS_TARGET=local only after the user explicitly asks for local-only helper functions.
- Ask or use the interactive preparation scripts before choosing
PIXELS_HOME, Trino install/data paths, single-node vs. cluster mode, coordinator role, worker list, or whether a coordinator also runs worker tasks. Never let the skill silently accept topology/path defaults on the user's behalf.
- In non-interactive runs, only set
CONFIRM_PIXELS_DEPLOYMENT=true, CONFIRM_PIXELS_INSTALL=true, CONFIRM_TRINO_DEPLOYMENT=true, CONFIRM_TRINO_INSTALL=true, or CONFIRM_TRINO_CLUSTER_INSTALL=true after showing the user the exact topology and paths that will be written or installed.
- Never hardcode
/home/pixels, /home/ubuntu, or any other specific home directory in a command; always resolve paths from $HOME for whichever user is actually running the install.
- Never persist
JAVA_HOME, MAVEN_HOME, ETCD, PIXELS_HOME, or PIXELS_CONFIG into a global file (/etc/environment, /etc/profile.d/*); only append to the current user's shell profile, matching their actual login shell.
- For Trino nodes, never set
PIXELS_HOME to the etc directory. It must be the parent directory (for example $HOME/opt/pixels), with PIXELS_CONFIG=$PIXELS_HOME/etc/pixels.properties.
- Do not wrap or replace Trino's
bin/launcher to smuggle Pixels environment variables, and do not create a separate ~/.pixels-trino-env.sh. Persist PIXELS_HOME and PIXELS_CONFIG directly in the user's bash/zsh profile; generated helpers and skill-run SSH commands must inline-export them before invoking Trino.
- Do not create SSH tunnels for etcd. If Trino/Pixels nodes must access etcd remotely, configure etcd/network access explicitly and restrict the allowed source IP ranges to trusted Pixels and Trino nodes.
- Never accept the documented default MySQL passwords (
password) without an explicit interactive confirmation from the user; install_mysql.sh enforces this by prompting even when a default is acceptable.
- Never run
install_trino.sh (directly or via install_trino_cluster.sh) against a node without first knowing its role (coordinator vs. worker) from trino-deployment.env or an explicit TRINO_ROLE; discovery.uri must point at the coordinator's real IP, never localhost.
- Never put Pixels client/runtime properties such as
metadata.server.host, trans.server.host, query.schedule.server.host, or etcd.hosts into Trino's etc/catalog/pixels.properties. Keep them only in the Trino-side Pixels client config (PIXELS_CONFIG).
- Preserve the versioned plugin directory names produced by unzipping
pixels-trino-connector-*.zip and pixels-trino-listener-*.zip; do not normalize them to unversioned names.
- When a structured result summary reports
status=fail, address (or get a user decision on) every fail line before re-running — see "Failure handling" above. Don't paper over a fail by re-running the script in a loop.
STATE_DIR/deployment.env, STATE_DIR/deployment.secrets.env, STATE_DIR/toolchain.env, STATE_DIR/trino-deployment.env, STATE_DIR/progress.env, and STATE_DIR/logs/ are generated, user-specific, and must never be committed.
1---2name: pixels-install3description: Guide, execute, and verify a basic Pixels deployment from docs/INSTALL.md using bundled helper scripts. Use when the user asks to install, configure, resume, validate, or troubleshoot a basic Pixels deployment or optional Trino setup.4---56# pixels-install78Help the user install a basic Pixels deployment according to `docs/INSTALL.md`.910Treat `docs/INSTALL.md` as the source of truth for the installation flow. Use the bundled scripts only as helpers for deterministic, repeatable, or easy-to-misconfigure steps. Do not treat this skill as a one-shot unattended installer.1112## Resource Paths1314- Skill directory: use the directory containing this `SKILL.md`.15- Helper scripts: `scripts/`.16- Shared helper scripts: `shared-scripts/`.17- Runtime state: `STATE_DIR` when set; otherwise the scripts resolve it to `.agents/state/pixels-install` for project use or `$HOME/.agents/state/pixels-install` for global installs.18- Pixels source tree: `REPO_ROOT` when set; otherwise scripts try to discover the current Git repository root and fail with a clear message if they cannot.1920### Helper scripts2122Use these helpers when they directly fit the current environment and the user-approved goal:2324- `prepare_deployment.sh`: collect and confirm the Pixels deployment topology before any install writes to disk. With no node arguments it prompts for single-node vs. cluster, `PIXELS_HOME`, the coordinator node, whether the coordinator also runs a worker, and worker nodes; with arguments, use `--coordinator <ssh_target,host_ip,host_name,host_user,host_port>` plus repeated `--worker <...>` (legacy `--node` still works). Writes `STATE_DIR/deployment.env` and optionally delegates SSH setup to `shared-scripts/setup_cluster.sh`. In non-interactive mode, set `CONFIRM_PIXELS_DEPLOYMENT=true` only after the user has reviewed the chosen topology and paths.25- `check_prerequisites.sh`: validate OS, architecture, memory, disk, ports, host resolution, privilege, and optional SSH reachability. Runs every check and exits with a structured `=== check_prerequisites result ===` summary (one `ok|warn|fail|skip <check>: <detail>` line per check, plus a final `summary: ok=N warn=N fail=N skip=N status=pass|fail` line) instead of stopping at the first failure, so a single run shows every problem at once.26- `install_jdk.sh`: install a Zulu OpenJDK build (default JDK 23) matching the server's CPU architecture (x86_64 or aarch64) under `~/opt`, and persist `JAVA_HOME` only into the current user's shell profile. Looks for a JDK that already satisfies `JDK_VERSION` first — checking `JAVA_HOME` if set, otherwise resolving whatever `java` is on `PATH` (e.g. an `apt`-installed JDK that never exported `JAVA_HOME`) — and if one is found, skips the download entirely and only fixes up the environment variable to point at it. Only downloads/installs a fresh Zulu build when nothing on the system satisfies the version requirement. Falls back to pointing at the manual `.deb` method in `docs/INSTALL.md` if the Azul metadata API lookup fails.27- `install_maven.sh`: install or validate Maven 3.8+ (default pinned version `3.9.8`; override with `MAVEN_VERSION` only if the user explicitly asks for a different one) when the current Maven is missing or incompatible with the selected JDK. Same "skip if already satisfied" logic as `install_jdk.sh`: if an existing `mvn` (from `apt`, a prior manual install, etc.) already meets the minimum version, it reuses that installation's home directory instead of downloading anything. Fresh installs go into `~/opt/apache-maven-<version>` with a `~/opt/maven` symlink, never into `/opt`.28- `init_metadata.sh`: **default metadata setup**. After Pixels is installed and `pixels.properties` points at the metadata database, it runs pixels-cli `INIT-META` to create the tables. Derby is the default backend (`jdbc:derby:$PIXELS_HOME/var/pixels_metadata;create=true`); no extra server or JDBC connector is required because Derby is packaged with pixels-cli/pixels-daemon.29- `install_mysql.sh`: **optional alternative** to Derby. Use only when the user explicitly wants MySQL as the metadata database. Installs MySQL, then interactively confirm the root password and the `pixels` metadata-DB-user password (default `password` for both, never applied silently), create the `pixels_metadata` database/user, and optionally load `pixels-daemon/src/main/resources/pixels_metadata_mysql.sql`. It validates DB/user identifiers, quotes SQL values, avoids putting the root password in command-line arguments, and writes shell-quoted `STATE_DIR/deployment.secrets.env` (mode 600) so `configure_pixels.sh` can set `METADATA_DB_TYPE=mysql`. Table creation can still be done with `INIT-META`.30- `install_etcd.sh`: install and optionally start the bundled etcd 3.3.4 package under `~/opt`. Always fixes the shipped `conf.yml`'s hardcoded `/home/ubuntu/...` `data-dir` to the real install path. By default (`ETCD_ALLOW_REMOTE=false`) it keeps etcd localhost-only. For a cluster, set `ETCD_ALLOW_REMOTE=true` only after confirming private networking/security-group rules; the script then requires `CONFIRM_ETCD_REMOTE_ACCESS=true`, `ASSUME_YES=true`, or an interactive confirmation before binding the client/peer listeners for remote access. Can also install and enable a `systemd` unit so etcd survives reboots and restarts on failure, but only after asking — it never installs this silently. Leave `INSTALL_ETCD_SYSTEMD_SERVICE` unset to be prompted interactively (`[y/N]`, defaults to no); set it to `true`/`false` to answer non-interactively once the user has actually agreed, or `ASSUME_YES=true` to answer yes to this and other yes/no prompts. Declining (or running non-interactively with no explicit answer) just skips the unit — etcd still starts, as a manually backgrounded process instead. Falls back the same way when `systemctl` isn't available at all. Restarts/re-enables the installed service automatically when the config actually changed.31- `build_install_pixels.sh`: build and install Pixels into the confirmed `PIXELS_HOME` from `deployment.env`. It does **not** download the MySQL JDBC connector unless the user chose MySQL (`METADATA_DB_TYPE=mysql` or `INSTALL_MYSQL_CONNECTOR=true`). It prints the repo, deployment file, and install target before running; in non-interactive mode, set `CONFIRM_PIXELS_INSTALL=true` only after the user confirms the target. If you must bypass the prepared deployment file, pass `USE_DEPLOYMENT_FILE=false PIXELS_HOME=<path>` explicitly. `AUTO_CONFIRM_INSTALL` (default `true`) auto-answers `install.sh`'s prompts only when that is safe; if `PIXELS_HOME/etc/pixels.properties` or `pixels-cpp.properties` already exist, it forces the interactive path instead, because `install.sh` would otherwise prompt to add/remove config options or overwrite `pixels-cpp.properties` outright — exactly what `Claude agent`/`SKILL.md` say not to do without the user's say-so.32- `configure_pixels.sh`: update `PIXELS_HOME/etc/pixels.properties` after database, etcd, host, port, topology, and path values are known. Defaults to Derby (`METADATA_DB_TYPE=derby`, URL `jdbc:derby:$PIXELS_HOME/var/pixels_metadata;create=true`). Automatically sources `deployment.env` and `deployment.secrets.env` (if present). It writes `$PIXELS_HOME/etc/workers` from `PIXELS_WORKERS` by default. For the MySQL alternative, set `METADATA_DB_TYPE=mysql` and reuse credentials from `install_mysql.sh`; it fails instead of silently writing the documented default DB password unless `METADATA_DB_PASSWORD` is explicit or `ALLOW_DEFAULT_METADATA_PASSWORD=true` is set.33- `install_shell_helpers.sh`: optional convenience step that **asks first** before writing shell functions. By default it installs only `start_pixels`/`stop_pixels`/`restart_pixels` on the Pixels coordinator recorded in `deployment.env`; when invoked from another host, it copies a minimal installer over SSH and writes the coordinator user's `~/.pixels-shell-helpers.sh` plus shell-profile source line there. These wrap `$PIXELS_HOME/sbin/start-pixels.sh` and `$PIXELS_HOME/sbin/stop-pixels.sh`. Set `PIXELS_SHELL_HELPERS_TARGET=local` only when the user explicitly wants the functions on the current host. Leave `INSTALL_PIXELS_SHELL_HELPERS` unset to be prompted `[y/N]`; set `true`/`false` to answer non-interactively once the user has actually agreed, or `ASSUME_YES=true`.34- `smoke_test.sh`: verify the installed layout, configuration, Java (`CHECK_JAVA=true`), metadata access, etcd health, Pixels topology (`deployment.env` plus `$PIXELS_HOME/etc/workers`), core service ports (`CHECK_CORE_SERVICES=true`), `$PIXELS_HOME/logs` error patterns (`CHECK_PIXELS_LOGS=true`), and basic CLI behavior when applicable. It also checks Trino layout/catalog files when `CHECK_TRINO=true` and `trino-deployment.env` is present, can separately validate the lightweight Trino-side Pixels client config plus shell-profile `PIXELS_HOME`/`PIXELS_CONFIG` exports (`CHECK_TRINO_PIXELS_CLIENT=true`) without requiring a full Pixels runtime layout (`CHECK_PIXELS_LAYOUT=false`), can check Trino launcher status across the recorded cluster (`CHECK_TRINO_CLUSTER_STATUS=true`), checks that Trino logs did not fall back to classpath `pixels.properties` (`CHECK_TRINO_LOGS=true`), waits for `/v1/info` to report `starting=false` (`CHECK_TRINO_READY=true`), and can run `trino --catalog pixels --execute "SHOW SCHEMAS"` (`CHECK_TRINO_CLI=true`). `SHOW SCHEMAS` only needs to succeed; an empty Pixels catalog is expected before data is loaded. Same structured-summary behavior as `check_prerequisites.sh` above.35- `progress.sh`: records which of `skill.yaml`'s `phases` have actually completed, in `STATE_DIR/progress.env`. `progress.sh mark <phase> [note]` after a phase succeeds, `progress.sh show` to see what's already done, `progress.sh is-done <phase>` to check one phase, `progress.sh reset` to start over. Exists so a session interrupted partway through a multi-phase install can resume from the actual state instead of guessing or re-running completed steps.36- `build_pixels_trino_artifacts.sh`: optional Trino-prep step. Builds `pixelsdb/pixels-trino` once on the local machine where Pixels modules have already been `mvn install`ed, copies `pixels-trino-connector-*.zip` and `pixels-trino-listener-*.zip` into `STATE_DIR/pixels-trino-artifacts/`, and writes `STATE_DIR/pixels-trino-artifacts.env` for `install_trino_cluster.sh`. In non-interactive mode, set `CONFIRM_PIXELS_TRINO_ARTIFACTS=true` only after the user confirms the checkout/artifact paths.37- `install_trino_cluster.sh`: run **on the coordinator** after `prepare_trino_cluster.sh`. It prints and confirms the Trino deployment file, version, install parent, home link, data dir, coordinator, coordinator scheduling mode, workers, Trino-side Pixels client home/config source, Pixels service endpoints, and any prebuilt pixels-trino plugin zips before installing; in non-interactive mode, set `CONFIRM_TRINO_CLUSTER_INSTALL=true` only after the user confirms the fan-out. Installs Trino locally for the coordinator's own role, then dispatches `install_trino.sh` on every worker **in parallel** over the coordinator -> worker passwordless SSH that `prepare_trino_cluster.sh` already set up, instead of requiring the user or skill to copy the same command and run it by hand once per node. Copies the current `STATE_DIR/trino-deployment.env` to each worker first (workers don't need a shared filesystem). If `PIXELS_TRINO_CONNECTOR_ZIP` and `PIXELS_TRINO_LISTENER_ZIP` are provided, or `STATE_DIR/pixels-trino-artifacts.env` exists from `build_pixels_trino_artifacts.sh`, it also copies a minimal installer plus those prebuilt plugin zips to each worker, so workers do not need a Pixels checkout or Maven build. It also copies the selected Trino-side Pixels client config (`TRINO_PIXELS_CONFIG_SOURCE`, defaulting to `$PIXELS_HOME/etc/pixels.properties` when available) and lets `install_trino.sh` rewrite service endpoints through `TRINO_PIXELS_*` variables. Remote Trino nodes receive Trino, the two pixels-trino plugin zips, Trino catalog/listener configs, and a lightweight `~/opt/pixels/etc/pixels.properties` client config — not a full Pixels installation. Emits the same structured per-node summary as `smoke_test.sh`/`check_prerequisites.sh`, with a log file per node under `STATE_DIR/logs/` for diagnosing any failure.38- `prepare_trino_cluster.sh`: collects and confirms the Trino topology and install locations: one coordinator, zero or more workers, whether the coordinator also accepts scheduled work, Trino version, HTTP port, install parent, home symlink/current path, and data directory. With no node arguments it prompts interactively; with arguments, use `--coordinator <ssh_target,host_ip,host_name,host_user,host_port>` plus repeated `--worker <...>`. Writes `STATE_DIR/trino-deployment.env`. Optionally delegates to `shared-scripts/setup_cluster.sh` (`--setup-ssh true`) so the coordinator and every worker can passwordlessly SSH into each other, which the cluster shell helpers below depend on. In non-interactive mode, set `CONFIRM_TRINO_DEPLOYMENT=true` only after the user has reviewed the topology and paths.39- `install_trino.sh`: installs Trino (per the [466 deployment docs](https://trino.io/docs/466/installation/deployment.html)) on the **current node only** — run it once per node in the cluster, it does not SSH anywhere itself (use `install_trino_cluster.sh` to fan it out across the cluster instead of running it by hand on each node). It reads `trino-deployment.env`, validates this node's role (`coordinator` or `worker`) and the coordinator host, and confirms the role plus install/data paths before writing files; in non-interactive mode, set `CONFIRM_TRINO_INSTALL=true` only after the user confirms the target. Downloads `trino-server-<version>.tar.gz` (default 466) from Maven Central into the confirmed install parent with the confirmed home symlink for version-switch-friendly installs. Writes `etc/node.properties` (`node.data-dir` under the confirmed data dir, outside the versioned install dir so it survives version switches; `node.id` generated once and preserved across re-runs), `etc/jvm.config` (official defaults plus the two `--add-opens` flags `pixels-trino` requires), `etc/log.properties`, and `etc/config.properties` — `discovery.uri` always points at the coordinator's real IP from `trino-deployment.env`, never `localhost`; this node's coordinator/worker role is read from `trino-deployment.env` or inferred from this host's own IPs, overridable with `TRINO_ROLE`. For `pixelsdb/pixels-trino`, either clones/builds locally (requires Pixels already `mvn install`ed locally and JDK 23) or consumes prebuilt `PIXELS_TRINO_CONNECTOR_ZIP` and `PIXELS_TRINO_LISTENER_ZIP` so remote Trino nodes do not need a Pixels checkout or Maven build. Installs both documented plugins by directly unzipping their zips under `plugin/` and preserving the zip's versioned top-level directory names, e.g. `pixels-trino-connector-0.2.0-SNAPSHOT` and `pixels-trino-listener-0.2.0-SNAPSHOT`; do not rename them to unversioned stable directories. Writes Trino's catalog file `etc/catalog/pixels.properties` with only catalog properties (`connector.name=pixels`, `cloud.function.switch=off`, `clean.intermediate.result=true` by default). This catalog file is **not** the same thing as Pixels' client/runtime config file `$PIXELS_HOME/etc/pixels.properties`; never copy Pixels runtime properties such as `metadata.server.host` or `etcd.hosts` into the Trino catalog. Also prepares the Trino-side lightweight Pixels client home (`TRINO_PIXELS_HOME`, default `$HOME/opt/pixels`, not `$HOME/opt/pixels/etc`) with `etc/pixels.properties` and `logs/`, then writes `export PIXELS_HOME=...` and `export PIXELS_CONFIG=$PIXELS_HOME/etc/pixels.properties` directly into the current user's bash/zsh profile (and mirrors them into `STATE_DIR/toolchain.env` for later helper-script processes). It does **not** create `~/.pixels-trino-env.sh`. Use `TRINO_PIXELS_CONFIG_SOURCE` to copy a known-good Pixels config into that client home, and use `TRINO_PIXELS_METADATA_SERVER_HOST`, `TRINO_PIXELS_TRANS_SERVER_HOST`, `TRINO_PIXELS_QUERY_SCHEDULE_SERVER_HOST`, and `TRINO_PIXELS_ETCD_HOSTS` to rewrite endpoints so Trino nodes do not fall back to `localhost`. Writes `etc/event-listener.properties` for the listener and defaults its log directory to a Trino-side path under the install parent unless `PIXELS_TRINO_LISTENER_LOG_DIR` is set. Best-effort installs `trino-cli` into `bin/trino`.40- `install_trino_shell_helpers.sh`: optional, and **always asks first**, because it bakes a specific list of remote hostnames (from `trino-deployment.env`) into a shell profile and assumes passwordless SSH between them is already set up. By default it installs only `start_trino_cluster`/`stop_trino_cluster`/`restart_trino_cluster`/`trino_cli` on the Trino coordinator recorded in `trino-deployment.env`; when invoked from another host, it copies a minimal installer over SSH and writes the coordinator user's `~/.trino-shell-helpers.sh` plus shell-profile source line there. In the generated functions, the coordinator is always operated locally and every worker over SSH. `stop_trino_cluster` stops workers before the coordinator; `restart_trino_cluster` runs stop then start. The generated start/stop/restart/client helpers explicitly export `PIXELS_HOME` and `PIXELS_CONFIG` before invoking Trino locally or over SSH, without wrapping or replacing Trino's `bin/launcher`. Set `TRINO_SHELL_HELPERS_TARGET=local` only when the user explicitly wants the functions on the current host. Leave `INSTALL_TRINO_SHELL_HELPERS` unset to be prompted `[y/N]`; set `true`/`false` to answer non-interactively once the user has actually agreed, or `ASSUME_YES=true`.4142All executable entrypoints in this skill are Bash programs. Invoke them directly43so their `#!/usr/bin/env bash` shebang is honored; do not run them with `zsh`44or `source` them into the current shell. Bash must be installed on every45deployment node even when that account's login shell is Zsh. Each entrypoint46validates the deployment account's configured login shell from passwd/NSS and47accepts only Bash or Zsh. Profile exports go to `.bashrc` or `.zshrc`48accordingly, while the generated Pixels and Trino helper files are checked in49both Bash and Zsh syntax before they are sourced.5051All of these scripts anchor paths on `$HOME` (e.g. `~/opt/...`) and detect the52current account's login shell from passwd/NSS to pick `~/.bashrc` or `~/.zshrc` before53persisting any `export`. Never assume the deployment user is named `pixels`54or `ubuntu`, and never write environment variables into a global file such55as `/etc/environment` — `scripts/lib/shell_env.sh`56centralizes this logic and is shared by `install_jdk.sh`, `install_maven.sh`,57`install_etcd.sh`, `init_metadata.sh`, `build_install_pixels.sh`, `configure_pixels.sh`,58`check_prerequisites.sh`, `smoke_test.sh`, `install_trino.sh`,59`install_trino_cluster.sh`, `prepare_trino_cluster.sh`,60`build_pixels_trino_artifacts.sh`,61`install_trino_shell_helpers.sh`, and `progress.sh`. It also defines the62structured-result (`result_record`/`result_emit_summary`) and phase-checkpoint63(`mark_phase_done`/`phase_is_done`/`print_progress`) helpers described above.6465Each `install_*.sh` script runs as its own process, so exporting a variable66in one script does not make it visible to the next one. `docs/INSTALL.md`'s67own walkthrough handles this by running `source ~/.bashrc` right after68appending to it, but sourcing the user's real rc file from a *non*-interactive69script is unreliable (Ubuntu's default `~/.bashrc` returns immediately for70non-interactive shells, before reaching anything appended at the bottom). So71in addition to persisting into the user's real shell profile,72`shell_env.sh` also mirrors `JAVA_HOME`/`MAVEN_HOME`/`ETCD`/`PIXELS_HOME`/`PIXELS_CONFIG`73into a small `STATE_DIR/toolchain.env` file with no such guard,74and every script sources it (`load_toolchain_env`) at startup. This means75running `install_jdk.sh` then `install_maven.sh` as two separate tool calls76still works without the skill needing to manually re-source anything in77between — but if you set any of these variables yourself before running a78step, your explicit environment variable still wins.7980The skill may run simple commands from `docs/INSTALL.md` directly when that is clearer than adding another wrapper script.8182### Workflow8384The phase sequence is fixed in `skill.yaml`'s `phases` list — this section is decision guidance for each phase, not a second copy of that ordering. Before doing anything else, run `scripts/progress.sh show` to see which phases (if any) already completed in a prior session, and skip straight to the first phase that is not yet marked done instead of re-deriving state from scratch or blindly redoing finished work. After each phase actually succeeds, run `scripts/progress.sh mark <phase>` (phase names match `skill.yaml`) so a later session — or this one, after a long gap — can pick up correctly.8586- **discover_install_target**: clarify single node vs. cluster, `PIXELS_HOME`, coordinator vs. worker roles, storage/query engine needs, whether to start services now, and whether optional components are in scope. Determine the actual login user (`whoami`) and home directory (`$HOME`) instead of assuming `pixels` or `ubuntu`; every path this skill writes is anchored on that `$HOME`. Do not proceed with defaults until the user has confirmed the topology and install locations.87- **check_prerequisites**: run `check_prerequisites.sh` and read its full structured summary before deciding what to install — see "Failure Handling" below for what to do with anything it reports as `fail`.88- **prepare_deployment_config**: run for both single-node and cluster deployments so later scripts read the same confirmed `PIXELS_HOME`, coordinator service hosts, and worker list from `deployment.env`. Prefer `prepare_deployment.sh` with no node arguments for an interactive prompt, or explicit `--coordinator ... --worker ...` arguments when the user has already provided the topology.89- **install_or_verify_jdk**: `install_jdk.sh` already skips the download when anything on the system (apt-installed JDK, a previous run, etc.) satisfies `JDK_VERSION`; it only selects and installs a Zulu build under `~/opt` when nothing qualifies. JDK 23+ is required for the documented Pixels + Trino 466 path. Ask before installing a downloaded JDK package.90- **install_or_verify_maven**: `install_maven.sh` has the same "skip if already satisfied" behavior for Maven 3.8+ (default pinned `3.9.8`, only overridden if the user asks). Ensure `mvn -v` ends up using the selected JDK.91- **install_or_verify_etcd**: `install_etcd.sh` keeps localhost-only by default, asks before remote etcd access, and asks before installing its `systemd` auto-start unit.92- **build_install_pixels**: install Pixels from the repository into the confirmed `PIXELS_HOME` from `deployment.env`. Do **not** download the MySQL JDBC connector unless the user chose MySQL. In a Pixels cluster, worker nodes need this full installed `PIXELS_HOME` runtime layout to run `$PIXELS_HOME/bin/start-daemon.sh worker`, but they do **not** need to rebuild Pixels locally; build/install once, then distribute the installed `PIXELS_HOME` to worker nodes (for example with `$PIXELS_HOME/sbin/rsync-cluster.sh` after `etc/workers` is correct).93- **configure_pixels**: update `pixels.properties` only once paths, hosts, ports, database, etcd, topology, and cache settings are confirmed; it derives `pixels.var.dir` from the resolved `PIXELS_HOME`, writes `$PIXELS_HOME/etc/workers` from `PIXELS_WORKERS`, and defaults to Derby. If the user explicitly wants MySQL, run `install_mysql.sh` first (see Guardrails — never apply the default MySQL password silently), then set `METADATA_DB_TYPE=mysql` and pass `METADATA_DB_PASSWORD` or reuse `deployment.secrets.env`.94- **init_metadata**: run `init_metadata.sh` so pixels-cli `INIT-META` creates the metadata tables in the configured backend. This is the default table-creation path for both Derby and MySQL.95- **start_pixels_optional**: only when the user asks to start services or startup is part of the requested task.96- **install_shell_helpers_optional**: only if the user wants convenience functions. Install Pixels helpers on the Pixels coordinator and Trino helpers on the Trino coordinator; do not install them on the agent's current host unless that host is the corresponding coordinator or the user explicitly asks for a local helper install. Both helper scripts ask by default before writing to the target user's shell profile.97- **smoke_test**: run after configuration or startup changes; read the full structured summary (see "Failure Handling" below), don't just check the exit code. For a final deployment check, enable the relevant checks from the actual topology, e.g. `CHECK_PIXELS_LOGS=true CHECK_TRINO=true CHECK_TRINO_CLUSTER_STATUS=true CHECK_TRINO_CLI=true`. `SHOW SCHEMAS` success is enough before data is loaded; do not require returned schemas to be non-empty.98- **optional_trino_install**: only when the user explicitly asks. Clarify the topology first: coordinator vs. worker(s), how many workers, whether the coordinator also accepts scheduled work (`node-scheduler.include-coordinator`; official docs recommend `false` for a dedicated coordinator, `true` for a single-node test setup), `TRINO_INSTALL_PARENT`, `TRINO_HOME_LINK`, and `TRINO_DATA_DIR`. Never infer a fixed Trino cluster shape; use the user's topology or `prepare_trino_cluster.sh`'s confirmed output. For a Trino cluster whose remote nodes should not build Pixels, run `build_pixels_trino_artifacts.sh` once where Pixels has already been `mvn install`ed, or pass `PIXELS_TRINO_CONNECTOR_ZIP` and `PIXELS_TRINO_LISTENER_ZIP` explicitly to `install_trino_cluster.sh`; the remote nodes should only receive Trino plus those two plugins, Trino catalog/listener configs, and the lightweight Trino-side Pixels client config under `TRINO_PIXELS_HOME` (default `~/opt/pixels`). They must not be required to receive the full Pixels install, Maven, or source checkout. Run `prepare_trino_cluster.sh --setup-ssh true`, then run `install_trino_cluster.sh` **on the coordinator** to install Trino on the coordinator and fan out to every worker in parallel over SSH, instead of repeating `install_trino.sh` by hand once per node. Configure Trino's `etc/catalog/pixels.properties` as a Trino catalog file only; it is not `$PIXELS_HOME/etc/pixels.properties`. For the Trino-side Pixels client config, set `PIXELS_HOME`/`TRINO_PIXELS_HOME` to the parent directory (e.g. `~/opt/pixels`, never `~/opt/pixels/etc`) and set/export `PIXELS_CONFIG=$PIXELS_HOME/etc/pixels.properties`; all Trino nodes need this because the pixels-trino connector initializes on workers as well as the coordinator. `install_trino.sh` writes those two exports directly to the user's bash/zsh profile. When the skill itself starts, stops, checks, or runs CLI against Trino from a non-interactive SSH command, inline `PIXELS_HOME` and `PIXELS_CONFIG` in that command because remote non-interactive shells may not read `.bashrc`/`.zshrc`. Ensure `metadata.server.host`, `trans.server.host`, `query.schedule.server.host`, and `etcd.hosts` are reachable from every Trino node and are not accidental `localhost` values in a separated topology. Configure `presto.pixels.jdbc.url` only once the coordinator's endpoint and catalog/schema values are known. Offer `install_trino_shell_helpers.sh` only after the user agrees (it asks by default anyway).99- **optional_hadoop_or_monitoring**: out of scope unless the user asks — see Optional Components below.100101### Failure handling102103`check_prerequisites.sh` and `smoke_test.sh` never stop at the first failed check — they run every check and print a structured summary block (`=== <script> result ===` … `summary: ok=N warn=N fail=N skip=N status=pass|fail` … `=== end <script> result ===`). When `status=fail`:1041051. Read every `fail` line in the block, not just the first — they are independent and often have independent causes (e.g. a port conflict and a missing config property are unrelated problems, both worth knowing about before acting).1062. For each `fail`, decide whether it is something this skill should fix directly (e.g. an expected service genuinely isn't running yet — wait and re-check), something that needs a user decision (e.g. a port is held by an unrelated process — ask whether to stop it or change Pixels' port), or something informational that the user already knows about (e.g. a deliberately non-Ubuntu host).1073. Don't re-run the same script in a loop hoping it passes — fix (or get a decision on) the specific `fail` line first, then re-run once to confirm.1084. `warn` and `skip` lines never fail the run by themselves, but still read them — a `warn` is frequently the first sign of an edge case worth flagging to the user even though it didn't block anything.109110The same applies to `install_trino_cluster.sh`'s per-node summary: a `fail` for one worker doesn't mean the whole cluster install failed — check which specific node(s) failed, read that node's log under `STATE_DIR/logs/`, and retry only that node rather than re-running the whole fan-out.111112### Optional components113114Do not install these unless the user explicitly asks for the corresponding later stage:115116- Trino or another query engine — see the dedicated Trino steps above and the scripts list; this skill can now install Trino itself when asked, instead of only pointing at the external `pixels-trino` documentation.117- Hadoop or HDFS configuration.118- AWS credentials.119- Prometheus, Grafana, node exporter, or JMX exporter.120- Pixels Cache, Turbo, Retina, or Amphi.121122The basic Pixels install flow may configure `presto.pixels.jdbc.url` only after the Trino endpoint and catalog/schema values are known.123124Trino-side Pixels client config is not a full Pixels installation. A Trino node does not need `$PIXELS_HOME/bin`, `$PIXELS_HOME/sbin`, `$PIXELS_HOME/lib`, `pixels-daemon-*-full.jar`, `pixels-cli-*-full.jar`, Maven, or the Pixels source tree just to run the pixels-trino connector. It does need a readable `PIXELS_CONFIG`/`$PIXELS_HOME/etc/pixels.properties` with reachable Pixels metadata, transaction, query-schedule, etcd, and storage settings, plus access to the underlying data storage (for example shared local paths, HDFS configs, or S3/MinIO credentials depending on table storage).125126### Examples127128Prepare a three-node Pixels cluster config and SSH (run from this skill directory, or invoke the script by absolute path):129130```bash131./scripts/prepare_deployment.sh \132 --ssh-user root \133 --setup-ssh true \134 --coordinator-is-worker false \135 --pixels-home /home/pixels/opt/pixels \136 --coordinator 10.0.0.11,10.0.0.11,pixels-coordinator,root,22 \137 --worker 10.0.0.12,10.0.0.12,pixels-worker-1,root,22 \138 --worker 10.0.0.13,10.0.0.13,pixels-worker-2,root,22139```140141```bash142./scripts/check_prerequisites.sh143```144145Prepare a 1-coordinator/2-worker Trino cluster, SSH, then install Trino on every node in parallel (run on the coordinator):146147```bash148./scripts/prepare_trino_cluster.sh \149 --ssh-user root \150 --setup-ssh true \151 --coordinator 10.0.0.11,10.0.0.11,trino-coordinator,root,22 \152 --worker 10.0.0.12,10.0.0.12,trino-worker-1,root,22 \153 --worker 10.0.0.13,10.0.0.13,trino-worker-2,root,22154155./scripts/install_trino_cluster.sh156```157158Check (and resume from) the recorded install progress:159160```bash161./scripts/progress.sh show162./scripts/progress.sh mark check_prerequisites163```164165### Guardrails166167- Prefer `docs/INSTALL.md` plus targeted helper scripts over a fixed all-in-one install command.168- Do not run scripts that install packages, change services, update credentials, or start daemons unless the user is asking to perform that installation stage.169- Do not modify Git state.170- When modifying this skill's source, keep changes limited to `skills/pixels-install/` unless the user explicitly requests more.171- Ask before handling secrets, installing downloaded JDK packages, installing MySQL as an alternative metadata backend, changing MySQL root authentication, enabling remote database access, installing the etcd `systemd` auto-start unit, or installing the Trino cluster shell helpers.172- Use Derby plus pixels-cli `INIT-META` as the default metadata path. Do not install MySQL unless the user explicitly asks for it.173- Ask before installing Pixels shell helpers or Trino shell helpers into the user's shell profile; do not silently add operational functions.174- For management shell helpers, the default target is the relevant coordinator, not the current agent host: Pixels helpers go to `PIXELS_COORDINATOR_SSH_TARGET`, and Trino helpers go to `TRINO_COORDINATOR_SSH_TARGET`. Use `PIXELS_SHELL_HELPERS_TARGET=local` or `TRINO_SHELL_HELPERS_TARGET=local` only after the user explicitly asks for local-only helper functions.175- Ask or use the interactive preparation scripts before choosing `PIXELS_HOME`, Trino install/data paths, single-node vs. cluster mode, coordinator role, worker list, or whether a coordinator also runs worker tasks. Never let the skill silently accept topology/path defaults on the user's behalf.176- In non-interactive runs, only set `CONFIRM_PIXELS_DEPLOYMENT=true`, `CONFIRM_PIXELS_INSTALL=true`, `CONFIRM_TRINO_DEPLOYMENT=true`, `CONFIRM_TRINO_INSTALL=true`, or `CONFIRM_TRINO_CLUSTER_INSTALL=true` after showing the user the exact topology and paths that will be written or installed.177- Never hardcode `/home/pixels`, `/home/ubuntu`, or any other specific home directory in a command; always resolve paths from `$HOME` for whichever user is actually running the install.178- Never persist `JAVA_HOME`, `MAVEN_HOME`, `ETCD`, `PIXELS_HOME`, or `PIXELS_CONFIG` into a global file (`/etc/environment`, `/etc/profile.d/*`); only append to the current user's shell profile, matching their actual login shell.179- For Trino nodes, never set `PIXELS_HOME` to the `etc` directory. It must be the parent directory (for example `$HOME/opt/pixels`), with `PIXELS_CONFIG=$PIXELS_HOME/etc/pixels.properties`.180- Do not wrap or replace Trino's `bin/launcher` to smuggle Pixels environment variables, and do not create a separate `~/.pixels-trino-env.sh`. Persist `PIXELS_HOME` and `PIXELS_CONFIG` directly in the user's bash/zsh profile; generated helpers and skill-run SSH commands must inline-export them before invoking Trino.181- Do not create SSH tunnels for etcd. If Trino/Pixels nodes must access etcd remotely, configure etcd/network access explicitly and restrict the allowed source IP ranges to trusted Pixels and Trino nodes.182- Never accept the documented default MySQL passwords (`password`) without an explicit interactive confirmation from the user; `install_mysql.sh` enforces this by prompting even when a default is acceptable.183- Never run `install_trino.sh` (directly or via `install_trino_cluster.sh`) against a node without first knowing its role (coordinator vs. worker) from `trino-deployment.env` or an explicit `TRINO_ROLE`; `discovery.uri` must point at the coordinator's real IP, never `localhost`.184- Never put Pixels client/runtime properties such as `metadata.server.host`, `trans.server.host`, `query.schedule.server.host`, or `etcd.hosts` into Trino's `etc/catalog/pixels.properties`. Keep them only in the Trino-side Pixels client config (`PIXELS_CONFIG`).185- Preserve the versioned plugin directory names produced by unzipping `pixels-trino-connector-*.zip` and `pixels-trino-listener-*.zip`; do not normalize them to unversioned names.186- When a structured result summary reports `status=fail`, address (or get a user decision on) every `fail` line before re-running — see "Failure handling" above. Don't paper over a `fail` by re-running the script in a loop.187- `STATE_DIR/deployment.env`, `STATE_DIR/deployment.secrets.env`, `STATE_DIR/toolchain.env`, `STATE_DIR/trino-deployment.env`, `STATE_DIR/progress.env`, and `STATE_DIR/logs/` are generated, user-specific, and must never be committed.