feat(build): optional xvfb-run for Linux GUI builds
Add an --xvfb switch to 'build run' and 'job' that wraps the Linux build command in 'xvfb-run -a sh -c ...', giving GUI toolkits (GTK4/PyGObject) a headless $DISPLAY. Exported env vars precede xvfb-run so they propagate into the virtual-display session. No-op (with a notice) for Windows guests. The local-ci-build composite action exposes it as the 'xvfb' input (default false), forwarded as INPUT_XVFB -> --xvfb, mirroring the use-shared-cache/skip-artifact wiring. Docs: WORKFLOW-AUTHORING.md parameter table + Linux example note; LINUX-TEMPLATE-SETUP.md Step 4a (the template ships python3-gi gir1.2-gtk-4.0 xvfb). Adds build-run unit tests (wrap, plain, Windows no-op). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -142,6 +142,7 @@ def _linux_build(
|
||||
artifact_source: str,
|
||||
extra_env: dict[str, str],
|
||||
skip_artifact: bool,
|
||||
use_xvfb: bool = False,
|
||||
) -> None:
|
||||
click.echo("[build run] Linux build mode (SSH transport)")
|
||||
|
||||
@@ -220,13 +221,21 @@ def _linux_build(
|
||||
f"export {k}={_sh_quote(v)}; " for k, v in extra_env.items()
|
||||
)
|
||||
cmd = build_command or "make"
|
||||
# Optionally run the build under a headless X server (xvfb-run) so GUI
|
||||
# toolkits (e.g. GTK4/PyGObject) have a $DISPLAY. The exported env vars
|
||||
# precede xvfb-run so they propagate into the virtual-display session;
|
||||
# -a auto-selects a free server number. The whole `cd && build` is
|
||||
# wrapped via `sh -c` so xvfb-run drives a single command.
|
||||
inner = f"cd {_sh_quote(workdir)} && {cmd}"
|
||||
run_part = f"xvfb-run -a sh -c {_sh_quote(inner)}" if use_xvfb else inner
|
||||
# PYTHONUNBUFFERED so the guest python flushes promptly and the
|
||||
# streamed output is timely rather than emitted in one block.
|
||||
full_cmd = (
|
||||
f"{env_prefix}export PYTHONUNBUFFERED=1; "
|
||||
f"cd {_sh_quote(workdir)} && {cmd}"
|
||||
full_cmd = f"{env_prefix}export PYTHONUNBUFFERED=1; {run_part}"
|
||||
xvfb_note = " [xvfb-run]" if use_xvfb else ""
|
||||
click.echo(
|
||||
f"[build run] running build{xvfb_note} (env vars redacted): "
|
||||
f"cd {workdir} && {cmd}"
|
||||
)
|
||||
click.echo(f"[build run] running build (env vars redacted): cd {workdir} && {cmd}")
|
||||
click.echo("[build run] ----- build output (live) -----")
|
||||
result = t.run_streaming(full_cmd, check=False)
|
||||
click.echo("[build run] ----- end build output -----")
|
||||
@@ -508,6 +517,13 @@ def _windows_build(
|
||||
@click.option("--configuration", default="Release", show_default=True)
|
||||
@click.option("--skip-artifact", "skip_artifact", is_flag=True, default=False)
|
||||
@click.option("--use-shared-cache", "use_shared_cache", is_flag=True, default=False)
|
||||
@click.option(
|
||||
"--xvfb",
|
||||
"use_xvfb",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Linux only: run the build under xvfb-run (headless X for GUI toolkits).",
|
||||
)
|
||||
@click.option(
|
||||
"--extra-env",
|
||||
"extra_env",
|
||||
@@ -536,6 +552,7 @@ def build_run(
|
||||
configuration: str,
|
||||
skip_artifact: bool,
|
||||
use_shared_cache: bool,
|
||||
use_xvfb: bool,
|
||||
extra_env: tuple[str, ...],
|
||||
) -> None:
|
||||
"""Run a build inside a guest VM, optionally packaging the result.
|
||||
@@ -589,10 +606,13 @@ def build_run(
|
||||
artifact_source=guest_artifact_source,
|
||||
extra_env=extra,
|
||||
skip_artifact=skip_artifact,
|
||||
use_xvfb=use_xvfb,
|
||||
)
|
||||
except (TransportError, TransportCommandError) as exc:
|
||||
raise click.ClickException(str(exc)) from exc
|
||||
else:
|
||||
if use_xvfb:
|
||||
click.echo("[build run] --xvfb ignored for Windows guest.")
|
||||
workdir = guest_work_dir or "C:\\CI\\build"
|
||||
output_dir = guest_output_dir or "C:\\CI\\output"
|
||||
target = credential_target or config.guest_cred_target
|
||||
|
||||
@@ -327,6 +327,13 @@ def _destroy_clone(
|
||||
)
|
||||
@click.option("--use-shared-cache", "use_shared_cache", is_flag=True, default=False)
|
||||
@click.option("--skip-artifact", "skip_artifact", is_flag=True, default=False)
|
||||
@click.option(
|
||||
"--xvfb",
|
||||
"use_xvfb",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Linux only: run the build under xvfb-run (headless X for GUI toolkits).",
|
||||
)
|
||||
@click.option(
|
||||
"--gitea-credential-target",
|
||||
"gitea_credential_target",
|
||||
@@ -395,6 +402,7 @@ def job(
|
||||
use_git_clone: bool,
|
||||
use_shared_cache: bool,
|
||||
skip_artifact: bool,
|
||||
use_xvfb: bool,
|
||||
gitea_credential_target: str,
|
||||
template_path: str | None,
|
||||
snapshot_name: str,
|
||||
@@ -615,6 +623,7 @@ def job(
|
||||
artifact_source=guest_artifact_source,
|
||||
extra_env=extra_env,
|
||||
skip_artifact=skip_artifact,
|
||||
use_xvfb=use_xvfb,
|
||||
)
|
||||
else:
|
||||
_windows_build(
|
||||
|
||||
Reference in New Issue
Block a user