-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.js
More file actions
149 lines (128 loc) · 5.77 KB
/
main.js
File metadata and controls
149 lines (128 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const core = require('@actions/core');
const exec = require('@actions/exec');
const fs = require('fs').promises;
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const os = require('os');
const { WarpBuildConfig, assignBuilders, getBuilderDetails } = require('./utils/warpbuild');
// Helper function to wait for builder details
async function waitForBuilderDetails(builderId, config, timeout) {
while (true) {
const currentTime = Date.now();
const elapsed = currentTime - global.startTime;
if (elapsed > timeout) {
core.error(`ERROR: Global script timeout of ${timeout}ms exceeded after ${elapsed}ms`);
core.error('Script execution terminated');
throw new Error(`ERROR: Global script timeout of ${timeout}ms exceeded after ${elapsed}ms`);
}
let details;
try {
details = await getBuilderDetails(config, builderId);
} catch (error) {
core.warning(`Error getting builder details: ${error.message}`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
if (details.status === 'ready') {
if (!details.metadata?.host) {
throw new Error(`Builder ${builderId} is ready but host information is missing`);
}
return details;
} else if (details.status === 'failed') {
throw new Error(`Builder ${builderId} failed to initialize`);
}
core.info(`Builder ${builderId} status: ${details.status}. Waiting...`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
async function setupBuildxNode(index, builderId, builderName, config, timeout, shouldSetupBuildx) {
const details = await waitForBuilderDetails(builderId, config, timeout);
const builderHost = details.metadata.host;
const builderCa = details.metadata.ca;
const builderClientCert = details.metadata.client_cert;
const builderClientKey = details.metadata.client_key;
let builderPlatforms = details.arch;
// Format platforms
if (builderPlatforms && !builderPlatforms.includes('linux/')) {
builderPlatforms = builderPlatforms.split(',').map(p => `linux/${p.trim()}`).join(',');
}
// Create cert directory
const certDir = path.join(os.homedir(), '.warpbuild', 'buildkit', builderName, builderId);
await fs.mkdir(certDir, { recursive: true });
// Write certificates
await fs.writeFile(path.join(certDir, 'ca.pem'), builderCa);
await fs.writeFile(path.join(certDir, 'cert.pem'), builderClientCert);
await fs.writeFile(path.join(certDir, 'key.pem'), builderClientKey);
// Setup buildx if required
if (shouldSetupBuildx) {
const baseCmd = [
'buildx', 'create',
'--name', builderName,
'--node', builderId,
'--driver', 'remote',
'--driver-opt', `cacert=${path.join(certDir, 'ca.pem')}`,
'--driver-opt', `cert=${path.join(certDir, 'cert.pem')}`,
'--driver-opt', `key=${path.join(certDir, 'key.pem')}`,
'--platform', builderPlatforms,
'--use',
`tcp://${builderHost}`
];
// For nodes after the first one (index > 0), add the --append flag
// This tells buildx to add this node to the existing builder context
// instead of creating a new one
// The splice(2, 0, '--append') inserts '--append' at index 2 of baseCmd array,
// so it appears right after 'buildx create' in the command
if (index > 0) {
baseCmd.splice(2, 0, '--append');
}
await exec.exec('docker', baseCmd);
}
// Set outputs
core.setOutput(`docker-builder-node-${index}-endpoint`, builderHost);
core.setOutput(`docker-builder-node-${index}-platforms`, builderPlatforms);
core.setOutput(`docker-builder-node-${index}-cacert`, builderCa);
core.setOutput(`docker-builder-node-${index}-cert`, builderClientCert);
core.setOutput(`docker-builder-node-${index}-key`, builderClientKey);
}
async function run() {
try {
// Initialize global start time
global.startTime = Date.now();
const timeout = parseInt(core.getInput('timeout')) || 300000;
const profileName = core.getInput('profile-name', { required: true });
const shouldSetupBuildx = core.getInput('should-setup-buildx') !== 'false';
// Initialize WarpBuild configuration
const config = new WarpBuildConfig();
// Example output: lq1cr8p2n5x7d3fy
const idempotencyKey = uuidv4().replace(/-/g, '').substring(0, 16);
const builderName = `builder-${idempotencyKey}`;
// Assign builders
const responseData = await assignBuilders(config, idempotencyKey, profileName, timeout);
// Save builder information for cleanup
const buildersState = {
builderName,
idempotencyKey,
builders: responseData.builder_instances.map(b => ({
id: b.id,
request_id: b.request_id,
index: responseData.builder_instances.indexOf(b)
}))
};
// Save state for post cleanup
core.saveState('WARPBUILD_BUILDERS', JSON.stringify(buildersState));
// Setup each builder node
for (let i = 0; i < responseData.builder_instances.length; i++) {
core.info(`Setting up builder node ${responseData.builder_instances[i].id} and request ${responseData.builder_instances[i].request_id}...`);
await setupBuildxNode(
i,
responseData.builder_instances[i].id,
builderName,
config,
timeout,
shouldSetupBuildx
);
}
} catch (error) {
core.setFailed(error.message);
}
}
run();