-
Notifications
You must be signed in to change notification settings - Fork 0
feat: initial implementation #1
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
Changes from all commits
b2514c5
1873019
f310a98
c6fe98f
62d5035
78e8ec1
0bb961c
138911b
0aeba42
47d47dd
ce4d38e
8a5c921
48004b6
e95c61e
98afcb9
4b351a8
1b1c616
985cff2
d9afb37
1654736
ea4d56b
e29f1a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| on: push | ||
| jobs: | ||
| check-yarn: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: "16.x" | ||
| registry-url: "https://registry.npmjs.org" | ||
| - run: yarn install | ||
| - run: | | ||
| git config --global user.email "test@test.com" | ||
| git config --global user.name "CI Test" | ||
| - run: yarn test:yarn | ||
| check-npm: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: "16.x" | ||
| registry-url: "https://registry.npmjs.org" | ||
| - run: yarn install | ||
| - run: | | ||
| git config --global user.email "test@test.com" | ||
| git config --global user.name "CI Test" | ||
| - run: yarn test:npm |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| - dev | ||
| jobs: | ||
| release: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: "16.x" | ||
| registry-url: "https://registry.npmjs.org" | ||
| - run: yarn install | ||
| - run: npx semantic-release | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| node_modules/ | ||
| dist/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -ue pipefail | ||
|
|
||
| ORIGINAL_DIRECTORY="$(pwd)" | ||
| PACKAGE_NAME="create-functionless" | ||
| PACKED_NAME="${PACKAGE_NAME}.tgz" | ||
| TEST_PROJECT="test-project" | ||
|
|
||
| function cleanup() { | ||
| cd $ORIGINAL_DIRECTORY | ||
| yarn global remove create-functionless || true | ||
| rm -fr ${PACKED_NAME} || true | ||
| rm -fr ${TEST_PROJECT} || true | ||
| } | ||
|
|
||
| trap cleanup EXIT | ||
|
|
||
| npm run release | ||
| npm pack | ||
| mv ${PACKAGE_NAME}*.tgz ${PACKED_NAME} | ||
| npm i -g ${PACKED_NAME} | ||
|
Comment on lines
+21
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't you install directly from the tgz? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cleanup hook needs a consistent name. |
||
|
|
||
| # Use the create script to create a new project | ||
| create-functionless ${TEST_PROJECT} | ||
| cd ${TEST_PROJECT} | ||
|
|
||
| # Verify new project can synth | ||
| npm run synth | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -ue pipefail | ||
|
|
||
| ORIGINAL_DIRECTORY="$(pwd)" | ||
| PACKAGE_NAME="create-functionless" | ||
| PACKED_NAME="${PACKAGE_NAME}.tgz" | ||
| TEST_PROJECT="test-project" | ||
|
|
||
| function cleanup() { | ||
| cd $ORIGINAL_DIRECTORY | ||
| yarn global remove create-functionless || true | ||
| rm -fr ${PACKED_NAME} || true | ||
| rm -fr ${TEST_PROJECT} || true | ||
| } | ||
|
|
||
| trap cleanup EXIT | ||
|
|
||
| # Clean up installs of create-functionless if they exist | ||
| yarn cache clean | ||
|
|
||
| yarn run release | ||
| yarn pack -f ${PACKED_NAME} | ||
| yarn global add --skip-integrity-check --offline "file:$(pwd)/${PACKED_NAME}" | ||
|
|
||
| # Use the create script to create a new project | ||
| yarn create --offline functionless ${TEST_PROJECT} | ||
| cd ${TEST_PROJECT} | ||
|
|
||
| # Verify new project can synth | ||
| yarn synth |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| { | ||
| "name": "create-functionless", | ||
| "version": "0.1.0", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reasoning around this over 0.0.0? I don't have strong opinions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. semantic release will use |
||
| "description": "Create a Functionless CDK app.", | ||
| "files": [ | ||
| "dist", | ||
| "templates/**/*" | ||
| ], | ||
| "bin": { | ||
| "create-functionless": "./dist/index.js" | ||
| }, | ||
| "scripts": { | ||
| "test:npm": "./bin/test-npm.sh", | ||
| "test:yarn": "./bin/test-yarn.sh", | ||
| "prerelease": "rimraf ./dist/", | ||
| "release": "esbuild --bundle src/index.ts --outfile=dist/index.js --platform=node", | ||
| "prepublishOnly": "npm run release" | ||
| }, | ||
| "keywords": [ | ||
| "functionless" | ||
| ], | ||
| "author": "Functionless", | ||
| "license": "Apache-2.0", | ||
| "devDependencies": { | ||
| "@types/cross-spawn": "^6.0.2", | ||
| "@types/mustache": "^4.2.1", | ||
| "@types/node": "^18.7.14", | ||
| "@types/prompts": "^2.0.14", | ||
| "@types/validate-npm-package-name": "^4.0.0", | ||
| "aws-cdk-lib": "^2.40.0", | ||
| "chalk": "^5.0.1", | ||
| "commander": "^9.4.0", | ||
| "cross-spawn": "^7.0.3", | ||
| "esbuild": "^0.15.6", | ||
| "functionless": "^0.22.6", | ||
| "mustache": "^4.2.0", | ||
| "prompts": "^2.4.2", | ||
| "rimraf": "^3.0.2", | ||
| "semantic-release": "^19.0.5", | ||
| "typescript": "^4.8.2", | ||
| "validate-npm-package-name": "^4.0.0" | ||
| }, | ||
| "release": { | ||
| "branches": [ | ||
| "main", | ||
| { | ||
| "name": "dev", | ||
| "prerelease": true | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| #!/usr/bin/env node | ||
| import chalk from "chalk"; | ||
| import { Command } from "commander"; | ||
| import spawn from "cross-spawn"; | ||
| import fs from "fs"; | ||
| import mustache from "mustache"; | ||
| import path from "path"; | ||
|
|
||
| import packageJson from "../package.json"; | ||
| import { | ||
| askProjectName, | ||
| failOnError, | ||
| getPackageManager, | ||
| installPackages, | ||
| } from "./util"; | ||
|
|
||
| const program = new Command(); | ||
|
|
||
| let projectName: string | undefined; | ||
|
|
||
| program | ||
| .name(packageJson.name) | ||
| .version(packageJson.version) | ||
| .arguments("[projectName]") | ||
| .action((name) => { | ||
| if (typeof name === "string") { | ||
| projectName = name; | ||
| } | ||
| }) | ||
| .parse(process.argv); | ||
|
|
||
| run().catch((err) => { | ||
| console.error(err); | ||
| process.exit(1); | ||
| }); | ||
|
|
||
| async function run() { | ||
| const packageMangager = getPackageManager(); | ||
|
|
||
| const templateName = "default"; | ||
| const templateRoot = path.join(__dirname, "..", "templates", templateName); | ||
| const templateManifestPath = path.join(templateRoot, "manifest.json"); | ||
| const templateManifest: string[] = JSON.parse( | ||
| await fs.promises.readFile(templateManifestPath, "utf-8") | ||
| ); | ||
|
|
||
| if (!projectName) { | ||
| projectName = await askProjectName(); | ||
| } | ||
|
|
||
| console.log(); | ||
| console.log(`Creating ${chalk.green(projectName)}...`); | ||
|
|
||
| const root = path.resolve(projectName); | ||
|
|
||
| try { | ||
| await fs.promises.mkdir(root); | ||
| } catch (err: any) { | ||
| if (err.code === "EEXIST") { | ||
sam-goodwin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| console.error(`${chalk.red(`Folder already exists: ${projectName}`)}`); | ||
| } | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const renderTemplate = async ( | ||
| localPath: string, | ||
| data: Record<string, unknown> | ||
| ) => { | ||
| const templateFilePath = path.join(templateRoot, localPath); | ||
| const templateContent = mustache.render( | ||
| await fs.promises.readFile(templateFilePath, "utf-8"), | ||
| data | ||
| ); | ||
| // Npm won't include `.gitignore` files in a package. | ||
| // This allows you to add .template as a file ending | ||
| // and it will be removed when rendered in the end | ||
| // project. | ||
|
Comment on lines
+74
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand what you mean by this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take a look at https://github.com/functionless/create-functionless/pull/1/files#diff-b3dcca123bc557a15a933374361c741e8f04f515436c7aaa8f9fa89618f6c4c8 If I were to keep the name as |
||
| const destinationPath = path.join(root, localPath.replace(".template", "")); | ||
| await fs.promises.mkdir(path.dirname(destinationPath), { | ||
| recursive: true, | ||
| }); | ||
| await fs.promises.writeFile(destinationPath, templateContent); | ||
| }; | ||
|
|
||
| const templateData = { | ||
| projectName, | ||
| }; | ||
|
|
||
| await Promise.all( | ||
| templateManifest.map((path) => renderTemplate(path, templateData)) | ||
| ); | ||
|
|
||
| process.chdir(root); | ||
|
|
||
| console.log(); | ||
| console.log("Installing packages..."); | ||
| console.log(); | ||
|
Comment on lines
+95
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: provide a helper or use |
||
|
|
||
| const dependencies = [ | ||
| "@aws-cdk/aws-appsync-alpha", | ||
| "@functionless/ast-reflection", | ||
| "@functionless/language-service", | ||
| "aws-cdk", | ||
| "aws-cdk-lib", | ||
| "aws-sdk", | ||
| "constructs", | ||
| "esbuild", | ||
| "functionless", | ||
| "typesafe-dynamodb", | ||
| "typescript", | ||
| ]; | ||
|
|
||
| installPackages(packageMangager, dependencies); | ||
|
|
||
| console.log(); | ||
| console.log("Initializing git repository..."); | ||
| console.log(); | ||
|
|
||
| const gitErrorMessage = "Error initializing git repository."; | ||
|
|
||
| failOnError( | ||
| spawn.sync("git", ["init", "-q"], { | ||
tvanhens marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| stdio: "inherit", | ||
| }), | ||
| gitErrorMessage | ||
| ); | ||
|
|
||
| failOnError( | ||
| spawn.sync("git", ["add", "."], { | ||
| stdio: "inherit", | ||
| }), | ||
| gitErrorMessage | ||
| ); | ||
|
|
||
| failOnError( | ||
| spawn.sync("git", ["commit", "-q", "-m", "initial commit"], { | ||
| stdio: "inherit", | ||
| }), | ||
| gitErrorMessage | ||
| ); | ||
|
|
||
| console.log(chalk.green("Project ready!")); | ||
| console.log(); | ||
| console.log(`Run ${chalk.yellow(`cd ./${projectName}`)} to get started.`); | ||
|
|
||
| process.exit(0); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import chalk from "chalk"; | ||
| import spawn from "cross-spawn"; | ||
| import prompts from "prompts"; | ||
| import validateProjectName from "validate-npm-package-name"; | ||
|
|
||
| export function failOnError( | ||
| response: ReturnType<typeof spawn.sync>, | ||
| message: string | ||
| ) { | ||
| if (response.status !== 0) { | ||
| console.error(chalk.red(message)); | ||
| process.exit(1); | ||
| } | ||
| } | ||
|
|
||
| export async function askProjectName() { | ||
| const defaultName = "new-project"; | ||
|
|
||
| const answer = await prompts({ | ||
| type: "text", | ||
| name: "projectName", | ||
| message: "Project name:", | ||
| initial: defaultName, | ||
| validate: (name) => { | ||
| const result = validateProjectName(name); | ||
| if (result.validForNewPackages) { | ||
| return true; | ||
| } | ||
| return `Invalid project name: ${name}`; | ||
| }, | ||
| }); | ||
|
|
||
| if (typeof answer.projectName === "string") { | ||
| return answer.projectName.trim(); | ||
| } | ||
|
|
||
| return defaultName; | ||
| } | ||
|
|
||
| export type PackageManager = "yarn" | "pnpm" | "npm"; | ||
|
|
||
| export function getPackageManager(): PackageManager { | ||
| const packageManager = process.env.npm_config_user_agent; | ||
|
|
||
| if (packageManager?.startsWith("yarn")) { | ||
| return "yarn"; | ||
| } else if (packageManager?.startsWith("pnpm")) { | ||
| return "pnpm"; | ||
| } else { | ||
| return "npm"; | ||
| } | ||
| } | ||
|
|
||
| export function installPackages( | ||
| manager: PackageManager, | ||
| dependencies: string[] | ||
| ) { | ||
| let executable = "npm"; | ||
| let command = "install"; | ||
|
|
||
| if (manager === "yarn") { | ||
| executable = "yarn"; | ||
| command = "add"; | ||
| } | ||
|
|
||
| if (manager == "pnpm") { | ||
| executable = "pnpm"; | ||
| } | ||
|
|
||
| failOnError( | ||
| spawn.sync(executable, [command, "-D", ...dependencies], { | ||
| stdio: "inherit", | ||
| }), | ||
| "Unable to install dependencies" | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| .swc/ | ||
| cdk.out/ | ||
| node_modules/ |
Uh oh!
There was an error while loading. Please reload this page.