# Openship runBuildPipeline Clone Install Build Steps: Inside the Build Pipeline

> Understand Openship's runBuildPipeline: discover the clone, install, and build steps within the BuildEnvironment for clear observability and performance insights.

- Repository: [oblien/openship](https://github.com/oblien/openship)
- Tags: internals
- Published: 2026-08-19

---

**Openship's `runBuildPipeline()` orchestrates a strict three-phase sequence—clone, install, and build—through the `BuildEnvironment` abstraction, wrapping each phase in `logger.runStep()` for structured observability and returning a final `{status, durationMs}` result.**

The `oblien/openship` repository is an open-source deployment platform that powers Docker Compose, bare-host, and cloud runtimes from a single control-plane codebase. Understanding the `runBuildPipeline` clone install build steps is critical for debugging deployments, extending adapters, or self-hosting the platform.

## Build Pipeline Architecture

According to [`packages/adapters/docs/BUILD-PIPELINE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/BUILD-PIPELINE.md) lines 5-22, the build service instantiates a `BuildLogger` and invokes the active runtime's `build(config, logger)` method. The pipeline remains runtime-agnostic because the `BuildEnvironment` abstracts all command execution through `exec` and `streamExec`. This lets the same clone-install-build logic execute on a local shell, over remote SSH, or inside a Docker container without code changes.

## The Three Steps of runBuildPipeline

### Clone

The pipeline begins by fetching the application source. It executes `git clone …` via `BuildEnvironment.exec`, which automatically resolves the correct transport layer—local shell, remote SSH, or Docker—based on the active runtime configuration. This is the first operation in the build flow described in [`packages/adapters/docs/BUILD-PIPELINE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/BUILD-PIPELINE.md).

### Install

Once the repository is available, the pipeline detects the project's package manager. It supports **npm**, **yarn**, and **pnpm**, then runs the appropriate install command through the executor. Dependencies must resolve successfully before the pipeline proceeds to the compilation phase.

### Build

If the project configuration defines a `buildCommand`, the pipeline executes it as the final step. The entire sequence returns `{status: "deploying" | "failed", durationMs}` and persists all output through `BuildLogger`. As documented in [`packages/adapters/docs/BUILD-PIPELINE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/BUILD-PIPELINE.md) lines 5-22, each step is wrapped in `logger.runStep()`, providing the single source of truth for real-time dashboard stepper updates and SSE log streaming.

## Runtime-Specific Build Transports

Each runtime implements the `build(config, logger)` interface, but the transport mechanism differs:

- **CloudRuntime** — Calls `Oblien API: workspace.build()` followed by `execAndStream()`. Remote containers and TLS are handled by the Oblien cloud edge.
- **BareRuntime** — Uses `executor.streamExec()` inside `runBuildPipeline()`. Commands run directly on the host or over SSH, making this the path for local and self-managed servers.
- **DockerRuntime** — Exists as a stub in the current source. It is intended to run Dockerfile builds natively, but the full implementation is not yet complete.

These architectures are detailed in [`packages/adapters/docs/ARCHITECTURE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/ARCHITECTURE.md).

## Process Management and Logging

Every step emits structured output through the `BuildLogger`. The `logger.runStep()` wrapper feeds live logs to the UI and ensures the dashboard stepper stays synchronized with the actual build state.

For **bare-mode deployments**, the deploy step spawns the application with `setsid nohup npm start &` to create a new process group. The resulting PID is written to `.pids/{deploymentId}.pid`, while application logs are written to `.logs/{deploymentId}.log`. When stopping a deployment, Openship reads the PID file and issues `kill -<pgid>`—first SIGTERM, then SIGKILL—to terminate the entire process tree cleanly. The full sequence is documented in [`packages/adapters/docs/BUILD-PIPELINE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/BUILD-PIPELINE.md) lines 78-106.

## Practical Build and Deploy Examples

### Run a Local Bare-Mode Deployment

```bash

# Install the CLI (bundles API + dashboard)

curl -fsSL https://get.openship.io | sh

# Initialize and deploy the current project

cd my-app
openship init
openship deploy

```

The `openship deploy` command triggers `runBuildPipeline()` using the same code path across Linux, macOS, and Windows.

### Start the Full Docker Compose Stack

```bash
git clone https://github.com/oblien/openship.git && cd openship
cp .env.example .env
docker compose -f docker/docker-compose.yml up -d

```

This launches **Postgres**, **Redis**, **API**, **Dashboard**, and **Edge** containers. The edge service listens on `:80` and `:443`, writes reverse-proxy vhosts, and obtains Let's Encrypt certificates automatically.

### Trigger a Cloud Runtime Build via REST

```bash
curl -X POST https://api.openship.io/v1/workspaces/<ws>/build \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"repo":"git@github.com:user/repo.git","branch":"main"}'

```

This initiates the **CloudRuntime** build flow, which calls `workspace.build()` internally and streams logs back to the client via SSE.

## Summary

- Openship's build pipeline executes a fixed three-step flow: **clone**, **install**, and **build**.
- `runBuildPipeline()` delegates to `BuildEnvironment.exec` and `executor.streamExec()`, making the pipeline portable across bare metal, cloud, and Docker runtimes.
- Each phase is wrapped in `logger.runStep()` for live dashboard updates and persistent `BuildLogger` output.
- **BareRuntime** manages application lifecycle through `setsid`, `.pids/{deploymentId}.pid`, and `.logs/{deploymentId}.log`.
- **CloudRuntime** delegates container builds to the Oblien API, while **DockerRuntime** remains a stub for future Dockerfile-native builds.

## Frequently Asked Questions

### How does Openship detect which package manager to use during the install step?

The pipeline inspects the cloned repository for lockfiles and automatically selects **npm**, **yarn**, or **pnpm**. It then invokes the corresponding install command through the active `BuildEnvironment` executor before proceeding to the build phase.

### What happens if a step fails inside `runBuildPipeline()`?

If any step fails, the pipeline immediately returns `{status: "failed", durationMs}` and streams the error details through `BuildLogger`. Because each step is wrapped in `logger.runStep()`, the dashboard UI reflects the failure in real time without polling.

### Where does BareRuntime store running process IDs?

BareRuntime writes the deployment PID to `.pids/{deploymentId}.pid` and application logs to `.logs/{deploymentId}.log`. To stop a deployment, Openship reads the PID file and kills the entire process group using the stored PGID, ensuring no orphan processes remain.

### Is the DockerRuntime build pipeline fully implemented?

No. According to [`packages/adapters/docs/ARCHITECTURE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/ARCHITECTURE.md), **DockerRuntime** is currently a stub. The `runBuildPipeline` architecture is designed to accommodate it, but native Dockerfile-based builds are not yet fully implemented in the source.