imdone-core 1.45.1 → 1.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/adapters/file-gateway.js +22 -0
- package/lib/card.js +2 -0
- package/lib/config.js +12 -0
- package/lib/file.js +1 -0
- package/lib/migrate-config.js +19 -5
- package/lib/mixins/repo-fs-store.js +2 -1
- package/lib/plugins/archive-plugin.js +61 -0
- package/lib/plugins/plugin-manager.js +6 -2
- package/lib/project.js +50 -55
- package/lib/repository.js +11 -7
- package/package.json +1 -1
- package/cli.js +0 -225
- package/lib/cli/CliControler.js +0 -327
- package/lib/cli/MDStoryParser.js +0 -139
- package/lib/cli/ProjectConfigFactory.js +0 -114
- package/lib/cli/adapters/ApplicationContext.js +0 -6
- package/lib/cli/adapters/Errors.js +0 -8
- package/lib/cli/adapters/LogAdapter.js +0 -3
- package/lib/cli/adapters/StorageAdapter.js +0 -135
- package/lib/cli/domain/BacklogProject.js +0 -170
- package/lib/cli/domain/StoryProject.js +0 -67
- package/lib/cli/prompts/MarkdownPlanPrompt.js +0 -16
- package/lib/plugins/cli-backlog-plugin.js +0 -152
package/lib/cli/CliControler.js
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
const ApplicationContext = require('./adapters/ApplicationContext')
|
|
2
|
-
const {simpleGit} = require('simple-git')
|
|
3
|
-
const prompts = require('prompts')
|
|
4
|
-
const eol = require('eol')
|
|
5
|
-
const chalk = require('chalk')
|
|
6
|
-
const BacklogProject = require('./domain/BacklogProject')
|
|
7
|
-
const StoryProject = require('./domain/StoryProject')
|
|
8
|
-
const { createFileSystemProject } = require('../project-factory.js')
|
|
9
|
-
const commandExists = require('command-exists')
|
|
10
|
-
const git = simpleGit()
|
|
11
|
-
const _path = require('path')
|
|
12
|
-
const {
|
|
13
|
-
importMarkdown,
|
|
14
|
-
} = require('./MDStoryParser')
|
|
15
|
-
|
|
16
|
-
let consoleLog = () => {}
|
|
17
|
-
const applicationContext = () => {
|
|
18
|
-
return {
|
|
19
|
-
...ApplicationContext(),
|
|
20
|
-
log: consoleLog
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
configureProject,
|
|
26
|
-
defaultProjectPath,
|
|
27
|
-
projectExists,
|
|
28
|
-
} = require('./ProjectConfigFactory')
|
|
29
|
-
|
|
30
|
-
const {promptForMarkdownPlan} = require('./prompts/MarkdownPlanPrompt.js')
|
|
31
|
-
const {
|
|
32
|
-
TASK_ID,
|
|
33
|
-
UNGROUPED_TASKS
|
|
34
|
-
} = BacklogProject.constants
|
|
35
|
-
|
|
36
|
-
const replacer = (key, value) =>
|
|
37
|
-
value instanceof Object && !(value instanceof Array) ?
|
|
38
|
-
Object.keys(value)
|
|
39
|
-
.sort()
|
|
40
|
-
.reduce((sorted, key) => {
|
|
41
|
-
sorted[key] = value[key];
|
|
42
|
-
return sorted
|
|
43
|
-
}, {}) :
|
|
44
|
-
value;
|
|
45
|
-
|
|
46
|
-
module.exports = CliController = {}
|
|
47
|
-
|
|
48
|
-
CliController.exportProject = async function (log, verbose = false) {
|
|
49
|
-
const project = createFileSystemProject({path: process.env.PWD})
|
|
50
|
-
await project.init()
|
|
51
|
-
let jsonOutput = verbose
|
|
52
|
-
? project.toImdoneJSON()
|
|
53
|
-
: (() => {
|
|
54
|
-
const result = {}
|
|
55
|
-
const imdoneJSON = project.toImdoneJSON()
|
|
56
|
-
const lists = imdoneJSON.lists.map(list => {
|
|
57
|
-
list.tasks = list.tasks.map(task => {
|
|
58
|
-
const result = {
|
|
59
|
-
created: task.created,
|
|
60
|
-
completed: task.completed,
|
|
61
|
-
due: task.due,
|
|
62
|
-
path: _path.join(task.source.repoId, task.source.path),
|
|
63
|
-
meta: task.allMeta,
|
|
64
|
-
tags: task.allTags,
|
|
65
|
-
context: task.allContext,
|
|
66
|
-
line: task.line,
|
|
67
|
-
content: task.content,
|
|
68
|
-
interpretedContent: task.interpretedContent,
|
|
69
|
-
lastLine: task.lastLine,
|
|
70
|
-
list: task.list,
|
|
71
|
-
progress: task.progress,
|
|
72
|
-
order: task.order,
|
|
73
|
-
beforeText: task.beforeText,
|
|
74
|
-
props: task.props,
|
|
75
|
-
}
|
|
76
|
-
return result
|
|
77
|
-
})
|
|
78
|
-
return list
|
|
79
|
-
})
|
|
80
|
-
result.lists = lists
|
|
81
|
-
result.totals = imdoneJSON.totals
|
|
82
|
-
result.tags = imdoneJSON.tags
|
|
83
|
-
|
|
84
|
-
return result
|
|
85
|
-
})()
|
|
86
|
-
|
|
87
|
-
log(JSON.stringify(jsonOutput, replacer, 3))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
CliController.imdoneInit = async function () {
|
|
91
|
-
if (await projectExists(defaultProjectPath())) {
|
|
92
|
-
throw new Error('Imdone backlog is already initialized!')
|
|
93
|
-
}
|
|
94
|
-
const defaultBranch = await promptForDefaultBranch()
|
|
95
|
-
const remote = await promptForRemote()
|
|
96
|
-
await configureProject({defaultBranch, remote})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
CliController.openBoard = async function (log) {
|
|
100
|
-
const open = (await import('open')).default
|
|
101
|
-
try {
|
|
102
|
-
await commandExists('imdone')
|
|
103
|
-
const { storyProject, task } = await getCurrentTask()
|
|
104
|
-
const taskId = task && task.meta[TASK_ID] && task.meta[TASK_ID][0]
|
|
105
|
-
|
|
106
|
-
if (storyProject) {
|
|
107
|
-
const queryString = taskId ? `?filter=meta.${TASK_ID}="${taskId}"` : ''
|
|
108
|
-
open(`imdone://${storyProject.path}${queryString}`)
|
|
109
|
-
} else {
|
|
110
|
-
open(`imdone://${defaultProjectPath()}`)
|
|
111
|
-
}
|
|
112
|
-
} catch (e) {
|
|
113
|
-
console.error(e)
|
|
114
|
-
log(`\nimdone is not installed! Visit ${chalk.green('https://imdone.io')} to install imdone.`)
|
|
115
|
-
open(`https://imdone.io`)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
CliController.openTaskFile = async function (log) {
|
|
120
|
-
const open = (await import('open')).default
|
|
121
|
-
let { task } = await getCurrentTask()
|
|
122
|
-
if (!task) {
|
|
123
|
-
const storyId = await promptForStoryId()
|
|
124
|
-
const taskId = await promptForTaskId(storyId)
|
|
125
|
-
const storyProject = await StoryProject.createAndInit(defaultProjectPath(), null, storyId, applicationContext)
|
|
126
|
-
task = storyProject.getTask(taskId) || storyProject.getStory()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
open(task.fullPath)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// TODO: This should ask for a group to add a task to
|
|
134
|
-
CliController.addTask = async function ({content, storyId, group, log}) {
|
|
135
|
-
consoleLog = log
|
|
136
|
-
storyId = await determineStoryId(storyId)
|
|
137
|
-
const storyProject = await StoryProject.createAndInit(defaultProjectPath(), null, storyId, applicationContext)
|
|
138
|
-
|
|
139
|
-
const task = await storyProject.addTask({group, content})
|
|
140
|
-
|
|
141
|
-
log(`${TASK_ID}:"${getTaskId(task)}"`)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
CliController.listTasks = async function ({storyId, filter=null, json, log}) {
|
|
145
|
-
consoleLog = log
|
|
146
|
-
storyId = storyId || await determineStoryId(storyId)
|
|
147
|
-
|
|
148
|
-
const storyProject = await StoryProject.createAndInit(defaultProjectPath(), null, storyId, applicationContext)
|
|
149
|
-
|
|
150
|
-
const tasks = storyProject.getTasks(filter)
|
|
151
|
-
|
|
152
|
-
if (json) return log(
|
|
153
|
-
JSON.stringify({
|
|
154
|
-
name: storyProject.name,
|
|
155
|
-
path: storyProject.path,
|
|
156
|
-
tasks: storyProject.tasksToJson(tasks)
|
|
157
|
-
}, null, 2)
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
const story = storyProject.getStory()
|
|
161
|
-
const groupedTasks = getGroupedTasks(tasks)
|
|
162
|
-
logStory(log, storyProject, story)
|
|
163
|
-
logGroupedTasks(log, storyProject, groupedTasks)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
CliController.planStory = async function(markdown, storyId, log = console.log) {
|
|
167
|
-
consoleLog = log
|
|
168
|
-
if (storyId) {
|
|
169
|
-
storyId = await determineStoryId(storyId)
|
|
170
|
-
}
|
|
171
|
-
if (!markdown) {
|
|
172
|
-
markdown = await promptForMarkdownPlan()
|
|
173
|
-
}
|
|
174
|
-
return await importMarkdown({
|
|
175
|
-
projectPath: defaultProjectPath(),
|
|
176
|
-
existingStoryId: storyId,
|
|
177
|
-
markdown,
|
|
178
|
-
applicationContext
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
CliController.startTask = async function (taskId, log) {
|
|
183
|
-
consoleLog = log
|
|
184
|
-
if (!taskId) {
|
|
185
|
-
const storyId = await determineStoryId()
|
|
186
|
-
taskId = await promptForTaskId(storyId)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (!taskId) return log(chalk.bgRed('No tasks found!'))
|
|
190
|
-
const project = await BacklogProject.createAndInit(defaultProjectPath(), null, applicationContext)
|
|
191
|
-
await project.startTask(taskId)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
CliController.completeTask = async function (log) {
|
|
195
|
-
consoleLog = log
|
|
196
|
-
const {storyProject, task} = await getCurrentTask()
|
|
197
|
-
if (!task) return noTaskStarted(log)
|
|
198
|
-
try {
|
|
199
|
-
const task = await storyProject.completeTask()
|
|
200
|
-
log()
|
|
201
|
-
logTask(log, storyProject, task)
|
|
202
|
-
log(chalk.bgGreen('Remember to commit and push!'))
|
|
203
|
-
} catch (e) {
|
|
204
|
-
log(chalk.bgRed(e.message))
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function noTaskStarted(log) {
|
|
209
|
-
log('\n', chalk.bgRed('No task started!'))
|
|
210
|
-
}
|
|
211
|
-
function getTaskId(task) {
|
|
212
|
-
const taskIdMeta = task.meta[TASK_ID]
|
|
213
|
-
return taskIdMeta ? taskIdMeta[0] : null
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async function getCurrentTask() {
|
|
217
|
-
const backlogProject = await BacklogProject.createAndInit(defaultProjectPath(), null, applicationContext)
|
|
218
|
-
const storyId = backlogProject.getCurrentStoryId()
|
|
219
|
-
if (!storyId) return { backlogProject }
|
|
220
|
-
const storyProject = await StoryProject.createAndInit(defaultProjectPath(), null, storyId, applicationContext)
|
|
221
|
-
const task = await storyProject.getCurrentTask()
|
|
222
|
-
return { backlogProject, storyProject, storyId, task }
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async function determineStoryId(storyId) {
|
|
226
|
-
const project = await BacklogProject.createAndInit(defaultProjectPath(), null, applicationContext)
|
|
227
|
-
storyId = storyId || project.getCurrentStoryId()
|
|
228
|
-
storyId = await promptForStoryId(storyId)
|
|
229
|
-
return storyId
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async function promptForStoryId(lastStoryId) {
|
|
233
|
-
const backlogProject = new BacklogProject(defaultProjectPath(), null, applicationContext)
|
|
234
|
-
await backlogProject.init()
|
|
235
|
-
|
|
236
|
-
const storyIds = backlogProject.getStoryIds().filter(({list}) => list !== backlogProject.config.getDoneList())
|
|
237
|
-
const choices = storyIds.map(({storyId, text}) => ({ title: `${text} [ ${storyId} ]`, value: storyId }))
|
|
238
|
-
let initial = storyIds.findIndex(({storyId}) => storyId === lastStoryId)
|
|
239
|
-
if (initial < 0) initial = 0
|
|
240
|
-
const result = await prompts({
|
|
241
|
-
type: 'select',
|
|
242
|
-
name: 'storyId',
|
|
243
|
-
message: 'Select a story',
|
|
244
|
-
choices,
|
|
245
|
-
initial
|
|
246
|
-
})
|
|
247
|
-
return result.storyId
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async function promptForTaskId(storyId) {
|
|
251
|
-
const storyProject = await StoryProject.createAndInit(defaultProjectPath(), null, storyId, applicationContext)
|
|
252
|
-
const tasks = storyProject.getTasks(`list != ${storyProject.config.getDoneList()}`)
|
|
253
|
-
const choices = tasks.map(({text, meta}) => ({ title: `${text} [ ${meta[TASK_ID]} ]`, value: meta[TASK_ID] }))
|
|
254
|
-
const result = await prompts({
|
|
255
|
-
type: 'select',
|
|
256
|
-
name: 'taskId',
|
|
257
|
-
message: 'Select a task',
|
|
258
|
-
choices
|
|
259
|
-
})
|
|
260
|
-
return result.taskId
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async function promptForDefaultBranch() {
|
|
264
|
-
const branches = (await git.branchLocal()).all
|
|
265
|
-
const initial = branches.includes('master') ? 'master' : 'main'
|
|
266
|
-
const result = await prompts({
|
|
267
|
-
type: 'text',
|
|
268
|
-
name: 'defaultBranch',
|
|
269
|
-
message: 'Enter the name of the default branch',
|
|
270
|
-
initial
|
|
271
|
-
})
|
|
272
|
-
return result.defaultBranch
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
async function promptForRemote() {
|
|
276
|
-
const remotes = (await git.getRemotes(true)).map(({name}) => ({title: name, value: name}))
|
|
277
|
-
if (remotes.length === 1) return remotes[0].value
|
|
278
|
-
if (remotes.length === 0) return
|
|
279
|
-
|
|
280
|
-
const result = await prompts({
|
|
281
|
-
type: 'select',
|
|
282
|
-
name: 'remote',
|
|
283
|
-
message: 'Select a remote',
|
|
284
|
-
choices: remotes
|
|
285
|
-
})
|
|
286
|
-
return result.remote
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function getGroupedTasks(tasks) {
|
|
290
|
-
const groupedTasks = {}
|
|
291
|
-
tasks.forEach((task) => {
|
|
292
|
-
const group = task.meta.group
|
|
293
|
-
if (group) {
|
|
294
|
-
if (!groupedTasks[group]) groupedTasks[group] = []
|
|
295
|
-
groupedTasks[group].push(task)
|
|
296
|
-
}
|
|
297
|
-
})
|
|
298
|
-
return groupedTasks
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function logGroupedTasks(log, project, groupedTasks) {
|
|
302
|
-
log('\n## Tasks\n')
|
|
303
|
-
Object.keys(groupedTasks).forEach((group) => {
|
|
304
|
-
if (group !== UNGROUPED_TASKS) {
|
|
305
|
-
log('')
|
|
306
|
-
log(`### ${group}`)
|
|
307
|
-
log('')
|
|
308
|
-
}
|
|
309
|
-
logTasks(log, project, groupedTasks[group])
|
|
310
|
-
})
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function logTasks(log, project, tasks) {
|
|
314
|
-
tasks.forEach(task => logTask(log, project, task))
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function logStory(log, project, story) {
|
|
318
|
-
log(`# ${project.name}`)
|
|
319
|
-
log('')
|
|
320
|
-
log(`${story.text}`)
|
|
321
|
-
log(story.description.filter(ln => !/^<!--(.*?)-->$/.test(ln)).join(String(eol.lf)))
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function logTask(log, project, task) {
|
|
325
|
-
const doneMark = (task.list === project.config.getDoneList()) ? 'x' : ' '
|
|
326
|
-
log(`- [${doneMark}] ${task.text} <!-- task-id:"${getTaskId(task)}" -->`)
|
|
327
|
-
}
|
package/lib/cli/MDStoryParser.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
const { parseMD } = require('../tools')
|
|
2
|
-
const StoryProject = require('./domain/StoryProject')
|
|
3
|
-
const BacklogProject = require('./domain/BacklogProject')
|
|
4
|
-
const {
|
|
5
|
-
UNGROUPED_TASKS,
|
|
6
|
-
TODO,
|
|
7
|
-
DONE,
|
|
8
|
-
ORDER
|
|
9
|
-
} = BacklogProject.constants
|
|
10
|
-
const ApplicationContext = require('./adapters/ApplicationContext')
|
|
11
|
-
|
|
12
|
-
function isOpen(token, type, tag = token.tag) {
|
|
13
|
-
return token.type === type + '_open' && token.tag === tag
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isClose(token, type, tag = token.tag) {
|
|
17
|
-
return token.type === type + '_close' && token.tag === tag
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getHeadingLevel(tag) {
|
|
21
|
-
return parseInt(tag.substring(1), 10)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function parse(markdown) {
|
|
25
|
-
const heading = 'heading'
|
|
26
|
-
const paragraph = 'paragraph'
|
|
27
|
-
const inline = 'inline'
|
|
28
|
-
const bulletList = 'bullet_list'
|
|
29
|
-
const listItem = 'list_item'
|
|
30
|
-
const textType = 'text'
|
|
31
|
-
let storyText, description = [], groupName = '', tasks = []
|
|
32
|
-
let inHeading = false
|
|
33
|
-
let inParagraph = false
|
|
34
|
-
let inBulletList = false
|
|
35
|
-
let inListItem = false
|
|
36
|
-
let inTasks = false
|
|
37
|
-
let headingLevel = 0
|
|
38
|
-
const ast = parseMD(markdown)
|
|
39
|
-
|
|
40
|
-
ast.forEach((token) => {
|
|
41
|
-
const { type, tag, markup, content, info, children } = token
|
|
42
|
-
if (isOpen(token, heading)) {
|
|
43
|
-
inHeading = true
|
|
44
|
-
headingLevel = getHeadingLevel(tag)
|
|
45
|
-
} else if (isClose(token, heading)) {
|
|
46
|
-
inHeading = false
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (isOpen(token, bulletList)) {
|
|
50
|
-
inBulletList = true
|
|
51
|
-
} else if (isClose(token, bulletList)) {
|
|
52
|
-
inBulletList = false
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (isOpen(token, listItem)) {
|
|
56
|
-
inListItem = true
|
|
57
|
-
} else if (isClose(token, listItem)) {
|
|
58
|
-
inListItem = false
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (isOpen(token, paragraph)) {
|
|
62
|
-
inParagraph = true
|
|
63
|
-
} else if (isClose(token, paragraph)) {
|
|
64
|
-
inParagraph = false
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (inHeading && type == inline) {
|
|
68
|
-
groupName = content
|
|
69
|
-
if (headingLevel == 1) {
|
|
70
|
-
storyText = groupName
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (groupName.toLowerCase().endsWith('tasks') && headingLevel == 2) {
|
|
75
|
-
inTasks = true
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (inTasks && inBulletList && inListItem && inParagraph && type == inline) {
|
|
79
|
-
const group = headingLevel > 2 ? groupName : UNGROUPED_TASKS
|
|
80
|
-
const done = /^\[x\]\s/.test(content)
|
|
81
|
-
const description = children
|
|
82
|
-
.filter(child => child.type === textType)
|
|
83
|
-
.map(child => child.content)
|
|
84
|
-
const text = description.join('\n')
|
|
85
|
-
tasks.push({ group, text, done })
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
if (storyText) {
|
|
90
|
-
const result = new RegExp(`# ${storyText}\n(.*)## Tasks`, 'gms').exec(markdown)
|
|
91
|
-
description = result && result.length > 1 ? result[1].trim() : ''
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return { storyText, description, tasks }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async function importMarkdown({projectPath, existingStoryId, markdown, applicationContext = ApplicationContext}) {
|
|
98
|
-
const { log } = applicationContext()
|
|
99
|
-
|
|
100
|
-
const backlogProject = await BacklogProject.createAndInit(projectPath, null, applicationContext)
|
|
101
|
-
|
|
102
|
-
let { storyText, description, tasks } = parse(markdown)
|
|
103
|
-
|
|
104
|
-
const storyId = existingStoryId || backlogProject.toStoryId(storyText)
|
|
105
|
-
|
|
106
|
-
log(`Importing story ${storyId}\n${description}`)
|
|
107
|
-
|
|
108
|
-
await backlogProject.storageAdapter.initStoryPlan(storyId)
|
|
109
|
-
|
|
110
|
-
if (!existingStoryId) await backlogProject.addStory(`${storyText}\n${description}`)
|
|
111
|
-
|
|
112
|
-
const storyProject = await StoryProject.createAndInit(projectPath, null, storyId, applicationContext)
|
|
113
|
-
|
|
114
|
-
await addStoryTasks(storyProject, tasks)
|
|
115
|
-
|
|
116
|
-
await storyProject.init()
|
|
117
|
-
|
|
118
|
-
await backlogProject.storageAdapter.saveStoryPlan(storyId)
|
|
119
|
-
|
|
120
|
-
return storyProject
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function addStoryTasks(storyProject, tasks) {
|
|
124
|
-
tasks.forEach(async (task, i) => {
|
|
125
|
-
const order = (i + 1) * (10)
|
|
126
|
-
const list = task.done ? DONE : TODO
|
|
127
|
-
const meta = [
|
|
128
|
-
{ key: 'group', value: task.group },
|
|
129
|
-
{ key: ORDER, value: order }
|
|
130
|
-
]
|
|
131
|
-
await storyProject.addTaskToFile({list, content: task.text, meta})
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
module.exports = {
|
|
136
|
-
UNGROUPED_TASKS,
|
|
137
|
-
parse,
|
|
138
|
-
importMarkdown
|
|
139
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
const DefaultApplicationContext = require('./adapters/ApplicationContext')
|
|
2
|
-
const { loadYAML, dumpYAML } = require('../adapters/yaml')
|
|
3
|
-
const Config = require('../config')
|
|
4
|
-
const { readFile, mkdir, cp, rm, writeFile, access } = require('fs').promises
|
|
5
|
-
const { resolve } = path = require('path')
|
|
6
|
-
const BacklogProject = require('./domain/BacklogProject')
|
|
7
|
-
const StoryProject = require('./domain/StoryProject')
|
|
8
|
-
|
|
9
|
-
const DEFAULT_PROJECT_DIR = 'backlog'
|
|
10
|
-
const STORIES_DIR = 'stories'
|
|
11
|
-
const TASKS_DIR = 'tasks'
|
|
12
|
-
const CONFIG_PATH = path.join(__dirname, '..', '..', 'devops', 'imdone')
|
|
13
|
-
const BACKLOG_CONFIG_PATH = path.join(CONFIG_PATH, 'backlog-config.yml')
|
|
14
|
-
const STORY_CONFIG_PATH = path.join(CONFIG_PATH, 'story-config.yml')
|
|
15
|
-
const BACKLOG_IGNORE_PATH = path.join(CONFIG_PATH, 'backlog.imdoneignore')
|
|
16
|
-
|
|
17
|
-
const defaultProjectPath = (baseDir = process.env.PWD) => path.join(baseDir, DEFAULT_PROJECT_DIR)
|
|
18
|
-
|
|
19
|
-
const getProjectConfigPath = (projectPath = defaultProjectPath()) => path.join(projectPath, '.imdone', 'config.yml')
|
|
20
|
-
|
|
21
|
-
const projectExists = async (
|
|
22
|
-
projectPath = defaultProjectPath()
|
|
23
|
-
) => {
|
|
24
|
-
const projectConfigPath = getProjectConfigPath(projectPath)
|
|
25
|
-
try {
|
|
26
|
-
await access(projectConfigPath)
|
|
27
|
-
return projectConfigPath
|
|
28
|
-
} catch (e) {
|
|
29
|
-
return false
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const configureProject = async ({
|
|
34
|
-
projectPath = defaultProjectPath(),
|
|
35
|
-
configPath = BACKLOG_CONFIG_PATH,
|
|
36
|
-
tasksDir = STORIES_DIR,
|
|
37
|
-
defaultBranch,
|
|
38
|
-
remote,
|
|
39
|
-
name
|
|
40
|
-
}) => {
|
|
41
|
-
projectPath = resolve(projectPath)
|
|
42
|
-
configPath = resolve(configPath)
|
|
43
|
-
|
|
44
|
-
if (await projectExists(projectPath)) {
|
|
45
|
-
return { projectPath, config: await newConfigFromFile(projectConfigPath)}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const projectConfigPath = getProjectConfigPath(projectPath)
|
|
49
|
-
|
|
50
|
-
await mkdir(path.join(projectPath, tasksDir), {recursive: true})
|
|
51
|
-
await mkdir(path.join(projectPath, '.imdone'), { recursive: true })
|
|
52
|
-
|
|
53
|
-
const config = await newConfigFromFile(configPath)
|
|
54
|
-
const parentDir = path.basename(path.dirname(projectPath))
|
|
55
|
-
config.name = name || `${parentDir} ${DEFAULT_PROJECT_DIR}`
|
|
56
|
-
config.journalPath = tasksDir
|
|
57
|
-
const pluginConfig = config.settings.plugins.CliBacklogPlugin
|
|
58
|
-
config.settings.plugins.CliBacklogPlugin = {...pluginConfig, defaultBranch, remote}
|
|
59
|
-
|
|
60
|
-
await writeFile(projectConfigPath, dumpYAML(config))
|
|
61
|
-
return { projectPath, config }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const newBacklogProject = async ({
|
|
65
|
-
projectPath = defaultProjectPath(),
|
|
66
|
-
configPath = BACKLOG_CONFIG_PATH,
|
|
67
|
-
tasksDir = STORIES_DIR,
|
|
68
|
-
ApplicationContext = DefaultApplicationContext
|
|
69
|
-
}) => {
|
|
70
|
-
try {
|
|
71
|
-
await cp(BACKLOG_IGNORE_PATH, path.join(projectPath, '.imdoneignore'))
|
|
72
|
-
} catch (e) {
|
|
73
|
-
logFsError(ApplicationContext, e)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const { config } = projectConfig = await configureProject({projectPath, configPath, tasksDir})
|
|
77
|
-
return new BacklogProject(projectConfig.projectPath, config, ApplicationContext)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const newStoryProject = async ({
|
|
81
|
-
projectPath = defaultProjectPath(),
|
|
82
|
-
configPath = STORY_CONFIG_PATH,
|
|
83
|
-
tasksDir = TASKS_DIR,
|
|
84
|
-
storyId,
|
|
85
|
-
ApplicationContext = DefaultApplicationContext
|
|
86
|
-
}) => {
|
|
87
|
-
const storyProjectPath = BacklogProject.getStoryProjectPath(projectPath, storyId)
|
|
88
|
-
try {
|
|
89
|
-
await rm(storyProjectPath, { recursive: true })
|
|
90
|
-
await mkdir(storyProjectPath, { recursive: true })
|
|
91
|
-
} catch (e) {
|
|
92
|
-
logFsError(ApplicationContext, e)
|
|
93
|
-
}
|
|
94
|
-
const name = `${storyId} ${tasksDir}`
|
|
95
|
-
const { config } = projectConfig = await configureProject({projectPath: storyProjectPath, configPath, tasksDir, name})
|
|
96
|
-
return new StoryProject(projectPath, config, storyId, ApplicationContext)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function newConfigFromFile(configPath) {
|
|
100
|
-
const config = await readFile(configPath, 'utf8')
|
|
101
|
-
return new Config(loadYAML(config))
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function logFsError(ApplicationContext, e) {
|
|
105
|
-
ApplicationContext().log(`${e.code}: ${e.path}`)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
module.exports = {
|
|
109
|
-
projectExists,
|
|
110
|
-
configureProject,
|
|
111
|
-
newBacklogProject,
|
|
112
|
-
newStoryProject,
|
|
113
|
-
defaultProjectPath
|
|
114
|
-
}
|