Reworked CLI command line interface.
CLI interface options make more sense, are parsed in a more organized fashion, and the interface is more informative regarding its actions.
This commit is contained in:
parent
e99b65fb16
commit
b04655a428
@ -3,8 +3,8 @@ build.dir=build
|
|||||||
src.dir=src
|
src.dir=src
|
||||||
lib.shared.dir=../shared-libs
|
lib.shared.dir=../shared-libs
|
||||||
test.dir=test
|
test.dir=test
|
||||||
build.number=4
|
build.number=1
|
||||||
expected.application.version=2.5.1
|
expected.application.version=2.6.0
|
||||||
lib.dir=lib
|
lib.dir=lib
|
||||||
release.dir=release
|
release.dir=release
|
||||||
release.jar=pit-${application.version}.jar
|
release.jar=pit-${application.version}.jar
|
||||||
|
BIN
libpit/release/pit-2.6.0.jar
Normal file
BIN
libpit/release/pit-2.6.0.jar
Normal file
Binary file not shown.
BIN
pit-cli/lib/pit-2.6.0.jar
Normal file
BIN
pit-cli/lib/pit-2.6.0.jar
Normal file
Binary file not shown.
@ -1,9 +1,9 @@
|
|||||||
#Sat Feb 27 03:01:15 CST 2010
|
#Tue, 09 Aug 2011 17:21:28 -0500
|
||||||
build.dir=build
|
build.dir=build
|
||||||
src.dir=src
|
src.dir=src
|
||||||
build.jar=pit-cli-${application.version}.${build.number}.jar
|
build.jar=pit-cli-${application.version}.${build.number}.jar
|
||||||
build.number=5
|
build.number=7
|
||||||
expected.application.version=2.5.1
|
expected.application.version=2.6.0
|
||||||
lib.dir=lib
|
lib.dir=lib
|
||||||
release.dir=release
|
release.dir=release
|
||||||
release.jar=pit-cli-${application.version}.jar
|
release.jar=pit-cli-${application.version}.jar
|
||||||
|
Binary file not shown.
BIN
pit-cli/release/pit-cli-2.6.0.jar
Normal file
BIN
pit-cli/release/pit-cli-2.6.0.jar
Normal file
Binary file not shown.
@ -5,6 +5,8 @@ import com.jdbernard.pit.file.*
|
|||||||
import static java.lang.Math.max
|
import static java.lang.Math.max
|
||||||
import static java.lang.Math.min
|
import static java.lang.Math.min
|
||||||
|
|
||||||
|
// -------- command-line interface specification -------- //
|
||||||
|
|
||||||
def cli = new CliBuilder(usage: 'pit-cli [options]')
|
def cli = new CliBuilder(usage: 'pit-cli [options]')
|
||||||
cli.h(longOpt: 'help', 'Show help information.')
|
cli.h(longOpt: 'help', 'Show help information.')
|
||||||
cli.v(longOpt: 'verbose', 'Show verbose task information')
|
cli.v(longOpt: 'verbose', 'Show verbose task information')
|
||||||
@ -14,8 +16,8 @@ cli.i(argName: 'id', longOpt: 'id', args: 1,
|
|||||||
'Filter issues by id. Accepts a comma-delimited list.')
|
'Filter issues by id. Accepts a comma-delimited list.')
|
||||||
cli.c(argName: 'category', longOpt: 'category', args: 1,
|
cli.c(argName: 'category', longOpt: 'category', args: 1,
|
||||||
'Filter issues by category (bug, feature, task). Accepts a '
|
'Filter issues by category (bug, feature, task). Accepts a '
|
||||||
+ 'comma-delimited list.')
|
+ 'comma-delimited list. By default all categories are selected.')
|
||||||
cli.t(argName: 'status', longOpt: 'status', args: 1,
|
cli.s(argName: 'status', longOpt: 'status', args: 1,
|
||||||
'Filter issues by status (new, reassigned, rejected, resolved, ' +
|
'Filter issues by status (new, reassigned, rejected, resolved, ' +
|
||||||
'validation_required)')
|
'validation_required)')
|
||||||
cli.p(argName: 'priority', longOpt: 'priority', args: 1,
|
cli.p(argName: 'priority', longOpt: 'priority', args: 1,
|
||||||
@ -24,152 +26,225 @@ cli.p(argName: 'priority', longOpt: 'priority', args: 1,
|
|||||||
cli.r(argName: 'project', longOpt: 'project', args: 1,
|
cli.r(argName: 'project', longOpt: 'project', args: 1,
|
||||||
'Filter issues by project (relative to the current directory). Accepts a '
|
'Filter issues by project (relative to the current directory). Accepts a '
|
||||||
+ 'comma-delimited list.')
|
+ 'comma-delimited list.')
|
||||||
cli.s(longOpt: 'show-subprojects',
|
/*cli.s(longOpt: 'show-subprojects',
|
||||||
'Include sup projects in listing (default behaviour)')
|
'Include sup projects in listing (default behaviour)')
|
||||||
cli.S(longOpt: 'no-subprojects', 'Do not list subprojects.')
|
cli.S(longOpt: 'no-subprojects', 'Do not list subprojects.')*/ // TODO: figure out better flags for these options.
|
||||||
cli.P(argName: 'new-priority', longOpt: 'set-priority', args: 1,
|
cli.P(argName: 'new-priority', longOpt: 'set-priority', args: 1,
|
||||||
required: false, 'Modify the priority of the selected issues.')
|
required: false, 'Modify the priority of the selected issues.')
|
||||||
cli.C(argName: 'new-category', longOpt: 'set-category', args: 1,
|
cli.C(argName: 'new-category', longOpt: 'set-category', args: 1,
|
||||||
required: false, 'Modify the category of the selected issues.')
|
required: false, 'Modify the category of the selected issues.')
|
||||||
cli.T(argName: 'new-status', longOpt: 'set-status', args: 1,
|
cli.S(argName: 'new-status', longOpt: 'set-status', args: 1,
|
||||||
required: false, 'Modify the status of the selected issues.')
|
required: false, 'Modify the status of the selected issues.')
|
||||||
cli.n(longOpt: 'new-issue', 'Create a new issue.')
|
cli.n(longOpt: 'new-issue', 'Create a new issue.')
|
||||||
cli.d(longOpt: 'dir', argName: 'dir', args: 1, required: false,
|
cli.d(longOpt: 'dir', argName: 'dir', args: 1, required: false,
|
||||||
'Use <dir> as the base directory (defaults to current directory).')
|
'Use <dir> as the base directory (defaults to current directory).')
|
||||||
|
|
||||||
|
// -------- parse CLI options -------- //
|
||||||
def opts = cli.parse(args)
|
def opts = cli.parse(args)
|
||||||
def issuedb = [:]
|
def issuedb = [:]
|
||||||
def workingDir = new File('.')
|
def workingDir = new File('.')
|
||||||
|
|
||||||
if (!opts) System.exit(1) // better solution?
|
// defaults for the issue filter/selector
|
||||||
|
def selectOpts = [
|
||||||
|
categories: ['bug', 'feature', 'task'],
|
||||||
|
status: ['new', 'validation_required'],
|
||||||
|
priority: 9,
|
||||||
|
projects: [],
|
||||||
|
ids: [],
|
||||||
|
acceptProjects: true]
|
||||||
|
|
||||||
if (opts.h) cli.usage()
|
// defaults for changing properties of issue(s)
|
||||||
|
def assignOpts = [
|
||||||
|
category: Category.TASK,
|
||||||
|
status: Status.NEW,
|
||||||
|
priority: 5,
|
||||||
|
text: "New issue."]
|
||||||
|
|
||||||
def categories = ['bug','feature','task']
|
if (!opts) opts.l = true; // default to 'list'
|
||||||
if (opts.c) categories = opts.c.split(/[,\s]/)
|
|
||||||
categories = categories.collect { Category.toCategory(it) }
|
|
||||||
|
|
||||||
def statusList = ['new', 'validation_required']
|
if (opts.h) {
|
||||||
if (opts.t) statusList = opts.t.split(/[,\s]/)
|
cli.usage()
|
||||||
statusList = statusList.collect { Status.toStatus(it) }
|
System.exit(0) }
|
||||||
|
|
||||||
|
// read the category filter designation(s)
|
||||||
|
if (opts.c) {
|
||||||
|
if (opts.c =~ /all/) {} // no-op, same as defaults
|
||||||
|
else { selectOpts.categories = opts.c.split(/[,\s]/) } }
|
||||||
|
|
||||||
|
// parse the categories names into Category objects
|
||||||
|
try { selectOpts.categories =
|
||||||
|
selectOpts.categories.collect { Category.toCategory(it) } }
|
||||||
|
catch (Exception e) {
|
||||||
|
println "Invalid category option: '-c ${e.localizedMessage}'."
|
||||||
|
println "Valid options are: \n${Category.values().join(', ')}"
|
||||||
|
println " (abbreviations are accepted)."
|
||||||
|
System.exit(1) }
|
||||||
|
|
||||||
|
// read the status filter designation(s)
|
||||||
|
if (opts.s) {
|
||||||
|
// -s all
|
||||||
|
if (opts.s =~ /all/) selectOpts.status = ['new', 'reassigned', 'rejected',
|
||||||
|
'resolved', 'validation_required']
|
||||||
|
// is <list>
|
||||||
|
else selectOpts.status = opts.s.split(/[,\s]/) }
|
||||||
|
|
||||||
|
// parse the statuses into Status objects
|
||||||
|
try { selectOpts.status =
|
||||||
|
selectOpts.status.collect { Status.toStatus(it) } }
|
||||||
|
catch (Exception e) {
|
||||||
|
println "Invalid status option: '-s ${e.localizedMessage}'."
|
||||||
|
println "Valid options are: \b${Status.values().join(', ')}"
|
||||||
|
println " (abbreviations are accepted.)"
|
||||||
|
System.exit(1) }
|
||||||
|
|
||||||
|
// read and parse the priority filter
|
||||||
|
if (opts.p) try {
|
||||||
|
selectOpts.priority = opts.p.toInteger() }
|
||||||
|
catch (NumberFormatException nfe) {
|
||||||
|
println "Not a valid priority value: '-p ${opts.p}'."
|
||||||
|
println "Valid values are: 0-9"
|
||||||
|
System.exit(1) }
|
||||||
|
|
||||||
|
// read and parse the projects filter
|
||||||
|
if (opts.r) { selectOpts.projects =
|
||||||
|
opts.r.toLowerCase().split(/[,\s]/).asType(List.class) }
|
||||||
|
|
||||||
|
// read and parse the ids filter
|
||||||
|
if (opts.i) { selectOpts.ids = opts.i.split(/[,\s]/).asType(List.class) }
|
||||||
|
|
||||||
|
// TODO: accept projects value from input
|
||||||
|
|
||||||
|
// read and parse the category to assign
|
||||||
|
if (opts.C) try { assignOpts.category = Category.toCategory(opts.C) }
|
||||||
|
catch (Exception e) {
|
||||||
|
println "Invalid category option: '-C ${e.localizedMessage}'."
|
||||||
|
println "Valid categories are: \n${Category.values().join(', ')}"
|
||||||
|
println " (abbreviations are accepted)."
|
||||||
|
System.exit(1) }
|
||||||
|
|
||||||
|
// read and parse the status to assign
|
||||||
|
if (opts.S) try { assignOpts.status = Status.toStatus(opts.S) }
|
||||||
|
catch (Exception e) {
|
||||||
|
println "Invalid status option: '-S ${e.localizedMessage}'."
|
||||||
|
println "Valid stasus options are: \n{Status.values().join(', ')}"
|
||||||
|
println " (abbreviations are accepted)."
|
||||||
|
System.exit(1) }
|
||||||
|
|
||||||
|
// read and parse the priority to assign
|
||||||
|
if (opts.P) try {assignOpts.priority = opts.P.toInteger() }
|
||||||
|
catch (NumberFormatException nfe) {
|
||||||
|
println "Not a valid priority value: '-P ${opts.P}'."
|
||||||
|
println "Valid values are: 0-9"
|
||||||
|
System.exit(1) }
|
||||||
|
|
||||||
|
// look for assignment text
|
||||||
|
if (opts.getArgs().length > 0) {
|
||||||
|
assignOpts.text = opts.getArgs()[0] }
|
||||||
|
|
||||||
|
// set the project working directory
|
||||||
if (opts.d) {
|
if (opts.d) {
|
||||||
workingDir = new File(opts.d.trim())
|
workingDir = new File(opts.d.trim())
|
||||||
if (!workingDir.exists()) {
|
if (!workingDir.exists()) {
|
||||||
println "Directory '${workingDir}' does not exist."
|
println "Directory '${workingDir}' does not exist."
|
||||||
return -1
|
return -1 } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def EOL = System.getProperty('line.separator')
|
def EOL = System.getProperty('line.separator')
|
||||||
|
|
||||||
// build issue list
|
// build issue list
|
||||||
issuedb = new FileProject(workingDir)
|
issuedb = new FileProject(workingDir)
|
||||||
|
|
||||||
// build filter from options
|
// build filter from options
|
||||||
def filter = new Filter('categories': categories,
|
def filter = new Filter(selectOpts)
|
||||||
'status': statusList,
|
|
||||||
'priority': (opts.p ? opts.p.toInteger() : 9),
|
|
||||||
'projects': (opts.r ? opts.r.toLowerCase().split(/[,\s]/).asType(List.class) : []),
|
|
||||||
'ids': (opts.i ? opts.i.split(/[,\s]/).asType(List.class) : []),
|
|
||||||
'acceptProjects': (opts.s || !opts.S))
|
|
||||||
|
|
||||||
// list first
|
// list first
|
||||||
if (opts.l) {
|
if (opts.l) {
|
||||||
|
|
||||||
|
// local function (closure) to print a single issue
|
||||||
def printIssue = { issue, offset ->
|
def printIssue = { issue, offset ->
|
||||||
println "${offset}${issue}"
|
println "${offset}${issue}"
|
||||||
if (opts.v) {
|
if (opts.v) {
|
||||||
println ""
|
println ""
|
||||||
issue.text.eachLine { println "${offset} ${it}" }
|
issue.text.eachLine { println "${offset} ${it}" }
|
||||||
println ""
|
println "" } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// local function (closure) to print a project and all visible subprojects
|
||||||
def printProject
|
def printProject
|
||||||
printProject = { project, offset ->
|
printProject = { project, offset ->
|
||||||
println "\n${offset}${project.name}"
|
println "\n${offset}${project.name}"
|
||||||
println "${offset}${'-'.multiply(project.name.length())}"
|
println "${offset}${'-'.multiply(project.name.length())}"
|
||||||
project.eachIssue(filter) { printIssue(it, offset) }
|
project.eachIssue(filter) { printIssue(it, offset) }
|
||||||
project.eachProject(filter) { printProject(it, offset + " ") }
|
project.eachProject(filter) { printProject(it, offset + " ") } }
|
||||||
}
|
|
||||||
|
// print all the issues in the root of this db
|
||||||
issuedb.eachIssue(filter) { printIssue(it, "") }
|
issuedb.eachIssue(filter) { printIssue(it, "") }
|
||||||
issuedb.eachProject(filter) { printProject(it, "") }
|
// print all projects
|
||||||
}
|
issuedb.eachProject(filter) { printProject(it, "") } }
|
||||||
|
|
||||||
// change priority second
|
// new issues second
|
||||||
else if (opts.P) {
|
|
||||||
def priority
|
|
||||||
try { priority = max(0, min(9, opts.P.toInteger())) }
|
|
||||||
catch (e) { println "Invalid priority: ${opts.P}"; return 1 }
|
|
||||||
|
|
||||||
walkProject(issuedb, filter) { it.priority = priority }
|
|
||||||
}
|
|
||||||
// change category third
|
|
||||||
else if (opts.C) {
|
|
||||||
def cat
|
|
||||||
try { cat = Category.toCategory(opts.C) }
|
|
||||||
catch (e) { println "Invalid category: ${opts.C}"; return 1 }
|
|
||||||
|
|
||||||
walkProject(issuedb, filter) { it.category = cat }
|
|
||||||
}
|
|
||||||
// change status fourth
|
|
||||||
else if (opts.T) {
|
|
||||||
def status
|
|
||||||
try { status = Status.toStatus(opts.T) }
|
|
||||||
catch (e) { println "Invalid status: ${opts.T}"; return 1 }
|
|
||||||
|
|
||||||
walkProject(issuedb, filter) { it.status = status }
|
|
||||||
}
|
|
||||||
// new entry last
|
|
||||||
else if (opts.n) {
|
else if (opts.n) {
|
||||||
def cat, priority
|
def cat, priority
|
||||||
String text = ""
|
String text = ""
|
||||||
Issue issue
|
Issue issue
|
||||||
def sin = System.in.newReader()
|
def sin = System.in.newReader()
|
||||||
|
|
||||||
while(true) {
|
if (opts.C) { cat = assignOpts.category }
|
||||||
try {
|
else while(true) {
|
||||||
print "Category (bug, feature, task, closed): "
|
try {
|
||||||
cat = Category.toCategory(sin.readLine())
|
print "Category (bug, feature, task, closed): "
|
||||||
break
|
cat = Category.toCategory(sin.readLine())
|
||||||
} catch (e) {
|
break }
|
||||||
println "Invalid category: " + e.getLocalizedMessage()
|
catch (e) {
|
||||||
println "Valid options are: \n${Category.values().join(', ')}\n " +
|
println "Invalid category: " + e.getLocalizedMessage()
|
||||||
"(abbreviations are accepted.)"
|
println "Valid options are: \n${Category.values().join(', ')}"
|
||||||
}
|
println " (abbreviations are accepted)." } }
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
if (opts.P) { priority = assignOpts.priority }
|
||||||
|
else while (true) {
|
||||||
try {
|
try {
|
||||||
print "Priority (0-9): "
|
print "Priority (0-9): "
|
||||||
priority = max(0, min(9, sin.readLine().toInteger()))
|
priority = max(0, min(9, sin.readLine().toInteger()))
|
||||||
break
|
break }
|
||||||
} catch (e) { println "Not a valid value." }
|
catch (e) { println "Not a valid value." } }
|
||||||
}
|
|
||||||
|
|
||||||
println "Enter issue (use EOF or ^D to end): "
|
if (opts.getArgs().length > 0) { text = assignOpts.text }
|
||||||
try {
|
else {
|
||||||
sin.eachLine { line ->
|
println "Enter issue (use EOF): "
|
||||||
def m = line =~ /(.*)EOF.*/
|
try {
|
||||||
if (m) {
|
def line = ""
|
||||||
text += m[0][1] + EOL
|
while(true) {
|
||||||
sin.close()
|
line = sin.readLine()
|
||||||
} else text += line + EOL
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
|
if (line =~ /EOF/) break
|
||||||
|
|
||||||
|
text += line + EOL
|
||||||
|
} }
|
||||||
|
catch (e) {} }
|
||||||
|
|
||||||
issue = issuedb.createNewIssue(category: cat, priority: priority, text: text)
|
issue = issuedb.createNewIssue(category: cat, priority: priority, text: text)
|
||||||
|
|
||||||
println "New issue created: "
|
println "New issue created: "
|
||||||
println issue
|
println issue }
|
||||||
|
|
||||||
|
// last, changes to existing issues
|
||||||
|
else {
|
||||||
|
// change priority
|
||||||
|
if (opts.P) walkProject(issuedb, filter) {
|
||||||
|
it.priority = assignOpts.priority
|
||||||
|
println "[${it}] -- set priority to ${assignOpts.priority}"}
|
||||||
|
|
||||||
|
// change third
|
||||||
|
else if (opts.C) walkProject(issuedb, filter) {
|
||||||
|
it.category = assignOpts.cat
|
||||||
|
println "[${it}] -- set category to ${assignOpts.category}"}
|
||||||
|
|
||||||
|
// change status
|
||||||
|
else if (opts.S) walkProject(issuedb, filter) {
|
||||||
|
it.status = assignOpts.status
|
||||||
|
println "[${it}] -- set status to ${assignOpts.status}"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// walk every issue and project in this project recursively and execute the
|
||||||
|
// given closure on each issue that meets the filter criteria
|
||||||
def walkProject(Project p, Filter filter, Closure c) {
|
def walkProject(Project p, Filter filter, Closure c) {
|
||||||
p.eachIssue(filter, c)
|
p.eachIssue(filter, c)
|
||||||
p.eachProject(filter) { walkProject(it, filter, c) }
|
p.eachProject(filter) { walkProject(it, filter, c) }
|
||||||
}
|
}
|
||||||
|
|
||||||
def printProject(Project project, String offset, Filter filter, boolean verbose = false) {
|
|
||||||
}
|
|
||||||
|
@ -1 +1 @@
|
|||||||
application.version=2.5.1
|
application.version=2.6.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user