From 7796f5699c6d60c28e907100f2132413411c1073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Carter=20=E7=A5=81=E6=98=8E=E6=80=9D?= Date: Fri, 26 Jun 2026 08:23:47 +1000 Subject: [PATCH 1/2] fix(scripts): use Node.js stdlib only, works with bun and node Rewrote submit.mjs and checkpoint.mjs to use only Node.js standard library (child_process, fs, path, url, readline). No runtime-specific APIs. Works with both `bun scripts/submit.mjs` and `node scripts/submit.mjs`. Fixes: - Missing imports ($, fs, question were undefined) - Relative path resolution (used cd .. from CWD, now uses import.meta.url) - fs-extra APIs (pathExists, move) replaced with node:fs equivalents - Bun Buffer stdout (stdout.trim failed, now uses execSync with encoding) - xargs command line limit (presigned S3 URL too long, now passed directly) - Excludes .venv, __pycache__, .DS_Store, mainrun.log from zip Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- scripts/checkpoint.mjs | 55 ++++++++++------- scripts/submit.mjs | 136 ++++++++++++++++++++++++----------------- 2 files changed, 114 insertions(+), 77 deletions(-) diff --git a/scripts/checkpoint.mjs b/scripts/checkpoint.mjs index cbfab1b..8790ff0 100644 --- a/scripts/checkpoint.mjs +++ b/scripts/checkpoint.mjs @@ -1,28 +1,39 @@ +import { execSync } from "node:child_process"; +import fs from "node:fs/promises"; +import { existsSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(scriptDir, ".."); + +const run = (cmd) => execSync(cmd, { cwd: repoRoot, encoding: "utf-8" }).trim(); + try { - const logPath = '../mainrun/logs/mainrun.log' - if (await fs.pathExists(logPath)) { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5) - const newLogPath = `../mainrun/logs/mainrun_${timestamp}.log` - await fs.move(logPath, newLogPath) - console.log(`Moved existing log to: mainrun_${timestamp}.log`) + const logPath = path.join(repoRoot, "mainrun", "logs", "mainrun.log"); + if (existsSync(logPath)) { + const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5); + const newLogPath = path.join(repoRoot, "mainrun", "logs", `mainrun_${timestamp}.log`); + await fs.rename(logPath, newLogPath); + console.log(`Moved existing log to: mainrun_${timestamp}.log`); } - await $`git -C .. add .` - - const status = await $`git -C .. status --porcelain` - if (status.stdout.trim() === '') { - console.log('No changes to checkpoint') - process.exit(0) + run("git add ."); + + const status = run("git status --porcelain"); + if (status === "") { + console.log("No changes to checkpoint"); + process.exit(0); } - - await $`git -C .. commit -m "Mainrun auto checkpoint"` - console.log('Auto checkpoint created') + + run('git commit -m "Mainrun auto checkpoint"'); + console.log("Auto checkpoint created"); } catch (error) { - if (error.message.includes('nothing to commit')) { - console.log('No changes to checkpoint') - process.exit(0) + if (error.message.includes("nothing to commit")) { + console.log("No changes to checkpoint"); + process.exit(0); } - - console.error('Failed to create checkpoint:', error.message) - process.exit(1) -} \ No newline at end of file + + console.error("Failed to create checkpoint:", error.message); + process.exit(1); +} diff --git a/scripts/submit.mjs b/scripts/submit.mjs index 4a57600..76b8fe0 100644 --- a/scripts/submit.mjs +++ b/scripts/submit.mjs @@ -1,64 +1,90 @@ +import { execSync } from "node:child_process"; +import fs from "node:fs/promises"; +import { existsSync } from "node:fs"; +import readline from "node:readline/promises"; +import { stdin, stdout } from "node:process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(scriptDir, ".."); + +const question = async (prompt) => { + const rl = readline.createInterface({ input: stdin, output: stdout }); + const answer = await rl.question(prompt); + rl.close(); + return answer; +}; + +const run = (cmd) => execSync(cmd, { cwd: repoRoot, encoding: "utf-8" }).trim(); + try { - let email - const envPath = '../.env' - - if (await fs.pathExists(envPath)) { - const envContent = await fs.readFile(envPath, 'utf-8') - const match = envContent.match(/EMAIL=(.+)/) + let email; + const envPath = path.join(repoRoot, ".env"); + + if (existsSync(envPath)) { + const envContent = await fs.readFile(envPath, "utf-8"); + const match = envContent.match(/EMAIL=(.+)/); if (match) { - email = match[1].trim() - console.log(`Using email: ${email}`) + email = match[1].trim(); + console.log(`Using email: ${email}`); } } - + if (!email) { - email = await question('Please enter your email address: ') - - await fs.writeFile(envPath, `EMAIL=${email}\n`) - console.log('Email saved for future submissions') + email = await question("Please enter your email address: "); + + await fs.writeFile(envPath, `EMAIL=${email}\n`); + console.log("Email saved for future submissions"); } - - console.log('\n' + '='.repeat(60)) - console.log('LEGAL NOTICE') - console.log('='.repeat(60)) - console.log('\nBy submitting this assessment, you agree that:') - console.log('- All submitted code becomes the property of Maincode Pty Ltd') - console.log('- You assign all intellectual property rights to Maincode Pty Ltd') - console.log('- You have read and agree to the full legal terms') - console.log('\nFull terms: https://github.com/maincodehq/mainrun/blob/main/LEGAL-NOTICE.md') - console.log('='.repeat(60) + '\n') - - const confirmation = await question('Do you agree to these terms and want to proceed? (yes/no): ') - - if (confirmation.toLowerCase() !== 'yes') { - console.log('Submission cancelled.') - process.exit(0) + + console.log("\n" + "=".repeat(60)); + console.log("LEGAL NOTICE"); + console.log("=".repeat(60)); + console.log("\nBy submitting this assessment, you agree that:"); + console.log("- All submitted code becomes the property of Maincode Pty Ltd"); + console.log("- You assign all intellectual property rights to Maincode Pty Ltd"); + console.log("- You have read and agree to the full legal terms"); + console.log("\nFull terms: https://github.com/maincodehq/mainrun/blob/main/LEGAL-NOTICE.md"); + console.log("=".repeat(60) + "\n"); + + const confirmation = await question("Do you agree to these terms and want to proceed? (yes/no): "); + + if (confirmation.toLowerCase() !== "yes") { + console.log("Submission cancelled."); + process.exit(0); } - - console.log('\nCreating submission zip...') - - await $`cd .. && zip -r submission.zip . -x "node_modules/*" -x "mainrun/data/*"` - - console.log('Requesting upload URL...') - - const response = await $`curl -s "https://api.hanger.maincode.com/api/v1/upload/request?email=${email}&filename=submission.zip"` - const uploadUrl = response.stdout.trim() - - if (!uploadUrl || uploadUrl.includes('error')) { - throw new Error(`Failed to get upload URL: ${uploadUrl}`) + + console.log("\nCreating submission zip..."); + + const zipPath = path.join(repoRoot, "submission.zip"); + if (existsSync(zipPath)) await fs.unlink(zipPath); + + run( + `zip -r submission.zip . -x "node_modules/*" -x "mainrun/data/*" -x "mainrun/.venv/*" -x ".git/*" -x "*.zip" -x "*/__pycache__/*" -x "*.DS_Store" -x "mainrun/logs/mainrun.log"` + ); + + console.log("Requesting upload URL..."); + + const uploadUrl = run( + `curl -s "https://api.hanger.maincode.com/api/v1/upload/request?email=${email}&filename=submission.zip"` + ); + + if (!uploadUrl || uploadUrl.includes("error")) { + throw new Error(`Failed to get upload URL: ${uploadUrl}`); } - - console.log('Uploading submission...') - - await $`echo ${uploadUrl} | xargs -I {} curl -X PUT {} --upload-file ../submission.zip` - - await $`rm -f ../submission.zip` - - console.log('✓ Submission uploaded successfully!') - + + console.log("Uploading submission..."); + + run(`curl -X PUT "${uploadUrl}" --upload-file "${zipPath}"`); + + await fs.unlink(zipPath); + + console.log("✓ Submission uploaded successfully!"); } catch (error) { - await $`rm -f ../submission.zip`.catch(() => {}) - - console.error('Failed to submit:', error.message) - process.exit(1) -} \ No newline at end of file + const zipPath = path.join(repoRoot, "submission.zip"); + if (existsSync(zipPath)) await fs.unlink(zipPath).catch(() => {}); + + console.error("Failed to submit:", error.message); + process.exit(1); +} From 7fcff46ba01c0bf6723088a7dd648357e53c26a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Carter=20=E7=A5=81=E6=98=8E=E6=80=9D?= Date: Fri, 26 Jun 2026 08:31:24 +1000 Subject: [PATCH 2/2] fix(scripts): include mainrun.log in submission zip --- scripts/submit.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/submit.mjs b/scripts/submit.mjs index 76b8fe0..69d0c67 100644 --- a/scripts/submit.mjs +++ b/scripts/submit.mjs @@ -61,7 +61,7 @@ try { if (existsSync(zipPath)) await fs.unlink(zipPath); run( - `zip -r submission.zip . -x "node_modules/*" -x "mainrun/data/*" -x "mainrun/.venv/*" -x ".git/*" -x "*.zip" -x "*/__pycache__/*" -x "*.DS_Store" -x "mainrun/logs/mainrun.log"` + `zip -r submission.zip . -x "node_modules/*" -x "mainrun/data/*" -x "mainrun/.venv/*" -x ".git/*" -x "*.zip" -x "*/__pycache__/*" -x "*.DS_Store"` ); console.log("Requesting upload URL...");