Skip to content

Commit 97e055e

Browse files
committed
[TASK] add option to init local repo from template
1 parent 15f2489 commit 97e055e

7 files changed

Lines changed: 270 additions & 93 deletions

File tree

lib/cmd/init.js

Lines changed: 118 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,131 @@
11
import * as TYPES from '../types/types.js' // eslint-disable-line
2-
import { checkIsOnline } from '../utils/check.js'
2+
import { checkIsOnline, checkSshConnection } from '../utils/check.js'
33
import { checkGithubTokenEnv, checkGithubToken, getGithubOrgs } from '../github/auth.js'
4-
import { githubRepoFromTemplatePrompts, initType } from './init/prompts.js'
4+
import { githubRepoFromTemplatePrompts, localRepoFromTemplatePrompts, initType } from './init/prompts.js'
55
import { createGithubRepoWithTemplate, updateGithubRepoSettings, updateGithubBranchProtection } from '../github/repo.js'
66
import { addRepositorySecrets } from '../github/secrets.js'
77
import { updateAndCommit } from '../github/commit.js'
88
import { confirmNextSteps } from '../utils/prompts.js'
9+
import { gitCloneDepth1, gitInit, gitAddAll, gitCommit } from '../git/git.js'
10+
import { parseJson, removePath, writeJson } from '../utils/fs.js'
911
import chalk from 'chalk'
1012

1113
/**
1214
* @ignore
1315
* @typedef {TYPES.LOCALDATA} LOCALDATA {@link LOCALDATA}
1416
*/
1517

18+
/**
19+
* #### Create a new GitHub repo based on a GitHub hosted template
20+
* @param {LOCALDATA} data - local data
21+
* @private
22+
* @async
23+
* @returns undefined
24+
*/
25+
async function initGithubRepoBasedOnTemplate (data) {
26+
checkGithubTokenEnv()
27+
await checkIsOnline()
28+
await checkGithubToken(data)
29+
await getGithubOrgs(data)
30+
await githubRepoFromTemplatePrompts(data)
31+
await confirmNextSteps(
32+
`You are about to create a new repository with the following settings:
33+
- New repo owner: ${chalk.blue(data.prompts?.githubRepoFromTmpl?.newRepoOwner)}
34+
- New repo name: ${chalk.green(data.prompts?.githubRepoFromTmpl?.newRepoName)}
35+
- Template owner: ${data.prompts?.githubRepoFromTmpl?.templateRepoOwner}
36+
- Template name: ${data.prompts?.githubRepoFromTmpl?.templateRepoName}
37+
- Private repo: ${chalk.bold.yellow(data.prompts?.githubRepoFromTmpl?.isPrivate)}
38+
- Add secrets: ${chalk.bold.yellow(data.prompts?.githubRepoFromTmpl?.isSecrets)}`
39+
)
40+
await createGithubRepoWithTemplate(data)
41+
await updateGithubRepoSettings(data, {
42+
owner: data.prompts?.githubRepoFromTmpl?.newRepoOwner || '',
43+
repo: data.prompts?.githubRepoFromTmpl?.newRepoName || '',
44+
has_issues: true,
45+
has_projects: false,
46+
has_wiki: false,
47+
allow_rebase_merge: false,
48+
allow_squash_merge: true,
49+
allow_merge_commit: false,
50+
delete_branch_on_merge: true,
51+
allow_update_branch: true,
52+
squash_merge_commit_title: 'PR_TITLE'
53+
})
54+
await updateGithubBranchProtection(data, {
55+
owner: data.prompts?.githubRepoFromTmpl?.newRepoOwner || '',
56+
repo: data.prompts?.githubRepoFromTmpl?.newRepoName || '',
57+
branch: 'master',
58+
required_status_checks: {
59+
strict: true,
60+
contexts: [
61+
'test',
62+
'check-commit-message',
63+
'validate-theme'
64+
]
65+
},
66+
enforce_admins: false,
67+
required_pull_request_reviews: {
68+
dismiss_stale_reviews: true,
69+
required_approving_review_count: 1,
70+
require_last_push_approval: true
71+
},
72+
restrictions: null,
73+
required_linear_history: true,
74+
required_conversation_resolution: true
75+
})
76+
if (data.prompts?.githubRepoFromTmpl?.isSecrets) {
77+
await addRepositorySecrets(data)
78+
}
79+
await updateAndCommit(
80+
data.prompts?.githubRepoFromTmpl?.newRepoOwner || '',
81+
data.prompts?.githubRepoFromTmpl?.newRepoName || '',
82+
['package.json', 'theme/theme.json'],
83+
{
84+
name: data.prompts?.githubRepoFromTmpl?.newRepoName,
85+
label: `${data.prompts?.githubRepoFromTmpl?.newRepoLabel.charAt(0).toUpperCase()}${data.prompts?.githubRepoFromTmpl?.newRepoLabel.slice(1)}`
86+
}
87+
)
88+
}
89+
90+
/**
91+
* #### Create a new local repo based on a GitHub hosted template
92+
* @param {LOCALDATA} data - local data
93+
* @private
94+
* @async
95+
* @returns undefined
96+
*/
97+
async function initLocalRepoBasedOnTemplate (data) {
98+
await checkIsOnline()
99+
await checkSshConnection('git@github.com')
100+
if (data.git?.isRepo) {
101+
console.error(`${chalk.bold.red('Error:')} This script requires to be run in a simple folder, not a git repository\n`)
102+
process.exit(1)
103+
}
104+
await localRepoFromTemplatePrompts(data)
105+
await confirmNextSteps(
106+
`You are about to create a new repository with the following settings:
107+
- New repo name: ${chalk.green(data.prompts?.localRepoFromTmpl?.newRepoName)}
108+
- Template owner: ${data.prompts?.localRepoFromTmpl?.templateRepoOwner}
109+
- Template name: ${data.prompts?.localRepoFromTmpl?.templateRepoName}`
110+
)
111+
if (data.prompts?.localRepoFromTmpl?.newRepoName) {
112+
await gitCloneDepth1(data.prompts?.localRepoFromTmpl?.newRepoName)
113+
await removePath(`${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}/.git`)
114+
await gitInit({ cwd: `${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}` })
115+
await gitAddAll({ cwd: `${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}` })
116+
await gitCommit('Initial commit', { cwd: `${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}` })
117+
const packageJson = await parseJson(`${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}/package.json`)
118+
const themeJson = await parseJson(`${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}/theme/theme.json`)
119+
packageJson.name = data.prompts?.localRepoFromTmpl?.newRepoName
120+
themeJson.name = data.prompts?.localRepoFromTmpl?.newRepoName
121+
themeJson.label = `${data.prompts?.localRepoFromTmpl?.newRepoLabel.charAt(0).toUpperCase()}${data.prompts?.localRepoFromTmpl?.newRepoLabel.slice(1)}`
122+
await writeJson(`${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}/package.json`, packageJson)
123+
await writeJson(`${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}/theme/theme.json`, themeJson)
124+
await gitAddAll({ cwd: `${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}` })
125+
await gitCommit('[TASK] update package.json and theme.json with new project info', { cwd: `${data?.cwd.path}/${data.prompts?.localRepoFromTmpl?.newRepoName}` })
126+
}
127+
}
128+
16129
/**
17130
* #### Show info about the current project
18131
* @param {LOCALDATA} data - local data
@@ -22,68 +135,9 @@ import chalk from 'chalk'
22135
async function init (data) {
23136
const type = await initType()
24137
if (type === 'github-repo-based-on-template') {
25-
checkGithubTokenEnv()
26-
await checkIsOnline()
27-
await checkGithubToken(data)
28-
await getGithubOrgs(data)
29-
await githubRepoFromTemplatePrompts(data)
30-
await confirmNextSteps(
31-
`You are about to create a new repository with the following settings:
32-
- New repo owner: ${chalk.blue(data.prompts?.repoFromTmpl?.newRepoOwner)}
33-
- New repo name: ${chalk.green(data.prompts?.repoFromTmpl?.newRepoName)}
34-
- Template owner: ${data.prompts?.repoFromTmpl?.templateRepoOwner}
35-
- Template name: ${data.prompts?.repoFromTmpl?.templateRepoName}
36-
- Private repo: ${chalk.bold.yellow(data.prompts?.repoFromTmpl?.isPrivate)}
37-
- Add secrets: ${chalk.bold.yellow(data.prompts?.repoFromTmpl?.isSecrets)}`
38-
)
39-
await createGithubRepoWithTemplate(data)
40-
await updateGithubRepoSettings(data, {
41-
owner: data.prompts?.repoFromTmpl?.newRepoOwner || '',
42-
repo: data.prompts?.repoFromTmpl?.newRepoName || '',
43-
has_issues: true,
44-
has_projects: false,
45-
has_wiki: false,
46-
allow_rebase_merge: false,
47-
allow_squash_merge: true,
48-
allow_merge_commit: false,
49-
delete_branch_on_merge: true,
50-
allow_update_branch: true,
51-
squash_merge_commit_title: 'PR_TITLE'
52-
})
53-
await updateGithubBranchProtection(data, {
54-
owner: data.prompts?.repoFromTmpl?.newRepoOwner || '',
55-
repo: data.prompts?.repoFromTmpl?.newRepoName || '',
56-
branch: 'master',
57-
required_status_checks: {
58-
strict: true,
59-
contexts: [
60-
'test',
61-
'check-commit-message',
62-
'validate-theme'
63-
]
64-
},
65-
enforce_admins: false,
66-
required_pull_request_reviews: {
67-
dismiss_stale_reviews: true,
68-
required_approving_review_count: 1,
69-
require_last_push_approval: true
70-
},
71-
restrictions: null,
72-
required_linear_history: true,
73-
required_conversation_resolution: true
74-
})
75-
if (data.prompts?.repoFromTmpl?.isSecrets) {
76-
await addRepositorySecrets(data)
77-
}
78-
await updateAndCommit(
79-
data.prompts?.repoFromTmpl?.newRepoOwner || '',
80-
data.prompts?.repoFromTmpl?.newRepoName || '',
81-
['package.json', 'theme/theme.json'],
82-
{
83-
name: data.prompts?.repoFromTmpl?.newRepoName,
84-
label: `${data.prompts?.repoFromTmpl?.newRepoLabel.charAt(0).toUpperCase()}${data.prompts?.repoFromTmpl?.newRepoLabel.slice(1)}`
85-
}
86-
)
138+
await initGithubRepoBasedOnTemplate(data)
139+
} else if (type === 'local-repo-based-on-template') {
140+
await initLocalRepoBasedOnTemplate(data)
87141
}
88142
}
89143

lib/cmd/init/prompts.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ async function initType () {
2525
{
2626
name: 'Local repo based on template',
2727
value: 'local-repo-based-on-template',
28-
description: 'Create a new local repo based on a GitHub hosted template',
29-
disabled: true
28+
description: 'Create a new local repo based on a GitHub hosted template'
3029
}
3130
]
3231
})
@@ -178,7 +177,7 @@ async function collectRepoSecrets () {
178177
}
179178

180179
/**
181-
* #### collect data for the new child theme
180+
* #### collect data for the new child theme for GitHub repo
182181
* @param {LOCALDATA} data - env variables
183182
* @returns undefined
184183
*/
@@ -193,7 +192,7 @@ async function githubRepoFromTemplatePrompts (data) {
193192
repoSecrets = await collectRepoSecrets()
194193
}
195194
data.prompts = {
196-
repoFromTmpl: {
195+
githubRepoFromTmpl: {
197196
newRepoOwner: repoOwner,
198197
newRepoName: `${projectName}-${childTheme}-${new Date().getFullYear()}`,
199198
newRepoLabel: `${projectName} theme`,
@@ -206,4 +205,22 @@ async function githubRepoFromTemplatePrompts (data) {
206205
}
207206
}
208207

209-
export { githubRepoFromTemplatePrompts, initType }
208+
/**
209+
* #### collect data for the new child theme for local repo
210+
* @param {LOCALDATA} data - env variables
211+
* @returns undefined
212+
*/
213+
async function localRepoFromTemplatePrompts (data) {
214+
const childTheme = await selectRepoTemplate()
215+
const projectName = await getProjectName()
216+
data.prompts = {
217+
localRepoFromTmpl: {
218+
newRepoName: `${projectName}-${childTheme}-${new Date().getFullYear()}`,
219+
newRepoLabel: `${projectName} theme`,
220+
templateRepoOwner: 'Resultify',
221+
templateRepoName: childTheme
222+
}
223+
}
224+
}
225+
226+
export { githubRepoFromTemplatePrompts, localRepoFromTemplatePrompts, initType }

lib/git/git.js

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -440,17 +440,41 @@ async function gitAdd (files) {
440440
}
441441
}
442442

443+
/**
444+
* #### git add files
445+
* @async
446+
* @memberof GIT
447+
* @param {Object} [options] - execa options
448+
* @returns undefined
449+
*/
450+
async function gitAddAll (options) {
451+
try {
452+
if (options) {
453+
await $(options)`git add .`
454+
} else {
455+
await $`git add .`
456+
}
457+
} catch (error) {
458+
console.error(error)
459+
}
460+
}
461+
443462
/**
444463
* #### git commit release
445464
* @async
446465
* @memberof GIT
447466
* @param {string} msg - commit message
467+
* @param {Object} [options] - execa options
448468
* @returns undefined
449469
*/
450-
async function gitCommit (msg) {
451-
const spinner = ora('Commit release').start()
470+
async function gitCommit (msg, options) {
471+
const spinner = ora(msg).start()
452472
try {
453-
await $`git commit -m ${msg}`
473+
if (options) {
474+
await $(options)`git commit -m ${msg}`
475+
} else {
476+
await $`git commit -m ${msg}`
477+
}
454478
spinner.succeed()
455479
} catch (error) {
456480
spinner.fail()
@@ -571,6 +595,43 @@ async function saveGitlog (data) {
571595
data.git.log = gitLogData
572596
}
573597

598+
/**
599+
* #### git clone repo
600+
* @async
601+
* @memberof GIT
602+
* @param {string} folder - folder name
603+
* @returns undefined
604+
*/
605+
async function gitCloneDepth1 (folder) {
606+
const spinner = ora('Clone repo').start()
607+
try {
608+
await $`git clone --depth 1 git@github.com:Resultify/nimbly-lite-child.git ${folder}`
609+
spinner.succeed()
610+
} catch (error) {
611+
spinner.fail()
612+
console.error(error)
613+
}
614+
}
615+
616+
/**
617+
* #### git init
618+
* @async
619+
* @memberof GIT
620+
* @param {Object} [options] - execa options
621+
* @returns undefined
622+
*/
623+
async function gitInit (options) {
624+
try {
625+
if (options) {
626+
await $(options)`git init`
627+
} else {
628+
await $`git init`
629+
}
630+
} catch (error) {
631+
console.error(error)
632+
}
633+
}
634+
574635
export {
575636
isRepo,
576637
isRoot,
@@ -585,5 +646,8 @@ export {
585646
gitPush,
586647
gitTag,
587648
gitPushTag,
588-
saveGitlog
649+
saveGitlog,
650+
gitCloneDepth1,
651+
gitInit,
652+
gitAddAll
589653
}

lib/github/repo.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ async function createGithubRepoWithTemplate (data) {
2121
const spinner = ora('Create new repository from template').start()
2222
try {
2323
// check if the repository already exists
24-
const repoExists = await isGithubRepoExists(data.prompts?.repoFromTmpl?.newRepoOwner || '', data.prompts?.repoFromTmpl?.newRepoName || '')
24+
const repoExists = await isGithubRepoExists(data.prompts?.githubRepoFromTmpl?.newRepoOwner || '', data.prompts?.githubRepoFromTmpl?.newRepoName || '')
2525
if (repoExists) {
2626
spinner.warn('Repository already exists.')
2727
process.exit(0)
@@ -31,16 +31,16 @@ async function createGithubRepoWithTemplate (data) {
3131
headers: {
3232
authorization: `token ${process.env.GITHUB_TOKEN}`
3333
},
34-
template_owner: data.prompts?.repoFromTmpl?.templateRepoOwner || '',
35-
template_repo: data.prompts?.repoFromTmpl?.templateRepoName || '',
36-
owner: data.prompts?.repoFromTmpl?.newRepoOwner || '',
37-
name: data.prompts?.repoFromTmpl?.newRepoName || '',
38-
description: data.prompts?.repoFromTmpl?.newRepoLabel,
34+
template_owner: data.prompts?.githubRepoFromTmpl?.templateRepoOwner || '',
35+
template_repo: data.prompts?.githubRepoFromTmpl?.templateRepoName || '',
36+
owner: data.prompts?.githubRepoFromTmpl?.newRepoOwner || '',
37+
name: data.prompts?.githubRepoFromTmpl?.newRepoName || '',
38+
description: data.prompts?.githubRepoFromTmpl?.newRepoLabel,
3939
include_all_branches: false,
40-
private: data.prompts?.repoFromTmpl?.isPrivate
40+
private: data.prompts?.githubRepoFromTmpl?.isPrivate
4141
})
42-
await waitForGithubRepo(data.prompts?.repoFromTmpl?.newRepoOwner || '', data.prompts?.repoFromTmpl?.newRepoName || '')
43-
await waitForGithubRepoNotEmpty(data.prompts?.repoFromTmpl?.newRepoOwner || '', data.prompts?.repoFromTmpl?.newRepoName || '')
42+
await waitForGithubRepo(data.prompts?.githubRepoFromTmpl?.newRepoOwner || '', data.prompts?.githubRepoFromTmpl?.newRepoName || '')
43+
await waitForGithubRepoNotEmpty(data.prompts?.githubRepoFromTmpl?.newRepoOwner || '', data.prompts?.githubRepoFromTmpl?.newRepoName || '')
4444
spinner.succeed()
4545
} catch (error) {
4646
spinner.fail()

0 commit comments

Comments
 (0)