-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from golemfactory/feature/JST-424/New-command
feat(new app command): implemented new app creation command from template
- Loading branch information
Showing
10 changed files
with
300 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.* | ||
tsconfig.json | ||
node_modules/ | ||
data/**/package-lock.json | ||
data/**/node_modules/ | ||
src/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "nodejs-golem-app", | ||
"version": "1.0.0", | ||
"description": "NodeJS script using Golem Network", | ||
"main": "src/index.js", | ||
"type": "module", | ||
"scripts": { | ||
"run": "node src/index.js", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "", | ||
"license": "ISC", | ||
"keywords": [ | ||
"golem", | ||
"network", | ||
"application" | ||
], | ||
"dependencies": { | ||
"@golem-sdk/golem-js": "^0.11.2", | ||
"dotenv": "^16.3.1" | ||
}, | ||
"devDependencies": { | ||
"@golem-sdk/cli": "^1.0.0-beta.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import * as dotenv from "dotenv"; | ||
import { LogLevel, ProposalFilters, TaskExecutor } from "@golem-sdk/golem-js"; | ||
|
||
dotenv.config(); | ||
|
||
(async function main() { | ||
const executor = await TaskExecutor.create({ | ||
// What do you want to run | ||
package: "golem/node:20-alpine", | ||
|
||
// How much you wish to spend | ||
budget: 0.5, | ||
proposalFilter: ProposalFilters.limitPriceFilter({ | ||
start: 0.1, | ||
cpuPerSec: 0.1 / 3600, | ||
envPerSec: 0.1 / 3600, | ||
}), | ||
|
||
// Where you want to spend | ||
payment: { | ||
network: "polygon", | ||
}, | ||
|
||
// Control the execution of tasks | ||
maxTaskRetries: 0, | ||
|
||
// Useful for debugging | ||
logLevel: LogLevel.Info, | ||
taskTimeout: 5 * 60 * 1000, | ||
}); | ||
|
||
try { | ||
// Your code goes here | ||
} catch (err) { | ||
console.error("Running the task on Golem failed due to", err); | ||
} finally { | ||
await executor.end(); | ||
} | ||
})(); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,9 +34,6 @@ | |
"marketplace", | ||
"cli" | ||
], | ||
"files": [ | ||
"dist" | ||
], | ||
"author": "GolemFactory <[email protected]>", | ||
"license": "GPL-3.0", | ||
"engines": { | ||
|
@@ -46,6 +43,7 @@ | |
"ajv": "^8.12.0", | ||
"ajv-formats": "^2.1.1", | ||
"commander": "^11.0.0", | ||
"enquirer": "^2.4.1", | ||
"lodash": "^4.17.21", | ||
"luxon": "^3.4.3", | ||
"new-find-package-json": "^2.0.0", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import { NewOptions, newProjectNameError, newProjectNameRegEx } from "./new.options"; | ||
|
||
import { prompt } from "enquirer"; | ||
import { join } from "path"; | ||
import { existsSync } from "fs"; | ||
import { cp, readFile, writeFile } from "fs/promises"; | ||
|
||
async function getName(providedName: string): Promise<string> { | ||
if (!providedName) { | ||
const result = (await prompt({ | ||
type: "input", | ||
name: "name", | ||
message: "Project name", | ||
validate(value: string) { | ||
return !newProjectNameRegEx.test(value) ? newProjectNameError : true; | ||
}, | ||
})) as { name: string }; | ||
|
||
providedName = result.name; | ||
} else { | ||
if (!newProjectNameRegEx.test(providedName)) { | ||
console.error(`Error: Project name ${providedName} is invalid: ${newProjectNameError}`); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
return providedName; | ||
} | ||
|
||
async function getVersion(providedVersion?: string): Promise<string> { | ||
if (typeof providedVersion === "string") { | ||
return providedVersion; | ||
} | ||
|
||
const result = (await prompt({ | ||
type: "input", | ||
name: "version", | ||
message: "Project version", | ||
initial: "1.0.0", | ||
})) as { version: string }; | ||
|
||
return result.version; | ||
} | ||
|
||
async function getTemplate(providedTemplate?: string): Promise<string> { | ||
if (typeof providedTemplate !== "string") { | ||
const result = (await prompt({ | ||
type: "select", | ||
name: "template", | ||
message: "Select a project template", | ||
choices: [ | ||
{ name: "js-node", hint: "Plain Javascript CLI application" }, | ||
// { name: "js-webapp", hint: "Plain Javascript Express based web application" }, | ||
], | ||
})) as { template: string }; | ||
|
||
providedTemplate = result.template; | ||
} | ||
|
||
if (!newProjectNameRegEx.test(providedTemplate)) { | ||
console.error(`Error: Template name ${providedTemplate} is invalid.`); | ||
process.exit(1); | ||
} | ||
|
||
return providedTemplate; | ||
} | ||
|
||
async function getDescription(providedDescription?: string): Promise<string> { | ||
if (typeof providedDescription === "string") { | ||
return providedDescription; | ||
} | ||
|
||
const result = (await prompt({ | ||
type: "input", | ||
name: "description", | ||
message: "Project description", | ||
initial: "An unique and awesome application that runs on Golem Network", | ||
})) as { description: string }; | ||
|
||
return result.description; | ||
} | ||
|
||
type PackageJsonBasic = { | ||
name: string; | ||
description: string; | ||
version: string; | ||
author?: string; | ||
}; | ||
|
||
async function updatePackageJson(projectPath: string, data: PackageJsonBasic): Promise<void> { | ||
const packageJson = join(projectPath, "package.json"); | ||
let input: string; | ||
let json: PackageJsonBasic; | ||
|
||
try { | ||
input = await readFile(packageJson, "utf8"); | ||
} catch (e) { | ||
console.error(`Error: Failed to read ${packageJson}: ${e}`); | ||
process.exit(1); | ||
} | ||
|
||
try { | ||
json = JSON.parse(input); | ||
} catch (e) { | ||
console.error(`Error: Failed to parse ${packageJson}: ${e}`); | ||
process.exit(1); | ||
} | ||
|
||
json.name = data.name; | ||
json.description = data.description; | ||
json.version = data.version; | ||
if (data.author) { | ||
json.author = data.author; | ||
} | ||
|
||
try { | ||
await writeFile(packageJson, JSON.stringify(json, null, 2)); | ||
} catch (e) { | ||
console.error(`Error: Failed to write ${packageJson}: ${e}`); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
export async function newAction(providedName: string, options: NewOptions) { | ||
const name = await getName(providedName); | ||
const projectPath = options.path ?? join(process.cwd(), name); | ||
if (existsSync(projectPath)) { | ||
console.error(`Error: ${projectPath} already exists.`); | ||
process.exit(1); | ||
} | ||
|
||
const template = await getTemplate(options.template); | ||
const templatePath = join(__dirname, "../../data/project-templates", template); | ||
if (!existsSync(templatePath)) { | ||
console.error(`Error: Template ${template} not found.`); | ||
process.exit(1); | ||
} | ||
|
||
const description = await getDescription(options.description); | ||
const version = await getVersion(options.version); | ||
const author = options.author; | ||
|
||
console.log(`Creating a new Golem app in ${projectPath}.`); | ||
|
||
try { | ||
await cp(templatePath, projectPath, { recursive: true }); | ||
} catch (e) { | ||
console.error(`Error: Failed to copy template files: ${e}`); | ||
process.exit(1); | ||
} | ||
|
||
// Update package.json | ||
await updatePackageJson(projectPath, { | ||
name, | ||
description, | ||
version, | ||
author, | ||
}); | ||
|
||
// TODO: Consider running npm install (or yarn, or whatever). | ||
|
||
console.log(`Project created successfully in ${projectPath}.`); | ||
|
||
if (!process.env.YAGNA_APPKEY) { | ||
console.log( | ||
"NOTE: You do not seem to have YAGNA_APPKEY environment variable defined. You will need to define it or provide a .env file with it to run your new appplication.", | ||
); | ||
} | ||
|
||
// TODO: Show some next steps, or pull it from template directory. | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Command } from "commander"; | ||
import { NewOptions } from "./new.options"; | ||
|
||
export const newCommand = new Command("new"); | ||
|
||
newCommand | ||
.summary("Create new Golem project.") | ||
.description("Create a new Golem project from template.") | ||
.option("-p, --path <path>", "Path of the new project.") | ||
.option("-t, --template <template>", "Template to be used for the project.") | ||
.option("-d, --description <text>", "Description of the project.") | ||
.option("-a, --author <name>", "Author of the project.") // TODO: try to read it from git config? | ||
.option("-v, --version <version>", "Version of the project.") | ||
// TODO: implement list-templates? | ||
// .option("-l, --list-templates", "List available projecttemplates.") | ||
.argument("[name]", "Name of the project.") | ||
.action(async (name: string, options: NewOptions) => { | ||
const action = await import("./new.action.js"); | ||
await action.default.newAction(name, options); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export interface NewOptions { | ||
author?: string; | ||
description?: string; | ||
path?: string; | ||
template?: string; | ||
version?: string; | ||
} | ||
|
||
export const newProjectNameRegEx = /^[a-z0-9-_]+$/; | ||
export const newProjectNameError = | ||
"Project name may only contain lower case letters, numbers, hyphens and underscores."; |