@chronsyn/eas-on-infra 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +11 -0
- package/README.md +191 -0
- package/dist/application.core.d.ts +3 -0
- package/dist/application.core.d.ts.map +1 -0
- package/dist/application.core.js +18 -0
- package/dist/client/root.d.ts +3 -0
- package/dist/client/root.d.ts.map +1 -0
- package/dist/client/root.js +53 -0
- package/dist/client/send-build.d.ts +3 -0
- package/dist/client/send-build.d.ts.map +1 -0
- package/dist/client/send-build.js +180 -0
- package/dist/ingress.d.ts +3 -0
- package/dist/ingress.d.ts.map +1 -0
- package/dist/ingress.js +264 -0
- package/package.json +46 -0
- package/src/application.core.ts +19 -0
- package/src/client/root.ts +43 -0
- package/src/client/send-build.ts +205 -0
- package/src/ingress.ts +350 -0
- package/tsconfig.json +33 -0
package/src/ingress.ts
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { execSync, spawn } from 'node:child_process';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import multer from 'multer';
|
|
7
|
+
|
|
8
|
+
import z from "zod";
|
|
9
|
+
import unzipper from 'unzipper';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const ZBodySchema = z.object({
|
|
13
|
+
eas_access_token: z.string(),
|
|
14
|
+
eas_profile: z.string(),
|
|
15
|
+
eas_platform: z.enum(['ios', 'android']),
|
|
16
|
+
installer: z.enum([
|
|
17
|
+
'npm', // npm i
|
|
18
|
+
'yarn', // yarn
|
|
19
|
+
'bun', // bun install
|
|
20
|
+
'pnpm', // pnpm install
|
|
21
|
+
]).optional()
|
|
22
|
+
}).strict()
|
|
23
|
+
|
|
24
|
+
export const configureRoute = (app: express.Express) => {
|
|
25
|
+
|
|
26
|
+
const multerDestination = !!process.env.UPLOAD_DIRECTORY
|
|
27
|
+
? path.resolve(process.env.UPLOAD_DIRECTORY)
|
|
28
|
+
: path.resolve(process.cwd(),'uploads')
|
|
29
|
+
|
|
30
|
+
app.post(
|
|
31
|
+
'/ingress',
|
|
32
|
+
|
|
33
|
+
multer({
|
|
34
|
+
limits: {
|
|
35
|
+
fileSize: 300000000, // 300MB
|
|
36
|
+
fieldNameSize: 8 * 50,
|
|
37
|
+
files: 1,
|
|
38
|
+
fields: 20,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
preservePath: false,
|
|
42
|
+
|
|
43
|
+
storage: multer.diskStorage({
|
|
44
|
+
destination: multerDestination,
|
|
45
|
+
filename: (req, file, cb) => {
|
|
46
|
+
cb(null, [new Date().getTime(), crypto.randomBytes(16).toString('hex'), '.zip'].join('_'));
|
|
47
|
+
}
|
|
48
|
+
}),
|
|
49
|
+
fileFilter: (req, file, cb) => {
|
|
50
|
+
if (file.mimetype.toLowerCase() !== 'application/zip') {
|
|
51
|
+
return cb(new Error('File rejected'))
|
|
52
|
+
}
|
|
53
|
+
return cb(null, true)
|
|
54
|
+
},
|
|
55
|
+
}).single('bundle'),
|
|
56
|
+
|
|
57
|
+
async (req, res, next) => {
|
|
58
|
+
|
|
59
|
+
console.log(`Received ingress request...`)
|
|
60
|
+
|
|
61
|
+
const actualInputFilePath = req.file?.path!;
|
|
62
|
+
|
|
63
|
+
const projectFileExtractName = [
|
|
64
|
+
new Date().getTime(),
|
|
65
|
+
crypto.randomBytes(4).toString("hex")
|
|
66
|
+
].join('_')
|
|
67
|
+
|
|
68
|
+
const extractedPath = path.resolve(
|
|
69
|
+
process.env.BINARY_OUTPUT_DIRECTORY
|
|
70
|
+
? (
|
|
71
|
+
process.env.BINARY_OUTPUT_DIRECTORY,
|
|
72
|
+
projectFileExtractName
|
|
73
|
+
)
|
|
74
|
+
: (
|
|
75
|
+
process.cwd(),
|
|
76
|
+
'extracted',
|
|
77
|
+
projectFileExtractName
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
const directory = await unzipper.Open.file(actualInputFilePath)
|
|
81
|
+
await directory.extract({ path: extractedPath });
|
|
82
|
+
|
|
83
|
+
const parse = await ZBodySchema.safeParseAsync(req.body);
|
|
84
|
+
if (parse.error) {
|
|
85
|
+
res.status(500).json({ status: "error", error: parse.error })
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
const {
|
|
89
|
+
eas_access_token,
|
|
90
|
+
// eas_project_id,
|
|
91
|
+
eas_platform,
|
|
92
|
+
eas_profile,
|
|
93
|
+
installer = 'npm'
|
|
94
|
+
} = parse.data;
|
|
95
|
+
|
|
96
|
+
const build_id = crypto.randomBytes(16).toString('hex')
|
|
97
|
+
const fn = [
|
|
98
|
+
new Date().getTime(),
|
|
99
|
+
eas_platform,
|
|
100
|
+
eas_profile,
|
|
101
|
+
build_id
|
|
102
|
+
].join('__')
|
|
103
|
+
|
|
104
|
+
// If user has provided BINARY_OUTPUT_DIRECTORY in env, use that
|
|
105
|
+
// Else, fallback to output directory
|
|
106
|
+
const easBuildOutputPath = path.resolve(
|
|
107
|
+
process?.env?.BINARY_OUTPUT_DIRECTORY
|
|
108
|
+
? (process?.env?.BINARY_OUTPUT_DIRECTORY, fn)
|
|
109
|
+
: (process.cwd(), 'builds', fn)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
console.log(`EXTRACTED PATH => ${extractedPath}`);
|
|
113
|
+
console.log(`BUILD OUTPUT PATH => ${easBuildOutputPath}`)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
// const eas_auto_submit = parse.data.eas_auto_submit === "true";
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
let platformParsed;
|
|
121
|
+
switch (true){
|
|
122
|
+
case eas_platform === "ios":
|
|
123
|
+
platformParsed = 'ios';
|
|
124
|
+
break;
|
|
125
|
+
case eas_platform === 'android':
|
|
126
|
+
platformParsed = 'android';
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// TODO: chdir to directory, install dependencies
|
|
131
|
+
// TODO: check for `yarn.lock` to run `yarn install`, `package-lock.json` to run `npm i`, etc.
|
|
132
|
+
|
|
133
|
+
// const easBuildCommandWithArgs = [
|
|
134
|
+
// `git init &&`,
|
|
135
|
+
// `git add . &&`,
|
|
136
|
+
// `git commit -m "ready to build" &&`,
|
|
137
|
+
// `${builderToolToUse} &&`,
|
|
138
|
+
// `EXPO_TOKEN=${eas_access_token}`,
|
|
139
|
+
// 'eas build',
|
|
140
|
+
// '--profile', eas_profile.toString(),
|
|
141
|
+
// '--platform', eas_platform.toString(),
|
|
142
|
+
// '--local',
|
|
143
|
+
// '--non-interactive',
|
|
144
|
+
// '--output', `"${easBuildOutputPath}"`,
|
|
145
|
+
// ]
|
|
146
|
+
|
|
147
|
+
const easBinaryLocation = execSync(`which eas`, { encoding: 'utf-8' }).trim()
|
|
148
|
+
console.log("EAS BINARY => ", easBinaryLocation.toString());
|
|
149
|
+
|
|
150
|
+
// // All these are defined values - i.e. no user input
|
|
151
|
+
// // We're safe to exec them, as this also ensures steps are performed in the order expected
|
|
152
|
+
// try {
|
|
153
|
+
// execSync(`git init`)
|
|
154
|
+
// } catch (err) {
|
|
155
|
+
// console.error("Error with git init: ", err)
|
|
156
|
+
// }
|
|
157
|
+
// try {
|
|
158
|
+
// execSync(`git add .`);
|
|
159
|
+
// } catch (err) {
|
|
160
|
+
// console.error("Error with git add: ", err)
|
|
161
|
+
// }
|
|
162
|
+
// try {
|
|
163
|
+
// execSync(`git commit -m "ready to build"`);
|
|
164
|
+
// } catch (err) {
|
|
165
|
+
// console.error("Error with git commit: ", err)
|
|
166
|
+
// }
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
switch (installer) {
|
|
170
|
+
case 'yarn':
|
|
171
|
+
execSync(
|
|
172
|
+
'yarn',
|
|
173
|
+
{
|
|
174
|
+
stdio: 'inherit',
|
|
175
|
+
env: {
|
|
176
|
+
...process.env,
|
|
177
|
+
PATH: process.env.PATH,
|
|
178
|
+
EXPO_TOKEN: eas_access_token,
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
break;
|
|
183
|
+
case 'bun':
|
|
184
|
+
execSync(
|
|
185
|
+
'bun install',
|
|
186
|
+
{
|
|
187
|
+
stdio: 'inherit',
|
|
188
|
+
env: {
|
|
189
|
+
...process.env,
|
|
190
|
+
PATH: process.env.PATH,
|
|
191
|
+
EXPO_TOKEN: eas_access_token,
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
break;
|
|
196
|
+
case "pnpm":
|
|
197
|
+
execSync(
|
|
198
|
+
'pnpm install',
|
|
199
|
+
{
|
|
200
|
+
stdio: 'inherit',
|
|
201
|
+
env: {
|
|
202
|
+
...process.env,
|
|
203
|
+
PATH: process.env.PATH,
|
|
204
|
+
EXPO_TOKEN: eas_access_token,
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
break;
|
|
209
|
+
case 'npm':
|
|
210
|
+
default:
|
|
211
|
+
execSync(
|
|
212
|
+
'npm i --force',
|
|
213
|
+
{
|
|
214
|
+
stdio: 'inherit',
|
|
215
|
+
env: {
|
|
216
|
+
...process.env,
|
|
217
|
+
PATH: process.env.PATH,
|
|
218
|
+
EXPO_TOKEN: eas_access_token,
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
} catch (err) {
|
|
225
|
+
console.error("Error with installer step: ", err)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
process.chdir(extractedPath);
|
|
229
|
+
console.log(`Process cwd is now => `, process.cwd());
|
|
230
|
+
|
|
231
|
+
const easBuildArgs = [
|
|
232
|
+
`build`,
|
|
233
|
+
`--profile`, eas_profile.toString(),
|
|
234
|
+
`--platform`, eas_platform.toString(),
|
|
235
|
+
`--local`,
|
|
236
|
+
`--non-interactive`,
|
|
237
|
+
`--output`, easBuildOutputPath
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
// We run this with spawn as there's som user input
|
|
241
|
+
// We've sanitised it as much as possible, but we still want to minimise risk
|
|
242
|
+
const easBuildResult = spawn(
|
|
243
|
+
'eas',
|
|
244
|
+
easBuildArgs,
|
|
245
|
+
{
|
|
246
|
+
stdio: 'inherit',
|
|
247
|
+
shell: false,
|
|
248
|
+
env: {
|
|
249
|
+
...process.env,
|
|
250
|
+
PATH: process.env.PATH,
|
|
251
|
+
EXPO_TOKEN: eas_access_token,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
easBuildResult.on('error', (error) => {
|
|
257
|
+
console.error('Error spawning process:', error);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
easBuildResult.on('close', (code, signal) => {
|
|
261
|
+
console.log(`Process exited with code ${code}, signal: ${signal?.toString()}`);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
easBuildResult.on('message', (msg) => {
|
|
265
|
+
console.log(`Process msg: ${msg.toString()}`)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// easBuildResult.addListener('spawn', () => {
|
|
269
|
+
// console.log('[EAS BUILD] [SPAWN]')
|
|
270
|
+
// })
|
|
271
|
+
// easBuildResult.addListener('disconnect', () => {
|
|
272
|
+
// console.log('[EAS BUILD] [DISCONNECT]')
|
|
273
|
+
// })
|
|
274
|
+
|
|
275
|
+
// easBuildResult.addListener('error', (msg) => {
|
|
276
|
+
// console.log('[EAS BUILD] [ERROR]', msg.toString())
|
|
277
|
+
// })
|
|
278
|
+
// easBuildResult.addListener('exit', () => {
|
|
279
|
+
// console.log('[EAS BUILD] COMPLETED!')
|
|
280
|
+
// })
|
|
281
|
+
|
|
282
|
+
// easBuildResult.stdout.on('close', () => {
|
|
283
|
+
// console.log('[STDOUT] [EAS BUILD] CLOSE!')
|
|
284
|
+
// })
|
|
285
|
+
// easBuildResult.stdout.on('data', (msg) => {
|
|
286
|
+
// console.log('[STDOUT] [EAS BUILD]', msg)
|
|
287
|
+
// })
|
|
288
|
+
// easBuildResult.stdout.on('end', () => {
|
|
289
|
+
// console.log('[STDOUT] [EAS BUILD] END!')
|
|
290
|
+
// })
|
|
291
|
+
// easBuildResult.stdout.on('error', (err) => {
|
|
292
|
+
// console.log('[STDOUT] [ERR] [EAS BUILD] ', err.message)
|
|
293
|
+
// })
|
|
294
|
+
// easBuildResult.stdout.on('pause', () => {
|
|
295
|
+
// console.log('[STDOUT] [EAS BUILD] PAUSE!')
|
|
296
|
+
// })
|
|
297
|
+
// easBuildResult.stdout.on('readable', () => {
|
|
298
|
+
// console.log('[STDOUT] [EAS BUILD] READABLE!')
|
|
299
|
+
// })
|
|
300
|
+
// easBuildResult.stdout.on('resume', () => {
|
|
301
|
+
// console.log('[STDOUT] [EAS BUILD] RESUME!')
|
|
302
|
+
// })
|
|
303
|
+
|
|
304
|
+
// easBuildResult.stderr.on('close', () => {
|
|
305
|
+
// console.log('[STDERR] [EAS BUILD] CLOSE!')
|
|
306
|
+
// })
|
|
307
|
+
// easBuildResult.stderr.on('data', (msg) => {
|
|
308
|
+
// console.log('[STDERR] [EAS BUILD]', msg)
|
|
309
|
+
// })
|
|
310
|
+
// easBuildResult.stderr.on('end', () => {
|
|
311
|
+
// console.log('[STDERR] [EAS BUILD] END!')
|
|
312
|
+
// })
|
|
313
|
+
// easBuildResult.stderr.on('error', (err) => {
|
|
314
|
+
// console.log('[STDERR] [ERR] [EAS BUILD] ', err.message)
|
|
315
|
+
// })
|
|
316
|
+
// easBuildResult.stderr.on('pause', () => {
|
|
317
|
+
// console.log('[STDERR] [EAS BUILD] PAUSE!')
|
|
318
|
+
// })
|
|
319
|
+
// easBuildResult.stderr.on('readable', () => {
|
|
320
|
+
// console.log('[STDERR] [EAS BUILD] READABLE!')
|
|
321
|
+
// })
|
|
322
|
+
// easBuildResult.stderr.on('resume', () => {
|
|
323
|
+
// console.log('[STDERR] [EAS BUILD] RESUME!')
|
|
324
|
+
// })
|
|
325
|
+
|
|
326
|
+
// exec(
|
|
327
|
+
// easBuildCommandWithArgs,
|
|
328
|
+
// {
|
|
329
|
+
// cwd: extractedPath
|
|
330
|
+
// },
|
|
331
|
+
// (err, stdout, stderr) => {
|
|
332
|
+
// if (err) console.log('[ERR] => ', err);
|
|
333
|
+
// if (stdout) console.log('[STDOUT] => ', stdout);
|
|
334
|
+
// if (stderr) console.log('[STDERR] => ', stderr);
|
|
335
|
+
// }
|
|
336
|
+
// )
|
|
337
|
+
|
|
338
|
+
const responseData = {
|
|
339
|
+
ok: true,
|
|
340
|
+
status: 'in_progress',
|
|
341
|
+
build_id,
|
|
342
|
+
command: easBuildArgs,
|
|
343
|
+
status_url: `${(process.env.SITE_BASE_URL ?? "")}/status/${build_id}`,
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
console.log("Processing... Response => ", JSON.stringify(responseData, null, 2))
|
|
347
|
+
|
|
348
|
+
res.status(200).json(responseData)
|
|
349
|
+
})
|
|
350
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exclude": [
|
|
3
|
+
"extracted",
|
|
4
|
+
"builds",
|
|
5
|
+
"uploads",
|
|
6
|
+
"dist",
|
|
7
|
+
],
|
|
8
|
+
"compilerOptions": {
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"module": "commonjs",
|
|
12
|
+
"target": "es2017",
|
|
13
|
+
|
|
14
|
+
"lib": ["esnext"],
|
|
15
|
+
"types": ["node"],
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// Other Outputs
|
|
19
|
+
"sourceMap": false,
|
|
20
|
+
"declaration": true,
|
|
21
|
+
"declarationMap": true,
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// Recommended Options
|
|
25
|
+
"strict": true,
|
|
26
|
+
// "verbatimModuleSyntax": true,
|
|
27
|
+
"esModuleInterop": true,
|
|
28
|
+
"isolatedModules": true,
|
|
29
|
+
"noUncheckedSideEffectImports": true,
|
|
30
|
+
"moduleDetection": "force",
|
|
31
|
+
"skipLibCheck": true,
|
|
32
|
+
}
|
|
33
|
+
}
|