From 6339388eff690e17b1ed1ad74dc189f849c06ec6 Mon Sep 17 00:00:00 2001 From: ljacobsson Date: Thu, 17 Aug 2023 11:18:28 +0200 Subject: [PATCH] [samp-local] fix mac-address bug + .NET improvements --- README.md | 2 +- package-lock.json | 101 ++++++++++++++++-- package.json | 3 +- src/commands/local/lib/connect.js | 37 +++++-- src/commands/local/lib/relay/relay.js | 4 +- src/commands/local/local.js | 10 +- .../local/runtime-support/dotnet/Program.cs | 1 + .../runtime-support/dotnet/iac-support/sam.js | 10 +- .../dotnet/ide-support/visualstudio/setup.js | 4 +- .../nodejs/ide-support/vscode/setup.js | 8 +- src/commands/policy/index.js | 2 +- src/commands/share/share.js | 5 +- 12 files changed, 151 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 9f6330a..8df0f38 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ Lets you quickly build IAM polices, find SAM policy templates or generate SAM Co ``` Usage: samp policy|p [options] -Opens a wizard thet help you to create and attach a new IAM policy to a resource in your template +Opens a wizard that helps you to create and attach a new IAM policy to a resource in your template Options: -t, --template Template file name (default: "template.yaml") diff --git a/package-lock.json b/package-lock.json index b5a2fe3..e4f22e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "samp-cli", - "version": "1.0.33", + "version": "1.0.41", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "samp-cli", - "version": "1.0.33", + "version": "1.0.41", "license": "ISC", "dependencies": { "@aws-sdk/client-cloudformation": "^3.319.0", @@ -47,6 +47,7 @@ "rimraf": "^5.0.1", "toml": "^3.0.0", "tsc-watch": "^6.0.4", + "uuid": "^9.0.0", "yaml": "^2.3.1", "yaml-cfn": "^0.3.2" }, @@ -321,6 +322,14 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-iot": { "version": "3.362.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-iot/-/client-iot-3.362.0.tgz", @@ -428,6 +437,14 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-iot/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-lambda": { "version": "3.362.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.362.0.tgz", @@ -648,6 +665,14 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-schemas/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-sfn": { "version": "3.362.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sfn/-/client-sfn-3.362.0.tgz", @@ -1606,6 +1631,14 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/middleware-sdk-sts": { "version": "3.379.1", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.379.1.tgz", @@ -3642,6 +3675,14 @@ "node": ">=14.0.0" } }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@smithy/middleware-serde": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.1.tgz", @@ -8893,6 +8934,16 @@ "node": ">=10" } }, + "node_modules/node-notifier/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/node-notifier/node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -11374,9 +11425,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -11968,6 +12019,11 @@ "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -12068,6 +12124,11 @@ "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -12274,6 +12335,11 @@ "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -13090,6 +13156,13 @@ "@aws-sdk/util-retry": "3.362.0", "tslib": "^2.5.0", "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, "@aws-sdk/middleware-sdk-sts": { @@ -14717,6 +14790,11 @@ "requires": { "tslib": "^2.5.0" } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -18716,6 +18794,13 @@ "lru-cache": "^6.0.0" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -20645,9 +20730,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, "v8-to-istanbul": { "version": "7.1.2", diff --git a/package.json b/package.json index 050ae4c..1398799 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "samp-cli", - "version": "1.0.40", + "version": "1.0.41", "description": "CLI tool for extended productivity with AWS Serverless Application Model (SAM)", "main": "index.js", "scripts": { @@ -68,6 +68,7 @@ "rimraf": "^5.0.1", "toml": "^3.0.0", "tsc-watch": "^6.0.4", + "uuid": "^9.0.0", "yaml": "^2.3.1", "yaml-cfn": "^0.3.2" }, diff --git a/src/commands/local/lib/connect.js b/src/commands/local/lib/connect.js index 78b8522..7711edf 100644 --- a/src/commands/local/lib/connect.js +++ b/src/commands/local/lib/connect.js @@ -16,17 +16,31 @@ import { fork, spawnSync } from 'child_process'; import samConfigParser from '../../../shared/samConfigParser.js'; import { locateHandler } from "./handler-finder.js"; import { findSAMTemplateFile, parse } from "../../../shared/parser.js"; +import { v4 as uuidv4 } from "uuid"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const mac = getMac(); + + +let uuid; +if (fs.existsSync(`${homedir()}/.lambda-debug/uuid`)) { + uuid = fs.readFileSync(`${homedir()}/.lambda-debug/uuid`).toString(); +} else { + uuid = uuidv4(); + try { + fs.mkdirSync(`${homedir()}/.lambda-debug`, { recursive: true }); + } catch (e) { + } + fs.writeFileSync(`${homedir()}/.lambda-debug/uuid`, uuid); +} + let targetConfig = {}; if (fs.existsSync(`samconfig.toml`)) { targetConfig = samConfigParser.parse(); } targetConfig = { - stack_name: targetConfig.stack_name || process.env.SAMP_STACKNAME, - region: targetConfig.region || process.env.SAMP_REGION, - profile: targetConfig.profile || process.env.SAMP_PROFILE + stack_name: valueOrNull(process.env.SAMP_STACKNAME) || targetConfig.stack_name, + region: valueOrNull(process.env.SAMP_REGION) || targetConfig.region, + profile: valueOrNull(process.env.SAMP_PROFILE) || targetConfig.profile } console.log(`Using profile: ${targetConfig.profile || 'default'}`); @@ -165,7 +179,7 @@ if (!fs.existsSync(".lambda-debug")) { }); if (!fs.existsSync(zipFilePath)) { console.log(`Creating Lambda artifact zip`); - fs.writeFileSync(path.join(__dirname, 'relay', 'config.json'), JSON.stringify({ mac: mac, endpoint: endpoint })); + fs.writeFileSync(path.join(__dirname, 'relay', 'config.json'), JSON.stringify({ uuid, endpoint: endpoint })); // install npm package sin relay folder const npm = os.platform() === 'win32' ? 'npm.cmd' : 'npm'; console.log(`Installing npm packages in relay lambda function folder`); @@ -214,6 +228,7 @@ const functionSources = functions.map(key => { return { uri: template.Resources[ baseDir = process.env.outDir + "/" + obj.uri; } } + const { module, handlerMethod, runtime } = locateHandler(template, obj, baseDir); map[obj.name] = { module, @@ -253,7 +268,7 @@ client.on('error', function (err) { client.on('connect', async function () { console.log('Ready for requests...') fs.writeFileSync(".lambda-debug", JSON.stringify({ stack, endpoint, certData, functions, template, envConfig: targetConfig, accountId })); - client.subscribe('lambda-debug/event/' + mac); + client.subscribe('lambda-debug/event/' + uuid); }); const eventQueue = {}; @@ -277,7 +292,7 @@ client.on('message', async function (topic, message) { frk.on('message', (result) => { console.log(`Result: ${result}`); - client.publish(`lambda-debug/callback/${mac}/${obj.sessionId}`, result); + client.publish(`lambda-debug/callback/${uuid}/${obj.sessionId}`, result); }); // @@ -296,7 +311,7 @@ if (!targetConfig.childProcess) { } process.on("exit", async (x) => { // delete certificates - client.publish('lambda-debug/callback/' + mac, JSON.stringify({ event: 'lambda-debug-exit', context: {} })); + client.publish('lambda-debug/callback/' + uuid, JSON.stringify({ event: 'lambda-debug-exit', context: {} })); client.end(); await iotClient.send(new DetachPolicyCommand({ policyName: policyName, @@ -365,3 +380,9 @@ function debugInProgress() { return fs.existsSync(".lambda-debug"); } +function valueOrNull(str) { + if (!str) { + return null; + } + return str.length > 0 ? str : null; +} \ No newline at end of file diff --git a/src/commands/local/lib/relay/relay.js b/src/commands/local/lib/relay/relay.js index d90b180..b4556fc 100644 --- a/src/commands/local/lib/relay/relay.js +++ b/src/commands/local/lib/relay/relay.js @@ -91,7 +91,7 @@ function publishEvent(event, context) { } function publish(payload, sessionId) { - client.subscribe(`lambda-debug/callback/${config.mac}/${sessionId}`); - client.publish('lambda-debug/event/' + config.mac, payload); + client.subscribe(`lambda-debug/callback/${config.uuid}/${sessionId}`); + client.publish('lambda-debug/event/' + config.uuid, payload); } diff --git a/src/commands/local/local.js b/src/commands/local/local.js index 51bac95..7014d75 100644 --- a/src/commands/local/local.js +++ b/src/commands/local/local.js @@ -12,10 +12,10 @@ const samConfigParser = require('../../shared/samConfigParser'); const runtimeEnvFinder = require('./runtime-env-finder'); let env; function setEnvVars(cmd) { - process.env.SAMP_PROFILE = cmd.profile || process.env.AWS_PROFILE || "default"; - process.env.SAMP_REGION = cmd.region || process.env.AWS_REGION; - process.env.SAMP_STACKNAME = process.env.SAMP_STACKNAME || cmd.stackName || process.env.stackName; - process.env.SAMP_CDK_STACK_PATH = cmd.construct || process.env.SAMP_CDK_STACK_PATH; + process.env.SAMP_PROFILE = cmd.profile || process.env.AWS_PROFILE || ''; + process.env.SAMP_REGION = cmd.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || ''; + process.env.SAMP_STACKNAME = process.env.SAMP_STACKNAME || cmd.stackName || ''; + process.env.SAMP_CDK_STACK_PATH = cmd.construct || process.env.SAMP_CDK_STACK_PATH || ''; } async function run(cmd) { @@ -38,7 +38,7 @@ async function run(cmd) { if (cmd.debug) { await setupDebug(cmd); - if (env.iac === "cdk") { + if (env.isNodeJS) { process.exit(0); } } else if (env.iac === "cdk" && (!cmd.stackName || !cmd.construct)) { diff --git a/src/commands/local/runtime-support/dotnet/Program.cs b/src/commands/local/runtime-support/dotnet/Program.cs index af9ba66..7c6e1e0 100644 --- a/src/commands/local/runtime-support/dotnet/Program.cs +++ b/src/commands/local/runtime-support/dotnet/Program.cs @@ -119,6 +119,7 @@ static void InvokeFunctionThread(object? data) Console.WriteLine("Method not found."); return; } + // do error handling!!! var response = methodInfo.Invoke(instance, parameterList.ToArray()); var responsesDir = e?.FullPath.Replace("samp-requests", "samp-responses"); diff --git a/src/commands/local/runtime-support/dotnet/iac-support/sam.js b/src/commands/local/runtime-support/dotnet/iac-support/sam.js index 21b4fd3..af6adeb 100644 --- a/src/commands/local/runtime-support/dotnet/iac-support/sam.js +++ b/src/commands/local/runtime-support/dotnet/iac-support/sam.js @@ -37,19 +37,19 @@ async function run(initialised, cmd) { //process.env.outDir = ".samp-out"; await copyAppsettings(); - const dotnetProcess = exec(`dotnet build .samp-out/dotnet.csproj`, {}); + const dotnetProcess = exec(`dotnet watch --project dotnet.csproj`, {cwd: `.samp-out`}); dotnetProcess.stderr.on('data', (data) => print(data)); dotnetProcess.stdout.on('data', (data) => { console.log("dotnet: ", data.toString().replace(/\n$/, '')); - if (data.toString().includes("Time Elapsed") && !initialised) { + if (data.toString().includes("dotnet watch 🚀 Started") && !initialised) { initialised = true; const childProcess = exec(`node ${__dirname}../../../../runner.js run`, {}); childProcess.stdout.on('data', (data) => print(data)); childProcess.stderr.on('data', (data) => print(data)); if (!cmd.debug) { - const pythonProcess = exec(`dotnet run`, {cwd: `${process.cwd()}/.samp-out`}); - pythonProcess.stderr.on('data', (data) => print(data)); - pythonProcess.stdout.on('data', (data) => print(data)); + const runProcess = exec(`dotnet run`, {cwd: `${process.cwd()}/.samp-out`}); + runProcess.stderr.on('data', (data) => print(data)); + runProcess.stdout.on('data', (data) => print(data)); } else { console.log("You can now select '[SAMP] Debug Lambda functions' and start debugging"); } diff --git a/src/commands/local/runtime-support/dotnet/ide-support/visualstudio/setup.js b/src/commands/local/runtime-support/dotnet/ide-support/visualstudio/setup.js index 0d4cd72..346f1d6 100644 --- a/src/commands/local/runtime-support/dotnet/ide-support/visualstudio/setup.js +++ b/src/commands/local/runtime-support/dotnet/ide-support/visualstudio/setup.js @@ -10,7 +10,9 @@ function copyConfig(name) { } catch (error) { } - launchSettings["Debug Lambda functions"] = { + launchSettings.profiles = launchSettings.profiles || {}; + + launchSettings.profiles[name] = { "commandName": "Executable", "executablePath": "$(SolutionDir).samp-out\\bin\\Debug\\net6.0\\dotnet.exe", "workingDirectory": "$(SolutionDir).samp-out", diff --git a/src/commands/local/runtime-support/nodejs/ide-support/vscode/setup.js b/src/commands/local/runtime-support/nodejs/ide-support/vscode/setup.js index daee5b9..191ecb7 100644 --- a/src/commands/local/runtime-support/nodejs/ide-support/vscode/setup.js +++ b/src/commands/local/runtime-support/nodejs/ide-support/vscode/setup.js @@ -5,6 +5,7 @@ const runtimeEnvFinder = require('../../../../runtime-env-finder'); const pwd = process.cwd(); function copyConfig(name, args) { + let env = runtimeEnvFinder.determineRuntime(); let launchJson; if (fs.existsSync(`${pwd}/.vscode/launch.json`)) { let fileContent = fs.readFileSync(`${pwd}/.vscode/launch.json`, "utf8"); @@ -19,7 +20,12 @@ function copyConfig(name, args) { const taskConfig = require(`${__dirname}/tasks.json`); launchConfig.configurations[0].name = name; - launchConfig.configurations[0].args = args; + launchConfig.configurations[0].args = args; + if (env.functionLanguage === "ts") { + launchConfig.configurations[0].outFiles = [ + "${workspaceFolder}/.samp-out/**/*.js" + ] + } if (!launchJson.configurations.find(c => c.name === name)) { launchJson.configurations.push(launchConfig.configurations[0]); diff --git a/src/commands/policy/index.js b/src/commands/policy/index.js index 2abc917..93a9eec 100644 --- a/src/commands/policy/index.js +++ b/src/commands/policy/index.js @@ -4,7 +4,7 @@ const iamPolicies = require("@mhlabs/iam-policies-cli/src/input-wizard") program .command("policy") .alias("p") - .description("Opens a wizard thet help you to create and attach a new IAM policy to a resource in your template") + .description("Opens a wizard that helps you create and attach a new IAM policy to a resource in your template") .option( "-t, --template ", "Template file name", diff --git a/src/commands/share/share.js b/src/commands/share/share.js index 65db9f4..d490d88 100644 --- a/src/commands/share/share.js +++ b/src/commands/share/share.js @@ -98,7 +98,7 @@ async function run(cmd) { template.Resources[resource] || sharedTemplate.Resources[resource]; const props = resourceObj.Properties; const runtime = runtimes.filter( - (p) => p.name === (props.Runtime || template.Globals.Function.Runtime) + (p) => p.name === (props.Runtime || template.Globals?.Function?.Runtime) )[0]; if ( resourceObj.Type === "AWS::Serverless::Function" && @@ -229,9 +229,8 @@ async function run(cmd) { } } -async function pushCode(file, relativeFilePath, repo, name, language) { +async function pushCode(file, relativeFilePath, repo, name, language = "") { const code = fs.readFileSync(file).toString(); - await githubUtil.putContent( repo.owner, repo.repo,