Skip to content

Commit 8c6f1ce

Browse files
committed
feat: setup buildx step
sets up docker buildx with the following configurations: - buildx version - builder name - driver opts - buildkit config
1 parent 59de3b7 commit 8c6f1ce

3 files changed

Lines changed: 276 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Setup Docker Buildx
2+
3+
Configure Docker Buildx to use a `docker-container` builder in your workflow.
4+
5+
Note: Docker support in Nx Cloud Workflows is an enterprise-only feature. To use this step, ensure you have Nx Cloud Enterprise.
6+
7+
## Usage
8+
```yaml
9+
- name: Setup Docker Buildx
10+
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/setup-docker-buildx/main.yaml'
11+
```
12+
13+
### Input Options
14+
15+
#### buildx-version
16+
17+
Version of Docker Buildx to install based on the [GitHub Releases](https://github.com/docker/buildx/releases). If not specified, uses the version already installed in the environment.
18+
19+
For example:
20+
```yaml
21+
buildx-version: 'v0.29.1'
22+
```
23+
24+
#### builder-name
25+
26+
Name for the Docker buildx builder instance. Defaults to `nx-agents-builder`.
27+
28+
For example:
29+
```yaml
30+
builder-name: 'my-custom-builder'
31+
```
32+
33+
#### driver-opts
34+
35+
Comma-separated driver options for the docker-container driver. See [Docker documentation](https://docs.docker.com/build/builders/drivers/docker-container/) for available options.
36+
37+
For example:
38+
```yaml
39+
driver-opts: 'network=host,env.BUILDKIT_STEP_LOG_MAX_SIZE=10485760'
40+
```
41+
42+
#### buildkit-config
43+
44+
Full contents of the `buildkitd.toml` configuration file.
45+
46+
This allows for complete customization of BuildKit configuration including registry mirrors, custom CA certificates, network settings, and more. See [BuildKit TOML configuration documentation](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) for all available options.
47+
48+
**Using the literal block scalar (`|`):**
49+
```yaml
50+
buildkit-config: |
51+
debug = true
52+
[worker.oci]
53+
max-parallelism = 4
54+
```
55+
56+
## What This Step Does
57+
58+
1. **Installs Docker Buildx** (if `buildx-version` is specified): Downloads and installs the specified version of Docker Buildx for your system architecture (amd64, arm64, or arm-v7)
59+
2. **Configures BuildKit**: Creates BuildKit configuration to support registry caching if enabled
60+
3. **Creates Builder**: Sets up a new docker-container driver builder with the specified configuration
61+
4. **Sets Environment Variable**: Exports `BUILDX_BUILDER` environment variable for use in subsequent workflow steps
62+
63+
## Environment Variables
64+
65+
After this step completes, the following environment variable is available for subsequent steps:
66+
67+
- `BUILDX_BUILDER`: The name of the configured buildx builder instance
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
const { execFileSync } = require('child_process');
2+
const { writeFileSync, appendFileSync, existsSync, copyFileSync } = require('fs');
3+
const { homedir } = require('os');
4+
5+
const builderName =
6+
process.env['NX_CLOUD_INPUT_builder-name'] || 'nx-agents-builder';
7+
const driverOpts =
8+
process.env['NX_CLOUD_INPUT_driver-opts'] || '';
9+
const buildxVersion =
10+
process.env['NX_CLOUD_INPUT_buildx-version'];
11+
const buildkitConfig =
12+
process.env['NX_CLOUD_INPUT_buildkit-config'];
13+
14+
const BUILDKIT_CONFIG_PATH = '/tmp/buildkitd.toml';
15+
16+
/**
17+
* Install Docker Buildx to the specified version
18+
* @param version {string}
19+
**/
20+
function installBuildx(version) {
21+
console.log(`> Installing Docker Buildx version: ${version}`);
22+
23+
const arch = process.arch;
24+
let archSuffix;
25+
switch (arch) {
26+
case 'x64':
27+
archSuffix = 'linux-amd64';
28+
break;
29+
case 'arm64':
30+
archSuffix = 'linux-arm64';
31+
break;
32+
case 'arm':
33+
archSuffix = 'linux-arm-v7';
34+
break;
35+
default:
36+
throw new Error(`Unsupported architecture: ${arch}`);
37+
}
38+
39+
console.log(`> Detected architecture: ${arch} (${archSuffix})`);
40+
41+
// version needs to start with 'v'
42+
const versionTag = version.startsWith('v') ? version : `v${version}`;
43+
const downloadUrl = `https://github.com/docker/buildx/releases/download/${versionTag}/buildx-${versionTag}.${archSuffix}`;
44+
const fileName = `buildx-${versionTag}.${archSuffix}`;
45+
const homeDir = homedir();
46+
const pluginDir = `${homeDir}/.docker/cli-plugins`;
47+
const targetPath = `${pluginDir}/docker-buildx`;
48+
49+
try {
50+
console.log(`> Downloading buildx from ${downloadUrl}`);
51+
execFileSync('wget', ['-q', downloadUrl], { stdio: 'inherit' });
52+
53+
console.log('> Creating Docker CLI plugins directory');
54+
execFileSync('mkdir', ['-p', pluginDir], { stdio: 'inherit' });
55+
56+
console.log('> Moving buildx binary to CLI plugins directory');
57+
execFileSync('mv', [fileName, targetPath], { stdio: 'inherit' });
58+
59+
console.log('> Making buildx executable');
60+
execFileSync('chmod', ['+x', targetPath], { stdio: 'inherit' });
61+
62+
console.log('> Verifying buildx installation');
63+
execFileSync('docker', ['buildx', 'version'], { stdio: 'inherit' });
64+
65+
console.log(`> Successfully installed Docker Buildx ${versionTag} for ${archSuffix}`);
66+
} catch (error) {
67+
console.error('> Failed to install Docker Buildx');
68+
throw error;
69+
}
70+
}
71+
72+
/**
73+
* Create buildkit configuration directory and file
74+
**/
75+
76+
function createBuildkitConfig(customConfig) {
77+
console.log('> Creating buildkit configuration...');
78+
79+
try {
80+
const sourcePath = '/etc/buildkit/buildkitd.toml';
81+
82+
// preserve any existing config
83+
if (existsSync(sourcePath)) {
84+
console.log('> Copying existing buildkit config');
85+
copyFileSync(sourcePath, BUILDKIT_CONFIG_PATH);
86+
} else {
87+
console.log('> No existing buildkit config found, creating new file');
88+
writeFileSync(BUILDKIT_CONFIG_PATH, '', { encoding: 'utf-8' });
89+
}
90+
91+
// custom config from input is appended
92+
if (customConfig) {
93+
appendFileSync(BUILDKIT_CONFIG_PATH, '\n' + customConfig, { encoding: 'utf-8' });
94+
}
95+
96+
console.log('> Buildkit config created');
97+
} catch (error) {
98+
console.error('> Failed to create buildkit config file');
99+
throw error;
100+
}
101+
}
102+
103+
/**
104+
* Create and configure docker buildx builder
105+
* @param name {string}
106+
**/
107+
function setupDockerBuilder(name) {
108+
console.log(`> Creating docker buildx builder: ${name}`);
109+
110+
// retry because while it is extremely rare, the docker daemon might not be up yet
111+
let lastError;
112+
const maxRetries = 10;
113+
const retryDelayMs = 5000;
114+
115+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
116+
try {
117+
const args = [
118+
'buildx', 'create',
119+
'--name', name,
120+
'--driver', 'docker-container',
121+
'--config', BUILDKIT_CONFIG_PATH,
122+
'--use',
123+
'--bootstrap'
124+
];
125+
126+
// driver-opt if provided
127+
if (driverOpts) {
128+
driverOpts.split(',').forEach(opt => {
129+
args.push('--driver-opt', opt.trim());
130+
});
131+
}
132+
133+
execFileSync('docker', args, { stdio: 'inherit' });
134+
135+
if (attempt > 1) {
136+
console.log(`> Docker buildx create succeeded on attempt ${attempt}`);
137+
}
138+
console.log(`> Successfully created and bootstrapped builder: ${name}`);
139+
return;
140+
} catch (error) {
141+
lastError = error;
142+
console.error(`> Docker buildx create failed on attempt ${attempt}/${maxRetries}`);
143+
console.error(` Error: ${error.message}`);
144+
145+
if (attempt < maxRetries) {
146+
console.log(`> Retrying in ${retryDelayMs}ms...`);
147+
execFileSync('sleep', [String(retryDelayMs / 1000)], { stdio: 'inherit' });
148+
}
149+
}
150+
}
151+
152+
console.error(`> Docker buildx create failed after ${maxRetries} attempts`);
153+
console.error('> Docker daemon may not be available');
154+
throw lastError;
155+
}
156+
157+
/**
158+
* Set environment variable for future steps
159+
* @param name {string}
160+
**/
161+
function setBuildxBuilderEnv(name) {
162+
console.log(`> Setting BUILDX_BUILDER environment variable to: ${name}`);
163+
164+
if (!process.env.NX_CLOUD_ENV) {
165+
console.error('> NX_CLOUD_ENV is not set');
166+
console.error('> Cannot persist environment variable for future steps');
167+
process.exit(1);
168+
}
169+
170+
try {
171+
appendFileSync(process.env.NX_CLOUD_ENV, `BUILDX_BUILDER=${name}\n`, {
172+
encoding: 'utf-8'
173+
});
174+
console.log('> BUILDX_BUILDER environment variable set for future steps');
175+
} catch (error) {
176+
console.error('> Failed to set BUILDX_BUILDER environment variable');
177+
throw error;
178+
}
179+
}
180+
181+
try {
182+
console.log('> Setting up Docker Buildx...\n');
183+
if (buildxVersion) {
184+
installBuildx(buildxVersion);
185+
}
186+
createBuildkitConfig(buildkitConfig);
187+
setupDockerBuilder(builderName);
188+
setBuildxBuilderEnv(builderName);
189+
console.log('\n> Docker Buildx setup complete!');
190+
} catch (error) {
191+
console.error('> Failed to setup Docker Buildx:');
192+
console.error(error);
193+
process.exit(1);
194+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Setup Docker Buildx
2+
description: Configure Docker Buildx
3+
inputs:
4+
- name: buildx-version
5+
description: 'Which version of Docker Buildx to install'
6+
- name: builder-name
7+
description: 'Name for the Docker buildx builder'
8+
default: 'nx-agents-builder'
9+
- name: driver-opts
10+
description: 'Comma-separated driver options for the docker-container driver, see https://docs.docker.com/build/builders/drivers/docker-container/'
11+
- name: buildkit-config
12+
description: 'Full contents of the buildkitd.toml configuration file'
13+
definition:
14+
using: 'node'
15+
main: workflow-steps/setup-docker-buildx/main.js

0 commit comments

Comments
 (0)