Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(scaffolder): add scaffolder command #277

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

tavsec
Copy link

@tavsec tavsec commented Sep 19, 2024

neonctl scaffold

This PR will add scaffold command to the neonctl tool. This command consists of two subcommands list and start.

image

list

The list subcommand will list all of the available templates from templates repository, and display the via the default output. It utilises GitHub API to list the folders from the repository.

Note

The "template" repository has to be created and populated before this functionality can be tested. For development purposes, I created temporary repository https://github.com/neon-scaffolder/templates

image

start <template-name>

This subcommand will initialize the scaffolding process. If the project-id flag is set, then the newly scaffolded directory will be templated using the data from the given project. Otherwise, new Neon project will be created. This subcommand accepts the same flags as neonctl projects create.

image

Templating

One of the biggest functionality that this commands provide is for community users to declare their own scaffolding templates, that can be used and selected by end users. This is done by using [mustache.js] (https://github.com/janl/mustache.js) templating engine. Available templating variables are project API object, and connection strings, but this can be extended if needed.

TODO:

  • Create templates repository
  • Add templating documentation, so new templates can be added by contributors

@davidgomes
Copy link
Collaborator

@tavsec we'll get to trying this out soon!

{
connection_uri: connectionString,
connection_parameters: {
database: projectData.project.name,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project name is not the database name, project names could have spaces and emoji etc. I guess you could try to parse out the path from the URL but there might be a better way.

@clarkbw
Copy link

clarkbw commented Oct 7, 2024

👋🏼 this is very cool. here's some feedback i wrote up:

I ended up with an .env.example file that still has templating (this is feedback for astro actually)

{{#connection_uris}}
DATABASE_URL="postgresql://{{connection_parameters.role}}:{{connection_parameters.password}}@{{connection_parameters.host}}:5432/{{connection_parameters.database}}?sslmode=require"
{{/connection_uris}}

I like how it creates the .env file automatically for me, that avoids an annoying step.


scaffold start could have a much better error state, perhaps show the list of scaffolds available instead or at least remind me of the command.

Screenshot 2024-10-05 at 7 17 34 AM

Part of this issue will be fixed when we allow free users more than 1 project but I don't think it should create a new project automatically. Would be better to ask if I want to reuse a project or at least list out the project names and ids along with the parameter so i could choose 1 without jumping to the console. I ended up going into the console, finding the project id and then running scaffold start --help to find the parameter required to pass in a project.

Screenshot 2024-10-05 at 7 18 17 AM

…database#297)

Move action repo from personal account to org, and bump to last version.
@tavsec
Copy link
Author

tavsec commented Oct 14, 2024

Hey @clarkbw thank you so much for taking the time and reviewing this PR, and also sorry for the late response from my side.

Regarding the first point (.env.example), do you think we should add new property to config.neon.json file in the template directory (https://github.com/neon-scaffolder/templates/blob/main/astro/config.neon.json) to include array of "cleanup_files", which will delete the listed files after the processing? This way, we could have file .env.template, which would get templated into .env, and at the end, .env.template would get deleted. What do you think about that?

As for the second point (error handling and project selection), I will update the code with better validation and error handling (check if project exists, check if enough parameters has been passed, ...), and change the behavior in case that no arguments have been passed (ask the user if he wants to reuse the project or create new one).

Thanks again, will push the updates soon 😃

@clarkbw
Copy link

clarkbw commented Oct 16, 2024

This way, we could have file .env.template, which would get templated into .env, and at the end, .env.template would get deleted. What do you think about that?

that sounds like a good pattern to me.

@tavsec
Copy link
Author

tavsec commented Oct 17, 2024

@clarkbw In the latest commit, I added the following:

  • If user does not pass --project-id flag, the command will ask him if he wants to create New project or select from the list of user's projects
  • If --template-id flag is not present, the command will prompt user to select the template from the template list
  • Added new property delete_files in the template directory, which contains a list of files that get deleted after templating

@davidgomes
Copy link
Collaborator

@tavsec since the UX/DevEx is now addressed, we'll do a code review soon

@pffigueiredo pffigueiredo self-requested a review October 18, 2024 08:48
@pffigueiredo
Copy link

@tavsec since the UX/DevEx is now addressed, we'll do a code review soon

@tavsec the PR is marked as "Draft", is there any work left to do "logic-wise", or it's mostly because of these?

 - Create templates repository
 - Add templating documentation, so new templates can be added by contributors

@tavsec
Copy link
Author

tavsec commented Oct 18, 2024

@tavsec since the UX/DevEx is now addressed, we'll do a code review soon

@tavsec the PR is marked as "Draft", is there any work left to do "logic-wise", or it's mostly because of these?

 - Create templates repository
 - Add templating documentation, so new templates can be added by contributors

There is nothing left logic-wise, mostly just the tasks on the list you posted

package.json Outdated
@@ -58,13 +59,17 @@
"@segment/analytics-node": "^1.0.0-beta.26",
"axios": "^1.4.0",
"axios-debug-log": "^1.0.0",
"buffer": "^6.0.3",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this dependency being used in?


const MULTITHREADING_LIMIT = 10;

export async function getContent(owner: string, repository: string) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we use an object to pass the params instead of 2 string args? It just makes it a bit less error-prone:

e.g.

export async function getContent({owner, repository}: {owner: string, repository: string}) {
...
}


const GENERATED_FOLDER_PREFIX = 'neon-';

// TODO: Maybe move to constants file?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the TODO, colocation is more than fine here 👍

'List available templates',
(yargs) => yargs,
async (args) => {
// @ts-expect-error: TODO - Assert `args` is `CommonProps`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make use of the yargs.Argv generic and remove this comment:

e.g.

export const builder = (argv: yargs.Argv<CommonProps>) => {

'aws-eu-central-1',
'aws-us-east-2',
'aws-us-east-1',
];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also include Azure regions by now? cc @davidgomes


if (!availableTemplates.includes(props.templateId)) {
log.error(
'Template not found. Please make sure the template exists and is public.',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's add the templateID to this error message, should be easier to follow up from a user perspective

await getFileContent(
REPOSITORY_OWNER,
REPOSITORY,
(props.templateId as string) + '/config.neon.json',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this '/config.neon.json' for? If it's relevant, let's extract it into it's own const

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file config.neon.json is part of every "template" project and includes which files should be templated, copied and/or deleted in the scaffolding process. Example of such file: https://github.com/neon-scaffolder/templates/blob/main/astro/config.neon.json .

I'm not entirely sure what you mean by extracting it into it's own const. Currently, it is extracted into its own variable config, but I cannot change it to const, as it is assigned in the try-catch block.

props.templateId,
dir,
);
} else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this a possible code path? seems like we are returning early in line 250 if the templateID doesn't exist?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right, this is dead code branch. Removed it.

import path from 'path';
import fs from 'fs';

const MULTITHREADING_LIMIT = 10;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we rename this to CONCURRENT_OPERATIONS_LIMIT? The current name is clear, but it's not threads that we are referring here

'/' +
repository +
'/main/' +
path;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we extract this into a util function that builds the URL? Since it's used in multiple place

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted into own util function 👍

@tavsec
Copy link
Author

tavsec commented Nov 9, 2024

@pffigueiredo Thanks for the review! I pushed some changes/fixes, feel free to review again :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants