Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Authenticate to your registry before selecting the remote context: the gateway d

An organization admin must request Gen2 builder activation first. Composal provisions a separate `ci-gen2` builder through the organization API with `backend: gen2`; an existing Classic builder cannot be converted in place. The token needs membership in that organization. Setup fails if the builder is missing, unavailable, or refused; it never falls back to a local build or creates a cache accidentally.

Inputs are `token` and `org` (required), `builder` (default `main-gen2`), `wake-timeout` (default `900` seconds, range `30`–`3600`), `use` (default `true`), `api-url` (default `https://composal.ai`), and `install-cli` (default `auto`). Automatic mode reuses a preinstalled stable `com` version 1.17.92 or newer within major version 1, including the CLI baked into Gen2 runners; otherwise it installs the latest release privately and adds it to the job PATH. Set `install-cli: 'true'` to always install, or `'false'` to require a compatible installed CLI. Docker and Buildx must be installed on the runner. Outputs are `builder`, `context`, and `driver` (`docker`). Each job isolates CLI supervisor state in its temporary directory. Setup logs the CLI version, checks gateway health and waits for the shared builder to become ready before opening native Docker connections. Cold starts can take several minutes; `wake-timeout` controls this wait. Progress includes the builder state and generation. Setup requests a retry when it finds a builder already starting, so a failed earlier wake can resume. Canceling a job disconnects only that job; it does not cancel the shared wake. It retries transient status-read server errors, then checks the remote daemon with bounded, retryable probes before selecting the context and builder for subsequent job steps using `DOCKER_CONTEXT` and `BUILDX_BUILDER`. CLI setup verifies that Buildx is installed; the build performs BuildKit discovery once, without a redundant setup inspection. It leaves Docker's saved defaults unchanged and restores the previous job environment during cleanup.
Inputs are `token` and `org` (required), `builder` (default `main-gen2`), `wake-timeout` (default `900` seconds, range `30`–`3600`), `use` (default `true`), `api-url` (default `https://composal.com`), and `install-cli` (default `auto`). Automatic mode reuses a preinstalled stable `com` version 1.17.92 or newer within major version 1, including the CLI baked into Gen2 runners; otherwise it installs the latest release privately and adds it to the job PATH. Set `install-cli: 'true'` to always install, or `'false'` to require a compatible installed CLI. Docker and Buildx must be installed on the runner. Outputs are `builder`, `context`, and `driver` (`docker`). Each job isolates CLI supervisor state in its temporary directory. Setup logs the CLI version, checks gateway health and waits for the shared builder to become ready before opening native Docker connections. Cold starts can take several minutes; `wake-timeout` controls this wait. Progress includes the builder state and generation. Setup requests a retry when it finds a builder already starting, so a failed earlier wake can resume. Canceling a job disconnects only that job; it does not cancel the shared wake. It retries transient status-read server errors, then checks the remote daemon with bounded, retryable probes before selecting the context and builder for subsequent job steps using `DOCKER_CONTEXT` and `BUILDX_BUILDER`. CLI setup verifies that Buildx is installed; the build performs BuildKit discovery once, without a redundant setup inspection. It leaves Docker's saved defaults unchanged and restores the previous job environment during cleanup.

If a startup attempt fails, setup reports its generation, stage and failure reason immediately. A fresh job can retry a failed startup against the same builder and retained cache. Simultaneous jobs share that startup; canceling one job keeps the shared startup running.

Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ inputs:
default: 'true'
api-url:
description: Composal API origin.
default: https://composal.ai
default: https://composal.com
install-cli:
description: 'auto reuses a compatible installed CLI, otherwise installs privately; true always installs; false requires an installed CLI.'
default: 'auto'
Expand Down
8 changes: 5 additions & 3 deletions builder.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export async function setup(env = process.env, run = command, wait = delay) {
const builder = input(env, 'builder', 'main-gen2');
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(org)) throw new Error('org is required and must be a slug or public ID');
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(builder)) throw new Error('builder must be a slug or public ID');
const api = new URL(input(env, 'api-url', 'https://composal.ai'));
const api = new URL(input(env, 'api-url', 'https://composal.com'));
if (api.protocol !== 'https:' || api.username || api.password || api.pathname !== '/' || api.search || api.hash) {
throw new Error('api-url must be an HTTPS origin');
}
Expand Down Expand Up @@ -95,8 +95,10 @@ export async function setup(env = process.env, run = command, wait = delay) {
const directory = join(env.RUNNER_TEMP, `com-docker-builder-${randomUUID()}`);
await mkdir(directory, { recursive: true, mode: 0o700 });
const script = join(directory, 'install.sh');
await run('curl', ['-fsSL', '--max-time', '60', 'https://dl.composal.ai/install.sh', '-o', script], cleanEnv);
await run('sh', [script], { ...cleanEnv, VEX_INSTALL_DIR: directory }, 300_000);
const downloadOrigin = ['https://composal.ai', 'https://vex.sc', 'https://api.vex.sc'].includes(api.origin)
? 'https://dl.composal.ai' : 'https://dl.composal.com';
await run('curl', ['-fsSL', '--max-time', '60', `${downloadOrigin}/install.sh`, '-o', script], cleanEnv);
await run('sh', [script], { ...cleanEnv, VEX_INSTALL_DIR: directory, VEX_API_BASE_URL: api.origin }, 300_000);
cli = join(directory, 'com');
if (!env.GITHUB_PATH) throw new Error('GITHUB_PATH is required');
await appendFile(env.GITHUB_PATH, `${dirname(cli)}\n`);
Expand Down
16 changes: 16 additions & 0 deletions builder.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -468,3 +468,19 @@ test('explicit preinstalled mode rejects an unsupported CLI without installing',
await assert.rejects(setup(f.env, run), /1.17.92 or newer/);
assert.equal(f.calls.length, 0);
});


test('installation and CLI calls follow the selected hosted API', async t => {
for (const [api, cdn] of [
['https://composal.com', 'https://dl.composal.com'],
['https://composal.ai', 'https://dl.composal.ai'],
['https://private.example', 'https://dl.composal.com'],
]) {
const f = await fixture(t, { 'INPUT_INSTALL-CLI': 'true', 'INPUT_API-URL': api });
await setup(f.env, f.run);
const download = f.calls.find(c => c.executable === 'curl');
assert.ok(download.args.includes(`${cdn}/install.sh`));
assert.equal(f.calls.find(c => c.executable === 'sh').env.VEX_API_BASE_URL, api);
assert.equal(f.calls.find(c => c.args[1] === 'status').env.VEX_API_BASE_URL, api);
}
});