diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0999e06..c9b5e16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1412,20 +1412,22 @@ jobs: - name: Run, build and deploy the demo apps timeout-minutes: 25 run: | + repo="$PWD" + cd build/workspace answers() { curl --fail --silent --show-error --max-time 10 \ -H "Host: $app.default.localhost" http://127.0.0.1:8080/test \ | tee /dev/stderr | grep "hello from $app" > /dev/null } for app in demo-app second-app; do - build/ly scaffold service "$app" + "$repo/build/ly" scaffold service "$app" service="services/$app" - source scripts/runtime-examples.sh + source "$repo/scripts/runtime-examples.sh" expose_workspace_services "$PWD" # Each app says its own name, so a response shows which one answered. sed --in-place "s/hello from Hyperlight edge/hello from $app/" "$service/handler.js" grep --quiet "hello from $app" "$service/handler.js" - build/ly dev --port 8080 "$service" > "$RUNNER_TEMP/$app-dev.log" 2>&1 & + "$repo/build/ly" dev --port 8080 "$service" > "$RUNNER_TEMP/$app-dev.log" 2>&1 & dev=$! ok= for _ in $(seq 120); do @@ -1435,8 +1437,8 @@ jobs: done if [ -z "$ok" ]; then cat "$RUNNER_TEMP/$app-dev.log"; exit 1; fi - build/ly build "$service" - build/ly deploy --image "localhost:5002/$app:v1" "$service" + "$repo/build/ly" build "$service" + "$repo/build/ly" deploy --image "localhost:5002/$app:v1" "$service" # ly build rewrote the rootfs that ly dev is running from. kill -0 "$dev" || { cat "$RUNNER_TEMP/$app-dev.log"; exit 1; } answers || { cat "$RUNNER_TEMP/$app-dev.log"; exit 1; } @@ -1457,6 +1459,8 @@ jobs: - name: Call the deployed apps through Kourier timeout-minutes: 10 run: | + repo="$PWD" + cd build/workspace kubectl port-forward -n kourier-system svc/kourier 8080:80 & forward=$! trap 'kill "$forward" 2> /dev/null || true' EXIT @@ -1466,10 +1470,10 @@ jobs: | tee /dev/stderr | grep "hello from $app" > /dev/null done # Free the smoke-test homes before the walkthrough deploys its own app. - build/ly delete services/demo-app - build/ly delete services/second-app + "$repo/build/ly" delete services/demo-app + "$repo/build/ly" delete services/second-app # The walkthrough owns a separate disposable installation and registry. - build/ly cluster uninstall --provider kind --delete-cluster + "$repo/build/ly" cluster uninstall --provider kind --delete-cluster - name: Check that /dev/kvm is unchanged if: always() diff --git a/Justfile b/Justfile index a6ee584..7f58fe0 100644 --- a/Justfile +++ b/Justfile @@ -198,13 +198,16 @@ install-mm: mkdir -p "{{repo-root}}/build/tools"; \ tar --extract --gzip --file "$dir/mm.tar.gz" --directory "{{repo-root}}/build/tools" mm +prepare-workspace: build-ly + if [[ ! -f build/workspace/lampyr.workspace.yaml ]]; then build/ly scaffold workspace build/workspace; fi + # Install the complete preview platform into a Kind cluster. -cluster-install: build-ly - build/ly cluster install --provider kind --allow-docker +cluster-install: prepare-workspace + cd build/workspace && ../ly cluster install --provider kind --allow-docker # Install Lampyr using the independently packaged k3d provider. -cluster-install-k3d: build-ly - build/ly cluster install --provider k3d --allow-docker +cluster-install-k3d: prepare-workspace + cd build/workspace && ../ly cluster install --provider k3d --allow-docker # Test the provider lifecycle and every committed runtime example on k3d. test-k3d-integration: build-ly k3d::build cli::runtime-images @@ -212,26 +215,28 @@ test-k3d-integration: build-ly k3d::build cli::runtime-images # Report the health of every required preview platform component. cluster-health: build-ly - build/ly cluster health + cd build/workspace && ../ly cluster health # Remove preview platform resources. cluster-uninstall: build-ly - build/ly cluster uninstall + cd build/workspace && ../ly cluster uninstall # Create or configure an AKS cluster with the latest regional Kubernetes version. -cluster-install-azure config name="lampyr": build-ly - build/ly cluster install --provider azure --name "{{name}}" --provider-config "{{config}}" --approve-provider-manifests +cluster-install-azure config name="lampyr": prepare-workspace + config="{{config}}"; \ + if [[ "$config" != /* ]]; then config="$PWD/$config"; fi; \ + cd build/workspace && ../ly cluster install --provider azure --name "{{name}}" --provider-config "$config" --approve-provider-manifests # Report AKS and Lampyr platform health. cluster-health-azure name="lampyr": build-ly - build/ly cluster health --context "aks-{{name}}" + cd build/workspace && ../ly cluster health --context "aks-{{name}}" # Remove Lampyr from AKS while retaining the cluster by default. cluster-uninstall-azure name="lampyr" delete-cluster="false": build-ly args=(--provider azure --name "{{name}}" --context "aks-{{name}}" --approve-provider-manifests); \ if [[ "{{delete-cluster}}" == "true" ]]; then args+=(--delete-cluster); fi; \ - build/ly cluster uninstall "${args[@]}" + cd build/workspace && ../ly cluster uninstall "${args[@]}" # Build every locally produced binary. build: setup-hluk build-fiberd build-control-plane build-ly build-eventgrid diff --git a/README.md b/README.md index 7dda745..e242d1c 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,20 @@ short-lived, hardware-isolated microVMs when work arrives. ## Getting started +### Prerequisites + +Install [Docker with Buildx](https://docs.docker.com/get-started/get-docker/) +and [kubectl](https://kubernetes.io/docs/tasks/tools/) on your `PATH`. +For the release installer below, also install the GitHub CLI (`gh`) and +authenticate with `gh auth login`. + +On Linux, also enable [KVM](https://documentation.ubuntu.com/server/how-to/virtualisation/libvirt/) +(Ubuntu setup guide). Your user must have read/write access to `/dev/kvm`. + ### Install the CLI Install `ly` and its platform bundle from the latest release on Linux/amd64 or -macOS/arm64. The installer checks every archive's checksum and GitHub +macOS/arm64. Use WSL on Windows. The installer checks every archive's checksum and GitHub attestation; see [`ly setup`](docs/cli/setup.md) for the verified install. ```bash @@ -50,7 +60,8 @@ export PATH="$HOME/.local/bin:$PATH" ly doctor ``` -To build `ly` from a checkout instead, run `just build-ly`. +To build `ly` from a checkout instead, run `just build-ly` and add it to your +`PATH` with `export PATH="$PWD/build:$PATH"`. ### Walkthrough diff --git a/cli/tests/dev.rs b/cli/tests/dev.rs index f3042a3..ee87be9 100644 --- a/cli/tests/dev.rs +++ b/cli/tests/dev.rs @@ -6,7 +6,7 @@ use std::process::Command; use lampyr_cli::project::Project; fn fixture() -> std::path::PathBuf { - std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../samples/local-worker-workspace") + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../demos/local-worker-workspace") } #[test] diff --git a/samples/local-worker-workspace/.gitignore b/demos/local-worker-workspace/.gitignore similarity index 100% rename from samples/local-worker-workspace/.gitignore rename to demos/local-worker-workspace/.gitignore diff --git a/samples/local-worker-workspace/lampyr.workspace.yaml b/demos/local-worker-workspace/lampyr.workspace.yaml similarity index 100% rename from samples/local-worker-workspace/lampyr.workspace.yaml rename to demos/local-worker-workspace/lampyr.workspace.yaml diff --git a/samples/local-worker-workspace/services/api/Dockerfile b/demos/local-worker-workspace/services/api/Dockerfile similarity index 100% rename from samples/local-worker-workspace/services/api/Dockerfile rename to demos/local-worker-workspace/services/api/Dockerfile diff --git a/samples/local-worker-workspace/services/api/Dockerfile.rootfs b/demos/local-worker-workspace/services/api/Dockerfile.rootfs similarity index 100% rename from samples/local-worker-workspace/services/api/Dockerfile.rootfs rename to demos/local-worker-workspace/services/api/Dockerfile.rootfs diff --git a/samples/local-worker-workspace/services/api/lampyr.yaml b/demos/local-worker-workspace/services/api/lampyr.yaml similarity index 100% rename from samples/local-worker-workspace/services/api/lampyr.yaml rename to demos/local-worker-workspace/services/api/lampyr.yaml diff --git a/samples/local-worker-workspace/services/api/server.js b/demos/local-worker-workspace/services/api/server.js similarity index 100% rename from samples/local-worker-workspace/services/api/server.js rename to demos/local-worker-workspace/services/api/server.js diff --git a/samples/local-worker-workspace/services/processor/Dockerfile b/demos/local-worker-workspace/services/processor/Dockerfile similarity index 100% rename from samples/local-worker-workspace/services/processor/Dockerfile rename to demos/local-worker-workspace/services/processor/Dockerfile diff --git a/samples/local-worker-workspace/services/processor/Dockerfile.rootfs b/demos/local-worker-workspace/services/processor/Dockerfile.rootfs similarity index 100% rename from samples/local-worker-workspace/services/processor/Dockerfile.rootfs rename to demos/local-worker-workspace/services/processor/Dockerfile.rootfs diff --git a/samples/local-worker-workspace/services/processor/lampyr.yaml b/demos/local-worker-workspace/services/processor/lampyr.yaml similarity index 100% rename from samples/local-worker-workspace/services/processor/lampyr.yaml rename to demos/local-worker-workspace/services/processor/lampyr.yaml diff --git a/samples/local-worker-workspace/services/processor/server.js b/demos/local-worker-workspace/services/processor/server.js similarity index 100% rename from samples/local-worker-workspace/services/processor/server.js rename to demos/local-worker-workspace/services/processor/server.js diff --git a/samples/local-worker-workspace/services/reused/Dockerfile b/demos/local-worker-workspace/services/reused/Dockerfile similarity index 100% rename from samples/local-worker-workspace/services/reused/Dockerfile rename to demos/local-worker-workspace/services/reused/Dockerfile diff --git a/samples/local-worker-workspace/services/reused/Dockerfile.rootfs b/demos/local-worker-workspace/services/reused/Dockerfile.rootfs similarity index 100% rename from samples/local-worker-workspace/services/reused/Dockerfile.rootfs rename to demos/local-worker-workspace/services/reused/Dockerfile.rootfs diff --git a/samples/local-worker-workspace/services/reused/lampyr.yaml b/demos/local-worker-workspace/services/reused/lampyr.yaml similarity index 100% rename from samples/local-worker-workspace/services/reused/lampyr.yaml rename to demos/local-worker-workspace/services/reused/lampyr.yaml diff --git a/samples/local-worker-workspace/services/reused/server.js b/demos/local-worker-workspace/services/reused/server.js similarity index 100% rename from samples/local-worker-workspace/services/reused/server.js rename to demos/local-worker-workspace/services/reused/server.js diff --git a/docs/cli/cluster.md b/docs/cli/cluster.md index d823f60..d709399 100644 --- a/docs/cli/cluster.md +++ b/docs/cli/cluster.md @@ -9,11 +9,20 @@ is optional: its guest only provisions infrastructure and returns a standard kub `ly` owns Kubernetes, platform installation, Eventing, source lifecycle, and health; it contains no Kind, k3d, or Azure provisioning implementation. +From a checkout, put `ly` on your `PATH`, then create and enter a Workspace. +If you already have a Workspace, enter it instead of scaffolding another: + ```bash -build/ly cluster install --provider kind --name lampyr \ - --allow-docker -build/ly cluster health -build/ly cluster uninstall --delete-cluster +repo_root="$PWD" +export PATH="$repo_root/build:$PATH" +ly scaffold workspace cluster-demo +cd cluster-demo +``` + +```bash +ly cluster install --provider kind --name lampyr --allow-docker +ly cluster health +ly cluster uninstall --delete-cluster ``` Without `--provider`, commands use the existing kubeconfig/context directly: @@ -29,9 +38,9 @@ domain: services.example.com ``` ```bash -build/ly cluster install --context customer -build/ly cluster health --context customer -build/ly cluster uninstall --context customer +ly cluster install --context customer +ly cluster health --context customer +ly cluster uninstall --context customer ``` The defaults are `ghcr.io/hyperlight-dev/lampyr/fiberd:preview` and @@ -61,6 +70,13 @@ All cluster lifecycle commands read bootstrap manifests from the Workspace's customer-editable; Lampyr Home retains the immutable runtime and provider artifacts. +Existing installations remain bound to their original Workspace and are not +moved automatically. If an installation used the checkout root, manage it +from the previous checkout revision that still has its manifest. Use a new +installation name for a separate Workspace; `--workspace` does not reassign +an existing installation. Keep the old installation state until its resources +have been accounted for and cleaned up. + | Option | Purpose | |---|---| | `--provider ID` | External provider identity, not a built-in enumeration. | @@ -92,8 +108,7 @@ registryPort: 5002 ``` ```bash -build/ly cluster install --provider kind --provider-config kind.yaml \ - --allow-docker +ly cluster install --provider kind --provider-config kind.yaml --allow-docker ``` The provider scaffolds complete infrastructure into `infra//` @@ -104,7 +119,7 @@ k3d accepts the same port and prebuilt-image settings, with a k3d v1alpha5 `source` rather than Kind YAML: ```bash -build/ly cluster install --provider k3d --allow-docker +ly cluster install --provider k3d --allow-docker ``` It owns a dedicated registry rather than reusing a shared one. Select an @@ -139,7 +154,7 @@ zygoteRegistries: ``` ```bash -build/ly cluster install --provider azure --eventing \ +ly cluster install --provider azure --eventing \ --provider-config azure.yaml \ --approve-provider-manifests ``` diff --git a/docs/cli/dev.md b/docs/cli/dev.md index 61f38ad..5291565 100644 --- a/docs/cli/dev.md +++ b/docs/cli/dev.md @@ -166,13 +166,13 @@ never be copied to a different architecture or hypervisor for deployment. ### Executable multi-service example -`samples/local-worker-workspace/` contains dependency-free Node.js +`demos/local-worker-workspace/` contains dependency-free Node.js Standalone Applications. Its API has several HTTP handlers, calls a fresh processor, emits a CloudEvent to that processor, and calls a service explicitly configured for reuse: ```bash -ly dev samples/local-worker-workspace +ly dev demos/local-worker-workspace curl http://worker-api.default.localhost:8080/invoke curl http://worker-api.default.localhost:8080/emit curl http://worker-api.default.localhost:8080/reuse @@ -269,7 +269,7 @@ No Hyperlight source checkout, Cargo overrides or runtime descriptor is needed: ```bash just build-ly -build/ly dev samples/local-worker-workspace --port 8080 +build/ly dev demos/local-worker-workspace --port 8080 ``` Lampyr downloads and verifies the released v0.21.0 executable and matching diff --git a/docs/cluster-k3d.md b/docs/cluster-k3d.md index 36d424b..b0078af 100644 --- a/docs/cluster-k3d.md +++ b/docs/cluster-k3d.md @@ -30,14 +30,19 @@ from (see [Preview images](contributing.md#preview-images)): `just preview-mirror-images` writes their `controllerManifest`, `grantImage` and `helperImage` to `build/preview-kind.yaml`, whose keys the k3d provider takes too. `just template-tools` builds the fiberd template tools that -`ly build` and `ly deploy` run. Then, from the repository Workspace: +`ly build` and `ly deploy` run. Then, from the checkout, create a Workspace +and enter it before running cluster commands: ```bash +repo_root="$PWD" just preview-mirror-images destination=REGISTRY/lampyr just build-ly k3d::build cli::runtime-images template-tools -build/ly cluster install --provider k3d --name lampyr-k3d \ - --provider-config build/preview-kind.yaml --allow-docker -build/ly cluster health --context k3d-lampyr-k3d +export PATH="$repo_root/build:$PATH" +ly scaffold workspace k3d-demo +cd k3d-demo +ly cluster install --provider k3d --name lampyr-k3d \ + --provider-config "$repo_root/build/preview-kind.yaml" --allow-docker +ly cluster health --context k3d-lampyr-k3d ``` `just use-cluster-inputs` does not work for k3d: it serves built images from @@ -58,7 +63,7 @@ takes one `--provider-config` file, so start it from a copy of `build/preview-kind.yaml` to keep the controller inputs: ```bash -cp build/preview-kind.yaml k3d.yaml +cp "$repo_root/build/preview-kind.yaml" k3d.yaml ``` ```yaml @@ -69,7 +74,7 @@ httpsPort: 18443 ``` ```bash -build/ly cluster install --provider k3d --name lampyr-k3d \ +ly cluster install --provider k3d --name lampyr-k3d \ --provider-config k3d.yaml --allow-docker ``` @@ -85,14 +90,13 @@ All six [runtime examples](examples.md) are provider-neutral: Node.js, Python, Go, Rust, and .NET use the same source and manifests on Kind and k3d. There are no k3d-specific forks to keep synchronized. -Create a workspace and copy the example into its services directory: +Copy the example into the Workspace created above: ```bash -build/ly scaffold workspace k3d-demo -cp -R docs/examples/hello-nodejs k3d-demo/services/hello-nodejs +cp -R "$repo_root/docs/examples/hello-nodejs" services/hello-nodejs ``` -In `k3d-demo/lampyr.workspace.yaml`, explicitly expose the example: +In `lampyr.workspace.yaml`, explicitly expose the example: ```yaml apiVersion: lampyr.dev/v1alpha1 @@ -104,9 +108,9 @@ ingress: Build and deploy using the default registry port: ```bash -build/ly build k3d-demo/services/hello-nodejs -build/ly deploy --context k3d-lampyr-k3d \ - --image localhost:5002/hello-nodejs:v1 k3d-demo/services/hello-nodejs +ly build services/hello-nodejs +ly deploy --context k3d-lampyr-k3d \ + --image localhost:5002/hello-nodejs:v1 services/hello-nodejs ``` Substitute any example directory and image name from the runtime table. If @@ -141,13 +145,13 @@ customer source. Remove only the platform, retaining K3s and the local registry: ```bash -build/ly cluster uninstall --context k3d-lampyr-k3d +ly cluster uninstall --context k3d-lampyr-k3d ``` Remove the owned cluster, tools container, image volume, registry, and network: ```bash -build/ly cluster uninstall --provider k3d --name lampyr-k3d --delete-cluster +ly cluster uninstall --provider k3d --name lampyr-k3d --delete-cluster ``` The provider refuses to adopt unrelated clusters or delete replaced diff --git a/docs/cluster-kind.md b/docs/cluster-kind.md index c5aaee8..25d8b09 100644 --- a/docs/cluster-kind.md +++ b/docs/cluster-kind.md @@ -24,20 +24,25 @@ With `ly` installed from a release (see the [README](https://github.com/hyperlight-dev/lampyr#install-the-cli)), the platform already carries the provider, runtime images and fiberd-k8s-controller inputs: -```bash -ly cluster install --provider kind --allow-docker -``` - -From a repository checkout: +From a repository checkout, prepare the CLI and runtime inputs instead: ```bash +repo_root="$PWD" just build-ly just kind::build just cli::runtime-images just upstream::cluster-inputs build/cluster-inputs LAMPYR_CLUSTER_INPUTS=$PWD/build/cluster-inputs just use-cluster-inputs 5002 -build/ly cluster install --provider kind \ - --allow-docker +export PATH="$repo_root/build:$PATH" +``` + +With either installation, create a Workspace and run the remaining commands +from inside it: + +```bash +ly scaffold workspace kind-demo +cd kind-demo +ly cluster install --provider kind --allow-docker ``` A release's platform installs fiberd-k8s-controller and the images its grant @@ -85,13 +90,12 @@ Deployment creates one grant Pod per CapacityGrant, running stock fiberd with Lampyr's helper. fiberd-k8s-controller issues each Pod's grant and renews it automatically. -Create a workspace, then build and deploy its default app to the local registry. -The scaffold exposes `app` through `ingress.services`: +Build and deploy the default app in the Workspace created above to the local +registry. The scaffold exposes `app` through `ingress.services`: ```bash -build/ly scaffold workspace kind-demo -build/ly build kind-demo/services/app -build/ly deploy --image localhost:5002/app:v1 kind-demo/services/app +ly build services/app +ly deploy --image localhost:5002/app:v1 services/app ``` `ly deploy` tags and pushes the locally built image automatically. Do not pass @@ -113,8 +117,7 @@ To add Knative Eventing and an InMemoryChannel-backed Broker, install explicitly with: ```bash -build/ly cluster install --provider kind --eventing \ - --allow-docker +ly cluster install --provider kind --eventing --allow-docker ``` This installs the pinned Eventing 1.23.0 manifests. The InMemoryChannel-backed @@ -124,13 +127,13 @@ available only on Azure-provider installations. ## Check health ```bash -build/ly cluster health +ly cluster health ``` ## Remove Lampyr ```bash -build/ly cluster uninstall +ly cluster uninstall ``` Add `--delete-cluster` to remove the Kind cluster as well. See diff --git a/lampyr.workspace.yaml b/lampyr.workspace.yaml deleted file mode 100644 index f127330..0000000 --- a/lampyr.workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: lampyr.dev/v1alpha1 -kind: Workspace diff --git a/scripts/test-kind-integration.sh b/scripts/test-kind-integration.sh index 85c935e..e5dff6f 100755 --- a/scripts/test-kind-integration.sh +++ b/scripts/test-kind-integration.sh @@ -92,6 +92,7 @@ fi if [[ "$suite" == "full" || "$suite" == "runtime" || "$runtime_extras" == 1 ]]; then prepare_fiberd_template_tools "$repo_root" fi +cd "$project" cleanup() { status=$? diff --git a/scripts/tests/test_preview_workflow.py b/scripts/tests/test_preview_workflow.py index d8866f7..314555f 100644 --- a/scripts/tests/test_preview_workflow.py +++ b/scripts/tests/test_preview_workflow.py @@ -604,6 +604,52 @@ def test_arm64_provider_builds_are_parallel_and_cached(self): self.assertIn("FROM --platform=$BUILDPLATFORM golang:", dockerfile) self.assertIn('GOOS="$TARGETOS" GOARCH="$TARGETARCH"', dockerfile) + def test_checkout_cluster_workflow_uses_a_separate_workspace(self): + self.assertFalse((ROOT / "lampyr.workspace.yaml").exists()) + justfile = (ROOT / "Justfile").read_text(encoding="utf-8") + self.assertIn("build/ly scaffold workspace build/workspace", justfile) + for recipe, arguments in [ + ("cluster-install", []), + ("cluster-install-k3d", []), + ("cluster-install-azure", ["provider.yaml"]), + ("cluster-health", []), + ("cluster-uninstall", []), + ("cluster-health-azure", []), + ("cluster-uninstall-azure", []), + ]: + with self.subTest(recipe=recipe): + dry_run = subprocess.run( + ["just", "--dry-run", recipe, *arguments], + cwd=ROOT, + check=True, + text=True, + capture_output=True, + ) + self.assertIn("cd build/workspace", dry_run.stderr) + self.assertNotIn("--workspace", dry_run.stderr) + if recipe.startswith("cluster-install"): + self.assertIn("scaffold workspace", dry_run.stderr) + job = self.ci["jobs"]["new-developer-workflow"] + apps = named(job, "Run, build and deploy the demo apps")["run"] + self.assertIn("cd build/workspace", apps) + self.assertIn('"$repo/build/ly" scaffold service "$app"', apps) + cleanup = named(job, "Call the deployed apps through Kourier")["run"] + self.assertIn("cd build/workspace", cleanup) + self.assertIn('"$repo/build/ly" delete services/demo-app', cleanup) + self.assertNotIn("--workspace", cleanup) + + def test_kind_lifecycle_enters_its_scaffolded_workspace(self): + script = KIND_INTEGRATION.read_text(encoding="utf-8") + self.assertIn('\ncd "$project"\n', script) + self.assertLess( + script.index('prepare_fiberd_template_tools "$repo_root"'), + script.index('\ncd "$project"\n'), + ) + self.assertLess( + script.index('\ncd "$project"\n'), + script.index('PATH="$project/docker-only-bin" "$repo_root/build/ly" cluster install'), + ) + def test_new_developer_timeout_leaves_diagnostic_headroom(self): job = self.ci["jobs"]["new-developer-workflow"] diagnostics = named(job, "Collect diagnostics")["timeout-minutes"] @@ -1036,7 +1082,7 @@ def test_documented_installs_take_the_built_controller_inputs_as_defaults(self): self.assertIn("registry_port=5500", walkthrough) self.assertIn("ly build services/app", walkthrough) self.assertIn( - 'build/ly build "$service"', + '"$repo/build/ly" build "$service"', named(jobs["new-developer-workflow"], "Run, build and deploy the demo apps")["run"], )