Compare commits
No commits in common. "main" and "v1.6" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
.gradle/
|
|
||||||
*.sw?
|
*.sw?
|
||||||
build/
|
build/
|
||||||
|
27
build.gradle
27
build.gradle
@ -1,27 +0,0 @@
|
|||||||
apply plugin: "groovy"
|
|
||||||
apply plugin: "war"
|
|
||||||
apply plugin: "maven"
|
|
||||||
|
|
||||||
group = "com.jdblabs"
|
|
||||||
version = "1.14"
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral() }
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile 'ch.qos.logback:logback-classic:1.1.2'
|
|
||||||
compile 'ch.qos.logback:logback-core:1.1.2'
|
|
||||||
compile 'com.jdbernard:jdb-util:3.4'
|
|
||||||
compile 'com.martiansoftware:nailgun-server:0.9.1'
|
|
||||||
compile 'joda-time:joda-time:2.7'
|
|
||||||
compile 'org.codehaus.groovy:groovy-all:2.3.6'
|
|
||||||
compile 'org.slf4j:slf4j-api:1.7.10'
|
|
||||||
providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
|
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
|
||||||
}
|
|
||||||
|
|
||||||
task jlpDocs(type:Exec) {
|
|
||||||
commandLine 'jlp', '--no-source', '--output-dir', 'doc', 'src', 'README.md'
|
|
||||||
}
|
|
70
build.xml
Normal file
70
build.xml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<project name="gtd-cli" basedir="." default="ng-deploy">
|
||||||
|
|
||||||
|
<property file="project.properties"/>
|
||||||
|
<import file="jdb-build-1.10.xml"/>
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="ng-deploy" depends="build">
|
||||||
|
<!-- Stop the Nailgun Server -->
|
||||||
|
<exec executable="cmd" os="Windows XP">
|
||||||
|
<arg value="/c"/>
|
||||||
|
<arg value="ng-stop"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="ng-stop" os="Linux"/>
|
||||||
|
|
||||||
|
<!-- delete old copies -->
|
||||||
|
<delete>
|
||||||
|
<fileset dir="${nailgun.classpath.dir}">
|
||||||
|
<include name="${name}*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- copy new build -->
|
||||||
|
<copy todir="${nailgun.classpath.dir}">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
<fileset dir="${build.dir}">
|
||||||
|
<include name="${name}-${version}.${build.number}.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<!-- start the NG server up again. -->
|
||||||
|
<exec executable="cmd" os="Windows XP">
|
||||||
|
<arg value="/c"/>
|
||||||
|
<arg value="ng-start"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="ng-start" os="Linux"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="servlet" depends="compile,increment-build-number">
|
||||||
|
<mkdir dir="${build.dir}/servlet/WEB-INF/classes"/>
|
||||||
|
<mkdir dir="${build.dir}/servlet/WEB-INF/lib"/>
|
||||||
|
<mkdir dir="${build.dir}/servlet/META-INF"/>
|
||||||
|
|
||||||
|
<copy todir="${build.dir}/servlet/WEB-INF/classes">
|
||||||
|
<fileset dir="${build.dir}/main/classes"/>
|
||||||
|
</copy>
|
||||||
|
<copy todir="${build.dir}/servlet/WEB-INF/lib">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
</copy>
|
||||||
|
<copy todir="${build.dir}/servlet/WEB-INF">
|
||||||
|
<fileset dir="${resources.dir}/WEB-INF"/>
|
||||||
|
</copy>
|
||||||
|
<copy todir="${build.dir}/servlet/META-INF">
|
||||||
|
<fileset dir="${resources.dir}/META-INF"/>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<!--<jar
|
||||||
|
destfile="${build.dir}/${name}-servlet-${version}.${build.number}.war"
|
||||||
|
basedir="${build.dir}/servlet"/> -->
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/gtd.war" basedir="${build.dir}/servlet"/>
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
248
jdb-build-1.10.xml
Normal file
248
jdb-build-1.10.xml
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<project name="Jonathan Bernard Build Common"
|
||||||
|
xmlns:ivy="antlib:org.apache.ivy.ant">
|
||||||
|
|
||||||
|
<property environment="env"/>
|
||||||
|
|
||||||
|
<!--======== INIT TARGETS ========-->
|
||||||
|
<target name="-init" depends="-common-init,init"/>
|
||||||
|
|
||||||
|
<target name="-common-init">
|
||||||
|
<!-- Set default values for some key properties. Since properties are
|
||||||
|
write once, any value set before this point takes precedence. -->
|
||||||
|
|
||||||
|
<property name="versioning.file" value="project.properties"/>
|
||||||
|
|
||||||
|
<property name="src.dir" value="${basedir}/src"/>
|
||||||
|
<property name="build.dir" value="${basedir}/build"/>
|
||||||
|
<property name="lib.dir" value="${basedir}/lib"/>
|
||||||
|
<property name="resources.dir" value="${basedir}/resources"/>
|
||||||
|
<property name="splash.image" value="splash.png"/>
|
||||||
|
|
||||||
|
<!--======== PATHS ========-->
|
||||||
|
<path id="groovy.classpath">
|
||||||
|
<fileset dir="${env.GROOVY_HOME}/lib">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path id="groovy.embeddable">
|
||||||
|
<fileset dir="${env.GROOVY_HOME}/embeddable">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path id="compile-libs">
|
||||||
|
<fileset dir="${build.dir}/lib/compile/jar">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path id="runtime-libs">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-init-groovy">
|
||||||
|
<taskdef name="groovyc" classpathref="groovy.classpath"
|
||||||
|
classname="org.codehaus.groovy.ant.Groovyc"/>
|
||||||
|
|
||||||
|
<taskdef name="groovy" classpathref="groovy.classpath"
|
||||||
|
classname="org.codehaus.groovy.ant.Groovy"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="init"/>
|
||||||
|
|
||||||
|
<target name="clean" depends="-init">
|
||||||
|
<delete dir="${build.dir}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== LIBRARY TARGETS ========-->
|
||||||
|
<target name="-lib" depends="-lib-local,-lib-ivy,lib"/>
|
||||||
|
|
||||||
|
<target name="lib"/>
|
||||||
|
|
||||||
|
<target name="-init-ivy">
|
||||||
|
<ivy:settings id="ivy.settings" file="ivysettings.xml"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-lib-ivy" depends="-init-ivy" unless="${lib.local}">
|
||||||
|
<ivy:retrieve settingsRef="ivy.settings"
|
||||||
|
pattern="${lib.dir}/[conf]/[type]/[artifact]-[revision].[ext]"
|
||||||
|
conf="compile,runtime"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-lib-groovy" if="${lib.local}">
|
||||||
|
<copy todir="${build.dir}/lib/runtime/jar">
|
||||||
|
<fileset dir="${env.GROOVY_HOME}/embeddable"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-lib-local" if="${lib.local}">
|
||||||
|
<echo message="Resolving libraries locally."/>
|
||||||
|
<mkdir dir="${build.dir}/lib/compile/jar"/>
|
||||||
|
<mkdir dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
<copy todir="${build.dir}/lib/compile/jar" failonerror="false">
|
||||||
|
<fileset dir="${lib.dir}/compile/jar"/>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<copy todir="${build.dir}/lib/runtime/jar" failonerror="false">
|
||||||
|
<fileset dir="${lib.dir}/runtime/jar"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== VERSIONING TARGETS ========-->
|
||||||
|
<target name="increment-build-number" depends="-init">
|
||||||
|
<propertyfile file="${versioning.file}">
|
||||||
|
<entry key="build.number" default="0" type="int" value="1"
|
||||||
|
operation="+"/>
|
||||||
|
</propertyfile>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="set-version" depends="-init">
|
||||||
|
<input
|
||||||
|
message="The current version is ${version}. Enter a new version: "
|
||||||
|
addproperty="new-version"/>
|
||||||
|
<propertyfile file="${versioning.file}">
|
||||||
|
<entry key="version" value="${new-version}" operation="="
|
||||||
|
type="string"/>
|
||||||
|
<entry key="build.number" value="0" type="int" operation="="/>
|
||||||
|
</propertyfile>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== COMPILATION TARGETS ========-->
|
||||||
|
<target name="-compile-groovy" depends="-init,-init-groovy,-lib,-lib-groovy">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
<groovyc srcdir="${src.dir}/main" destdir="${build.dir}/main/classes"
|
||||||
|
includeAntRuntime="false" fork="true">
|
||||||
|
|
||||||
|
<classpath>
|
||||||
|
<path refid="groovy.classpath"/>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
</classpath>
|
||||||
|
<javac/>
|
||||||
|
</groovyc>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-compile-java" depends="-init,-lib">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
<javac srcdir="${src.dir}/main" destdir="${build.dir}/main/classes"
|
||||||
|
includeAntRuntime="false" classpathref="compile-libs"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compile" depends="-compile-groovy"/>
|
||||||
|
|
||||||
|
<!--======== JUNIT TARGETS ========-->
|
||||||
|
<target name="-compile-tests-groovy" depends="-init,compile">
|
||||||
|
<mkdir dir="${build.dir}/test/classes"/>
|
||||||
|
<groovyc srcdir="${src.dir}/test" destdir="${build.dir}/test/classes"
|
||||||
|
includeAntRuntime="false" fork="true">
|
||||||
|
|
||||||
|
<classpath>
|
||||||
|
<path refid="groovy.classpath"/>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
<path location="${build.dir}/main/classes"/>
|
||||||
|
</classpath>
|
||||||
|
</groovyc>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-compile-tests-java" depends="-init,compile">
|
||||||
|
<mkdir dir="${build.dir}/test/classes"/>
|
||||||
|
<javac srcdir="${src.dir}/test" destdir="${build.dir}/test/classes"
|
||||||
|
includeAntRuntime="false">
|
||||||
|
<classpath>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
<path location="${build.dir}/main/classes"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compile-tests" depends="-compile-tests-groovy"/>
|
||||||
|
|
||||||
|
<target name="run-tests" depends="compile-tests,resources-test">
|
||||||
|
<junit printsummary="true">
|
||||||
|
<classpath>
|
||||||
|
<path refid="groovy.classpath"/>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
<path location="${build.dir}/main/classes"/>
|
||||||
|
<path location="${build.dir}/test/classes"/>
|
||||||
|
</classpath>
|
||||||
|
<formatter type="plain" usefile="false"/>
|
||||||
|
<batchtest>
|
||||||
|
<fileset dir="${build.dir}/test/classes">
|
||||||
|
<include name="**/*"/>
|
||||||
|
</fileset>
|
||||||
|
</batchtest>
|
||||||
|
</junit>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== RESOURCES TARGETS ========-->
|
||||||
|
|
||||||
|
<target name="resources" depends="-init">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
<copy todir="${build.dir}/main/classes" failonerror="false">
|
||||||
|
<fileset dir="${resources.dir}/main/"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="resources-test" depends="-init">
|
||||||
|
<mkdir dir="${build.dir}/test/classes"/>
|
||||||
|
<copy todir="${build.dir}/test/classes" failonerror="false">
|
||||||
|
<fileset dir="${resources.dir}/test/"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== BUILD TARGETS ========-->
|
||||||
|
<target name="-build-modular-lib" unless="executable.jar"
|
||||||
|
depends="compile,increment-build-number,resources">
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||||
|
basedir="${build.dir}/main/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-build-modular-executable" if="executable.jar"
|
||||||
|
depends="compile,increment-build-number,resources">
|
||||||
|
|
||||||
|
<pathconvert property="jar.classpath" pathsep=" " refid="runtime-libs">
|
||||||
|
<mapper>
|
||||||
|
<chainedmapper>
|
||||||
|
<!-- remove absolute path -->
|
||||||
|
<flattenmapper />
|
||||||
|
|
||||||
|
<!-- add lib/ prefix -->
|
||||||
|
<globmapper from="*" to="lib/*" />
|
||||||
|
</chainedmapper>
|
||||||
|
</mapper>
|
||||||
|
</pathconvert>
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||||
|
basedir="${build.dir}/main/classes">
|
||||||
|
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Main-Class" value="${main.class}"/>
|
||||||
|
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||||
|
<attribute name="SplashScreen-Image" value="${splash.image}"/>
|
||||||
|
</manifest>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-build-modular"
|
||||||
|
depends="-build-modular-lib,-build-modular-executable"/>
|
||||||
|
|
||||||
|
<target name="-build-packed-libs"
|
||||||
|
depends="compile,increment-build-number,resources">
|
||||||
|
|
||||||
|
<unjar destdir="${build.dir}/main/classes">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
</unjar>
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||||
|
basedir="${build.dir}/main/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="-build-modular"/>
|
||||||
|
|
||||||
|
</project>
|
BIN
lib/compile/jar/jdb-util-1.9.jar
Normal file
BIN
lib/compile/jar/jdb-util-1.9.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/joda-time-2.0.jar
Normal file
BIN
lib/compile/jar/joda-time-2.0.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/nailgun-0.7.1.jar
Normal file
BIN
lib/compile/jar/nailgun-0.7.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
BIN
lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/jdb-util-1.9.jar
Normal file
BIN
lib/runtime/jar/jdb-util-1.9.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/joda-time-2.0.jar
Normal file
BIN
lib/runtime/jar/joda-time-2.0.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
BIN
lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/logback-core-0.9.26.jar
Normal file
BIN
lib/runtime/jar/logback-core-0.9.26.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/nailgun-0.7.1.jar
Normal file
BIN
lib/runtime/jar/nailgun-0.7.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
BIN
lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
8
project.properties
Normal file
8
project.properties
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#Wed, 30 Oct 2013 10:46:52 -0500
|
||||||
|
lib.local=true
|
||||||
|
name=jdb-gtd
|
||||||
|
version=1.6
|
||||||
|
nailgun.classpath.dir=/home/jdbernard/programs/nailgun/classpath
|
||||||
|
executable.jar=true
|
||||||
|
main.class=com.jdblabs.gtd.cli.GTDCLI
|
||||||
|
build.number=0
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<init-param>
|
<init-param>
|
||||||
<param-name>gtdRootDir</param-name>
|
<param-name>gtdRootDir</param-name>
|
||||||
<param-value>/home/jdbernard/gtd</param-value>
|
<param-value>/home/jdbernard/Dropbox/gtd</param-value>
|
||||||
</init-param>
|
</init-param>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
_gtd()
|
|
||||||
{
|
|
||||||
local cur prev topOpts debugOpts logLevels
|
|
||||||
COMPREPLY=()
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
||||||
topOpts="help process done calendar list-copies new tickler list debug delegate rename-project list-projects list-contexts"
|
|
||||||
debugOpts="state loglevel"
|
|
||||||
logLevels="TRACE DEBUG INFO WARN ERROR"
|
|
||||||
|
|
||||||
case "${prev}" in
|
|
||||||
help)
|
|
||||||
COMPREPLY=( $(compgen -W "${topOpts}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
done|list-copies|delegate)
|
|
||||||
COMPREPLY=( $(compgen -f ${cur}) )
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
ls|list)
|
|
||||||
COMPREPLY=( $(gtd list-projects) $(gtd list-contexts) )
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
debug)
|
|
||||||
COMPREPLY=( $(compgen -W "${debugOpts}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
loglevel)
|
|
||||||
COMPREPLY=( $(compgen -W "${logLevels}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
COMPREPLY=( $(compgen -W "${topOpts}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
complete -F _gtd gtd
|
|
@ -19,7 +19,6 @@ package com.jdblabs.gtd
|
|||||||
* calendar to "schedule" items, but only to represent items which must be
|
* calendar to "schedule" items, but only to represent items which must be
|
||||||
* done by or on that day.
|
* done by or on that day.
|
||||||
* * `details`: more information related to this item.
|
* * `details`: more information related to this item.
|
||||||
* * `project`: the name of the project with which this item is associated.
|
|
||||||
* @org gtd.jdb-labs.com/Item
|
* @org gtd.jdb-labs.com/Item
|
||||||
*/
|
*/
|
||||||
public class Item {
|
public class Item {
|
@ -104,7 +104,7 @@ public class Util {
|
|||||||
def gtdDirs = [:]
|
def gtdDirs = [:]
|
||||||
|
|
||||||
/// Start by considering the current directory as a candidate.
|
/// Start by considering the current directory as a candidate.
|
||||||
File currentDir = givenDir.canonicalFile
|
File currentDir = givenDir
|
||||||
while (currentDir != null) {
|
while (currentDir != null) {
|
||||||
/// We recognize the GTD root directory when it contains all of the
|
/// We recognize the GTD root directory when it contains all of the
|
||||||
/// GTD top-level directories.
|
/// GTD top-level directories.
|
@ -5,30 +5,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.jdblabs.gtd.cli
|
package com.jdblabs.gtd.cli
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level
|
|
||||||
import ch.qos.logback.classic.Logger
|
|
||||||
import ch.qos.logback.classic.LoggerContext
|
|
||||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
|
|
||||||
import ch.qos.logback.classic.filter.LevelFilter
|
|
||||||
import ch.qos.logback.classic.filter.ThresholdFilter
|
|
||||||
import ch.qos.logback.core.OutputStreamAppender
|
|
||||||
import ch.qos.logback.core.spi.FilterReply
|
|
||||||
import com.jdblabs.gtd.Item
|
import com.jdblabs.gtd.Item
|
||||||
import com.jdblabs.gtd.PropertyHelp
|
import com.jdblabs.gtd.PropertyHelp
|
||||||
import com.jdbernard.util.LightOptionParser
|
import com.jdbernard.util.LightOptionParser
|
||||||
import com.martiansoftware.nailgun.NGContext
|
import com.martiansoftware.nailgun.NGContext
|
||||||
import java.io.FileFilter
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import groovy.io.FileType
|
|
||||||
import org.joda.time.DateMidnight
|
import org.joda.time.DateMidnight
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.slf4j.Logger as SFL4JLogger
|
//import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
//import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
import static com.jdblabs.gtd.Util.*
|
import static com.jdblabs.gtd.Util.*
|
||||||
import static java.nio.file.StandardCopyOption.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command-line helper for working with this implementation of the Getting
|
* Command-line helper for working with this implementation of the Getting
|
||||||
@ -36,7 +23,7 @@ import static java.nio.file.StandardCopyOption.*
|
|||||||
* @org gtd.jdb-labs.com/cli/GTDCLI */
|
* @org gtd.jdb-labs.com/cli/GTDCLI */
|
||||||
public class GTDCLI {
|
public class GTDCLI {
|
||||||
|
|
||||||
public static final String VERSION = "1.14"
|
public static final String VERSION = "1.6"
|
||||||
private static String EOL = System.getProperty("line.separator")
|
private static String EOL = System.getProperty("line.separator")
|
||||||
|
|
||||||
/// We have a persistent instance when we are in the context of a Nailgun
|
/// We have a persistent instance when we are in the context of a Nailgun
|
||||||
@ -54,21 +41,7 @@ public class GTDCLI {
|
|||||||
/// [root-map]: jlp://gtd.jdb-labs.com/notes/root-directory-map
|
/// [root-map]: jlp://gtd.jdb-labs.com/notes/root-directory-map
|
||||||
private Map<String, File> gtdDirs
|
private Map<String, File> gtdDirs
|
||||||
|
|
||||||
/// Logging objects
|
//private Logger log = LoggerFactory.getLogger(getClass())
|
||||||
private Logger log
|
|
||||||
private OutputStreamAppender otherAppender
|
|
||||||
private OutputStreamAppender infoAppender
|
|
||||||
private ThresholdFilter thresholdFilter
|
|
||||||
private LevelFilter rejectInfo
|
|
||||||
private String loggingThreshold
|
|
||||||
|
|
||||||
public void setLoggingThreshold(String level) {
|
|
||||||
if (thresholdFilter) {
|
|
||||||
System.out.println "Changing logging level to $level"
|
|
||||||
thresholdFilter.stop()
|
|
||||||
thresholdFilter.level = level
|
|
||||||
thresholdFilter.start() }
|
|
||||||
this.loggingThreshold = level }
|
|
||||||
|
|
||||||
/** #### `main`
|
/** #### `main`
|
||||||
* Main entry point for a normal GTD CLI process. */
|
* Main entry point for a normal GTD CLI process. */
|
||||||
@ -81,18 +54,16 @@ public class GTDCLI {
|
|||||||
/// Actual processing is done by the
|
/// Actual processing is done by the
|
||||||
/// [`run`](jlp://gtd.jdb-labs.com/cli/GTDCLI/run) method
|
/// [`run`](jlp://gtd.jdb-labs.com/cli/GTDCLI/run) method
|
||||||
if (args.length > 0) args[-1] = args[-1].trim()
|
if (args.length > 0) args[-1] = args[-1].trim()
|
||||||
|
|
||||||
inst.run(args) }
|
inst.run(args) }
|
||||||
|
|
||||||
/** #### `nailMain`
|
/** #### `nailMain`
|
||||||
* Entry point for a GTD CLI process under [Nailgun][ng].
|
* Entry point for a GTD CLI process under [Nailgun][ng].
|
||||||
* [ng]: http://www.martiansoftware.com/nailgun/ */
|
* [ng]: http://www.martiansoftware.com/nailgun/ */
|
||||||
public static void nailMain(NGContext context) {
|
public static void nailMain(NGContext context) {
|
||||||
|
if (nailgunInst == null)
|
||||||
if (nailgunInst == null) {
|
|
||||||
nailgunInst = new GTDCLI(new File(
|
nailgunInst = new GTDCLI(new File(
|
||||||
System.getProperty("user.home"), ".gtdclirc")) }
|
System.getProperty("user.home"), ".gtdclirc"))
|
||||||
else { nailgunInst.stdin = new Scanner(context.in) }
|
else nailgunInst.stdin = new Scanner(context.in)
|
||||||
|
|
||||||
/// Trim the last argument; not all cli's are well-behaved
|
/// Trim the last argument; not all cli's are well-behaved
|
||||||
if (context.args.length > 0) context.args[-1] = context.args[-1].trim()
|
if (context.args.length > 0) context.args[-1] = context.args[-1].trim()
|
||||||
@ -103,7 +74,6 @@ public class GTDCLI {
|
|||||||
* This method reloads the configuration before invoking the run function,
|
* This method reloads the configuration before invoking the run function,
|
||||||
* allowing a long-lived instance to react to configuration changes. */
|
* allowing a long-lived instance to react to configuration changes. */
|
||||||
public static void reconfigure(String[] args) {
|
public static void reconfigure(String[] args) {
|
||||||
|
|
||||||
/// If we do not have a long-running Nailgun instance we just call
|
/// If we do not have a long-running Nailgun instance we just call
|
||||||
/// main.
|
/// main.
|
||||||
if (nailgunInst == null) main(args)
|
if (nailgunInst == null) main(args)
|
||||||
@ -112,7 +82,7 @@ public class GTDCLI {
|
|||||||
/// read afresh the configuration file.
|
/// read afresh the configuration file.
|
||||||
nailgunInst = null
|
nailgunInst = null
|
||||||
nailgunInst = new GTDCLI(new File(
|
nailgunInst = new GTDCLI(new File(
|
||||||
System.getProperty("user.home"), ".gtdclirc"))
|
System.getProperty("user.home"), ".gritterrc"))
|
||||||
|
|
||||||
nailgunInst.run(args) } }
|
nailgunInst.run(args) } }
|
||||||
|
|
||||||
@ -125,55 +95,6 @@ public class GTDCLI {
|
|||||||
if (configFile.exists())
|
if (configFile.exists())
|
||||||
config = new ConfigSlurper().parse(configFile.toURL())
|
config = new ConfigSlurper().parse(configFile.toURL())
|
||||||
|
|
||||||
/// Setup logging
|
|
||||||
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory()
|
|
||||||
lc.reset()
|
|
||||||
|
|
||||||
thresholdFilter = new ThresholdFilter()
|
|
||||||
loggingThreshold = config.defaultLoggingLevel ?: 'INFO'
|
|
||||||
thresholdFilter.level = loggingThreshold
|
|
||||||
|
|
||||||
infoAppender = new OutputStreamAppender()
|
|
||||||
otherAppender = new OutputStreamAppender()
|
|
||||||
PatternLayoutEncoder infoLayout = new PatternLayoutEncoder()
|
|
||||||
PatternLayoutEncoder otherLayout = new PatternLayoutEncoder()
|
|
||||||
LevelFilter acceptInfo = new LevelFilter()
|
|
||||||
rejectInfo = new LevelFilter()
|
|
||||||
|
|
||||||
[infoAppender, otherAppender, infoLayout, otherLayout, acceptInfo,
|
|
||||||
rejectInfo].each { it.context = lc }
|
|
||||||
|
|
||||||
// Setup filter and layout for INFO appender
|
|
||||||
infoLayout.context = lc
|
|
||||||
infoLayout.pattern = '%msg'
|
|
||||||
infoLayout.start()
|
|
||||||
acceptInfo.level = Level.INFO
|
|
||||||
acceptInfo.onMatch = FilterReply.ACCEPT
|
|
||||||
acceptInfo.onMismatch = FilterReply.DENY
|
|
||||||
acceptInfo.start()
|
|
||||||
infoAppender.encoder = infoLayout
|
|
||||||
infoAppender.outputStream = System.out
|
|
||||||
infoAppender.addFilter(acceptInfo)
|
|
||||||
infoAppender.start()
|
|
||||||
|
|
||||||
// Setup filters and layout for non-INFO appender
|
|
||||||
otherLayout.context = lc
|
|
||||||
otherLayout.pattern = '%level -- %msg%n'
|
|
||||||
otherLayout.start()
|
|
||||||
rejectInfo.level = Level.INFO
|
|
||||||
rejectInfo.onMatch = FilterReply.DENY
|
|
||||||
rejectInfo.start()
|
|
||||||
thresholdFilter.start()
|
|
||||||
otherAppender.encoder = otherLayout
|
|
||||||
otherAppender.outputStream = System.err
|
|
||||||
otherAppender.addFilter(rejectInfo)
|
|
||||||
otherAppender.addFilter(thresholdFilter)
|
|
||||||
otherAppender.start()
|
|
||||||
|
|
||||||
log = lc.getLogger(getClass())
|
|
||||||
log.addAppender(infoAppender)
|
|
||||||
log.addAppender(otherAppender)
|
|
||||||
|
|
||||||
/// Configure the terminal width
|
/// Configure the terminal width
|
||||||
terminalWidth = (System.getenv().COLUMNS ?: config.terminalWidth ?: 79) as int
|
terminalWidth = (System.getenv().COLUMNS ?: config.terminalWidth ?: 79) as int
|
||||||
|
|
||||||
@ -190,8 +111,6 @@ public class GTDCLI {
|
|||||||
* @org gtd.jdb-labs.com/cli/GTDCLI/run */
|
* @org gtd.jdb-labs.com/cli/GTDCLI/run */
|
||||||
protected void run(String[] args) {
|
protected void run(String[] args) {
|
||||||
|
|
||||||
log.debug("Args: $args")
|
|
||||||
|
|
||||||
/// Simple CLI options:
|
/// Simple CLI options:
|
||||||
def cliDefinition = [
|
def cliDefinition = [
|
||||||
/// -h, --help
|
/// -h, --help
|
||||||
@ -208,7 +127,7 @@ public class GTDCLI {
|
|||||||
|
|
||||||
if (opts.h) { printUsage(null); return }
|
if (opts.h) { printUsage(null); return }
|
||||||
if (opts.v) { println "GTD CLI v$VERSION"; return }
|
if (opts.v) { println "GTD CLI v$VERSION"; return }
|
||||||
if (opts.d) workingDir = new File(opts.d[0])
|
if (opts.d) workingDir = new File(opts.d)
|
||||||
|
|
||||||
/// View the arguments as a [`LinkedList`][1] so we can use [`peek`][2]
|
/// View the arguments as a [`LinkedList`][1] so we can use [`peek`][2]
|
||||||
/// and [`poll`][3].
|
/// and [`poll`][3].
|
||||||
@ -218,23 +137,18 @@ public class GTDCLI {
|
|||||||
/// [3]: http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html#poll()
|
/// [3]: http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html#poll()
|
||||||
def parsedArgs = (opts.args as List) as LinkedList
|
def parsedArgs = (opts.args as List) as LinkedList
|
||||||
|
|
||||||
log.debug("Parsed args: ${parsedArgs}")
|
|
||||||
|
|
||||||
if (parsedArgs.size() < 1) printUsage()
|
if (parsedArgs.size() < 1) printUsage()
|
||||||
|
|
||||||
/// Make sure we are in a GTD directory.
|
/// Make sure we are in a GTD directory.
|
||||||
gtdDirs = findGtdRootDir(workingDir)
|
gtdDirs = findGtdRootDir(workingDir)
|
||||||
log.debug("gtdDirs:$EOL\t${gtdDirs}")
|
|
||||||
|
|
||||||
if (!gtdDirs) {
|
if (!gtdDirs) {
|
||||||
log.error "fatal: '${workingDir.canonicalPath}'"
|
println "fatal: '${workingDir.canonicalPath}'"
|
||||||
log.error " is not a GTD repository (or any of the parent directories)."
|
println " is not a GTD repository (or any of the parent directories)."
|
||||||
return }
|
return }
|
||||||
|
|
||||||
while (parsedArgs.peek()) {
|
while (parsedArgs.peek()) {
|
||||||
/// Pull off the first argument.
|
/// Pull off the first argument.
|
||||||
def command = parsedArgs.poll()
|
def command = parsedArgs.poll()
|
||||||
log.trace("Processing command: ${command}")
|
|
||||||
|
|
||||||
/// Match the first argument and invoke the proper command method.
|
/// Match the first argument and invoke the proper command method.
|
||||||
switch (command.toLowerCase()) {
|
switch (command.toLowerCase()) {
|
||||||
@ -245,14 +159,9 @@ public class GTDCLI {
|
|||||||
case ~/list-copies/: listCopies(parsedArgs); break
|
case ~/list-copies/: listCopies(parsedArgs); break
|
||||||
case ~/new/: newAction(parsedArgs); break
|
case ~/new/: newAction(parsedArgs); break
|
||||||
case ~/tickler/: tickler(parsedArgs); break
|
case ~/tickler/: tickler(parsedArgs); break
|
||||||
case ~/list-contexts/: listContexts(parsedArgs); break;
|
|
||||||
case ~/list-projects/: listProjects(parsedArgs); break;
|
|
||||||
case ~/ls|list/: ls(parsedArgs); break;
|
case ~/ls|list/: ls(parsedArgs); break;
|
||||||
case ~/debug/: debug(parsedArgs); break;
|
|
||||||
case ~/delegate/: delegateAction(parsedArgs); break;
|
|
||||||
case ~/rp|rename-project/: renameProject(parsedArgs); break;
|
|
||||||
default:
|
default:
|
||||||
log.error "Unrecognized command: ${command}"
|
println "Unrecognized command: ${command}"
|
||||||
break } } }
|
break } } }
|
||||||
|
|
||||||
/** #### `process`
|
/** #### `process`
|
||||||
@ -267,7 +176,7 @@ public class GTDCLI {
|
|||||||
if (path) {
|
if (path) {
|
||||||
def givenDir = new File(path)
|
def givenDir = new File(path)
|
||||||
if (!(gtdDirs = findGtdRootDir(givenDir))) {
|
if (!(gtdDirs = findGtdRootDir(givenDir))) {
|
||||||
log.error "'$path' is not a valid directory."; return }}
|
println "'$path' is not a valid directory."; return }}
|
||||||
|
|
||||||
/// Start processing items
|
/// Start processing items
|
||||||
gtdDirs.in.listFiles().collect { new Item(it) }.each { item ->
|
gtdDirs.in.listFiles().collect { new Item(it) }.each { item ->
|
||||||
@ -358,28 +267,28 @@ public class GTDCLI {
|
|||||||
if (response =~ /del/) {
|
if (response =~ /del/) {
|
||||||
|
|
||||||
item.action = prompt([
|
item.action = prompt([
|
||||||
"Next action (who needs to do what)?", ""])
|
"Next action (who needs to do what).", ""])
|
||||||
|
|
||||||
item.file = new File(promptContext(gtdDirs.waiting),
|
item.file = new File(promptContext(gtdDirs.waiting),
|
||||||
stringToFilename(item.toString())) }
|
stringToFilename(item.action)) }
|
||||||
|
|
||||||
|
|
||||||
/// Defer, move to the *next-actions* folder.
|
/// Defer, move to the *next-actions* folder.
|
||||||
else if (response =~ /def/) {
|
else if (response =~ /def/) {
|
||||||
item.action = prompt(["Next action?", ""])
|
item.action = prompt(["Next action.", ""])
|
||||||
|
|
||||||
item.file = new File(promptContext(gtdDirs["next-actions"]),
|
item.file = new File(promptContext(gtdDirs["next-actions"]),
|
||||||
stringToFilename(item.toString())) }
|
stringToFilename(item.action)) }
|
||||||
|
|
||||||
/// Forget for now, move it to the *tickler* folder.
|
/// Forget for now, move it to the *tickler* folder.
|
||||||
else {
|
else {
|
||||||
item.action = prompt(["Next action?", ""])
|
item.action = prompt(["Next action.", ""])
|
||||||
item.tickle = prompt([
|
item.tickle = prompt([
|
||||||
"When do you want it to become active?",
|
"When do you want it to become active?",
|
||||||
"(YYYY-MM-DD)"])
|
"(YYYY-MM-DD)"])
|
||||||
|
|
||||||
item.file = new File(gtdDirs.tickler,
|
item.file = new File(gtdDirs.tickler,
|
||||||
stringToFilename(item.toString())) }
|
stringToFilename(item.action)) }
|
||||||
|
|
||||||
item.save()
|
item.save()
|
||||||
oldFile.delete()
|
oldFile.delete()
|
||||||
@ -394,7 +303,7 @@ public class GTDCLI {
|
|||||||
if (item.project && projectDir.exists() &&
|
if (item.project && projectDir.exists() &&
|
||||||
projectDir.isDirectory()) {
|
projectDir.isDirectory()) {
|
||||||
item.file = new File(projectDir,
|
item.file = new File(projectDir,
|
||||||
stringToFilename(item.toString()))
|
stringToFilename(item.action))
|
||||||
item.save()
|
item.save()
|
||||||
println "Copied to " +
|
println "Copied to " +
|
||||||
getRelativePath(gtdDirs.root, item.file.parentFile) } } } } }
|
getRelativePath(gtdDirs.root, item.file.parentFile) } } } } }
|
||||||
@ -408,25 +317,23 @@ public class GTDCLI {
|
|||||||
*/
|
*/
|
||||||
protected void done(LinkedList args) {
|
protected void done(LinkedList args) {
|
||||||
|
|
||||||
def selectedFilePath
|
def selectedFilePath = args.poll()
|
||||||
|
|
||||||
if (!args) {
|
if (!selectedFilePath) {
|
||||||
log.error "The 'gtd done' command requires an <action-file> parameter."
|
println "gtd done command requires a <action-file> parameter."
|
||||||
return }
|
return }
|
||||||
|
|
||||||
while ((selectedFilePath = args.poll())) {
|
while (selectedFilePath) {
|
||||||
def item
|
def item
|
||||||
def selectedFile = new File(selectedFilePath)
|
def selectedFile = new File(selectedFilePath)
|
||||||
|
|
||||||
if (!selectedFile.isAbsolute())
|
|
||||||
selectedFile = new File(workingDir, selectedFilePath)
|
|
||||||
|
|
||||||
if (!selectedFile.exists() || !selectedFile.isFile()) {
|
if (!selectedFile.exists() || !selectedFile.isFile()) {
|
||||||
log.error "File does not exist or is a directory:"
|
println "File does not exist or is a directory:"
|
||||||
log.error "\t" + selectedFile.canonicalPath
|
println "\t" + selectedFile.canonicalPath
|
||||||
continue }
|
continue }
|
||||||
|
|
||||||
item = new Item(selectedFile)
|
if (selectedFile.isAbsolute()) item = new Item(selectedFile)
|
||||||
|
else item = new Item(new File(workingDir, selectedFilePath))
|
||||||
|
|
||||||
/// Move to the done folder.
|
/// Move to the done folder.
|
||||||
def oldFile = item.file
|
def oldFile = item.file
|
||||||
@ -438,16 +345,16 @@ public class GTDCLI {
|
|||||||
if (inPath(gtdDirs.projects, oldFile)) {
|
if (inPath(gtdDirs.projects, oldFile)) {
|
||||||
|
|
||||||
/// Delete any copies of this item from the next actions folder.
|
/// Delete any copies of this item from the next actions folder.
|
||||||
findAllCopies(oldFile, gtdDirs["next-actions"]).each { file ->
|
findAllCopies(oldFile, gtdDirs."next-actions").each { file ->
|
||||||
println "Deleting duplicate entry from the " +
|
println "Deleting duplicate entry from the " +
|
||||||
"${file.parentFile.name} context."
|
"${file.parentFile.name} context."
|
||||||
if (file.exists()) file.delete() }
|
file.delete() }
|
||||||
|
|
||||||
/// Delete any copies of this item from the waiting folder.
|
/// Delete any copies of this item from the waiting folder.
|
||||||
findAllCopies(oldFile, gtdDirs.waiting).each { file ->
|
findAllCopies(oldFile, gtdDirs.waiting).each { file ->
|
||||||
println "Deleting duplicate entry from the " +
|
println "Deleting duplicate entry from the " +
|
||||||
"${file.parentFile.name} waiting context."
|
"${file.parentFile.name} waiting context."
|
||||||
if (file.exists()) file.delete() }}
|
file.delete() }}
|
||||||
|
|
||||||
/// Check if this item was in the next-action or waiting folder.
|
/// Check if this item was in the next-action or waiting folder.
|
||||||
if (inPath(gtdDirs["next-actions"], oldFile) ||
|
if (inPath(gtdDirs["next-actions"], oldFile) ||
|
||||||
@ -457,11 +364,12 @@ public class GTDCLI {
|
|||||||
findAllCopies(oldFile, gtdDirs.projects).each { file ->
|
findAllCopies(oldFile, gtdDirs.projects).each { file ->
|
||||||
println "Deleting duplicate entry from the " +
|
println "Deleting duplicate entry from the " +
|
||||||
"${file.parentFile.name} project."
|
"${file.parentFile.name} project."
|
||||||
if (file.exists()) file.delete() }}
|
file.delete() }}
|
||||||
|
|
||||||
/// Delete the original
|
/// Delete the original
|
||||||
oldFile.delete()
|
oldFile.delete()
|
||||||
|
|
||||||
|
selectedFilePath = args.poll()
|
||||||
println "'$item' marked as done." } }
|
println "'$item' marked as done." } }
|
||||||
|
|
||||||
/** #### `calendar`
|
/** #### `calendar`
|
||||||
@ -524,7 +432,7 @@ public class GTDCLI {
|
|||||||
if (!file.isAbsolute()) file = new File(workingDir, filePath)
|
if (!file.isAbsolute()) file = new File(workingDir, filePath)
|
||||||
|
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
log.error "${file.canonicalPath} is not a regular file."
|
println "${file.canonicalPath} is not a regular file."
|
||||||
return }
|
return }
|
||||||
|
|
||||||
String originalRelativePath = getRelativePath(gtdDirs.root, file)
|
String originalRelativePath = getRelativePath(gtdDirs.root, file)
|
||||||
@ -582,7 +490,7 @@ public class GTDCLI {
|
|||||||
/// exists, copy the item there.
|
/// exists, copy the item there.
|
||||||
def projectDir = new File(gtdDirs.projects, item.project ?: '')
|
def projectDir = new File(gtdDirs.projects, item.project ?: '')
|
||||||
if (item.project && projectDir.exists() && projectDir.isDirectory()) {
|
if (item.project && projectDir.exists() && projectDir.isDirectory()) {
|
||||||
item.file = new File(projectDir, stringToFilename(item.toString()))
|
item.file = new File(projectDir, stringToFilename(item.action))
|
||||||
item.save()
|
item.save()
|
||||||
println "Copied to " +
|
println "Copied to " +
|
||||||
getRelativePath(gtdDirs.root, item.file.parentFile) } }
|
getRelativePath(gtdDirs.root, item.file.parentFile) } }
|
||||||
@ -604,10 +512,10 @@ public class GTDCLI {
|
|||||||
/// If the item is scheduled to be tickled today (or in the past)
|
/// If the item is scheduled to be tickled today (or in the past)
|
||||||
/// then move it into the next-actions folder
|
/// then move it into the next-actions folder
|
||||||
if ((item.tickle as DateMidnight) <= today) {
|
if ((item.tickle as DateMidnight) <= today) {
|
||||||
println "Moving '${item}' out of the tickler."
|
println "Moving '${item.action}' out of the tickler."
|
||||||
def oldFile = item.file
|
def oldFile = item.file
|
||||||
item.file = new File(gtdDirs."next-actions",
|
item.file = new File(gtdDirs."next-actions",
|
||||||
stringToFilename(item.toString()))
|
stringToFilename(item.action))
|
||||||
item.gtdProperties.remove("tickle")
|
item.gtdProperties.remove("tickle")
|
||||||
item.save()
|
item.save()
|
||||||
oldFile.delete() }}}
|
oldFile.delete() }}}
|
||||||
@ -622,254 +530,38 @@ public class GTDCLI {
|
|||||||
*/
|
*/
|
||||||
protected void ls(LinkedList args) {
|
protected void ls(LinkedList args) {
|
||||||
|
|
||||||
def target
|
def target = args.poll()
|
||||||
|
|
||||||
/// Temporary helper function to print all the items in a given
|
/// Temporary helper function to print all the items in a given
|
||||||
/// directory.
|
/// directory.
|
||||||
def printItems = { dir ->
|
def printItems = { dir ->
|
||||||
if (!dir.exists() || !dir.isDirectory()) return
|
if (!dir.exists() || !dir.isDirectory()) return
|
||||||
println "-- ${getRelativePath(gtdDirs.root, dir)} --"
|
println "-- ${getRelativePath(gtdDirs.root, dir)} --"
|
||||||
dir.listFiles().sort { it.name }.each { file ->
|
dir.eachFile { file ->
|
||||||
if (!file.exists() || !file.isFile() || file.isHidden() ||
|
if (!file.exists() || !file.isFile() || file.isHidden() ||
|
||||||
file.name.startsWith('.'))
|
file.name.startsWith('.'))
|
||||||
return
|
return
|
||||||
|
|
||||||
def item = new Item(file)
|
def item = new Item(file)
|
||||||
println item}
|
println item.action }
|
||||||
|
|
||||||
println "" }
|
println "" }
|
||||||
|
|
||||||
/// If we have no named context or project, print all items in the
|
/// If we have a named context or project, look for those items
|
||||||
/// *next-actions* and *waiting* folders and all their subfolders.
|
/// specifically
|
||||||
if (!args) {
|
if (target) {
|
||||||
|
|
||||||
|
printItems(new File(gtdDirs['next-actions'], target))
|
||||||
|
printItems(new File(gtdDirs.waiting, target))
|
||||||
|
printItems(new File(gtdDirs.projects, target)) }
|
||||||
|
|
||||||
|
/// Otherwise print all items in the *next-actions* and *waiting*
|
||||||
|
/// folders and all their subfolders.
|
||||||
|
else {
|
||||||
printItems(gtdDirs['next-actions'])
|
printItems(gtdDirs['next-actions'])
|
||||||
printItems(gtdDirs['waiting'])
|
printItems(gtdDirs['waiting'])
|
||||||
gtdDirs['next-actions'].eachDir(printItems)
|
gtdDirs['next-actions'].eachDir(printItems)
|
||||||
gtdDirs['waiting'].eachDir(printItems) }
|
gtdDirs['waiting'].eachDir(printItems) } }
|
||||||
|
|
||||||
/// For every name we do have, look for a project or context and
|
|
||||||
/// recursively print their contents.
|
|
||||||
else while ((target = args.poll())) {
|
|
||||||
printItems(new File(gtdDirs['next-actions'], target))
|
|
||||||
printItems(new File(gtdDirs.waiting, target))
|
|
||||||
printItems(new File(gtdDirs.projects, target)) } }
|
|
||||||
|
|
||||||
/** #### `listProjects`
|
|
||||||
* Implement the `list-projects` command to list all the known projects
|
|
||||||
* for this repository. For detailed information see the
|
|
||||||
* [online help][help-list-projects] by running `gtd help list-projects`.
|
|
||||||
*
|
|
||||||
* [help-list-projects]: jlp://gtd.jdb-labs.com/cli/GTDCLI/help/list-projects
|
|
||||||
*/
|
|
||||||
protected void listProjects(LinkedList args) {
|
|
||||||
gtdDirs.projects.eachFile(FileType.DIRECTORIES) { println it.name } }
|
|
||||||
|
|
||||||
/** #### `listContexts`
|
|
||||||
* Implement the `list-contexts` command to list all the known contexts
|
|
||||||
* for this repository. For detailed information see the
|
|
||||||
* [online help][help-list-contexts] by running `gtd help list-contexts`.
|
|
||||||
*
|
|
||||||
* [help-list-contexts]: jlp://gtd.jdb-labs.com/cli/GTDCLI/help/list-contexts
|
|
||||||
*/
|
|
||||||
protected void listContexts(LinkedList args) {
|
|
||||||
def ctxNames = []
|
|
||||||
gtdDirs["next-actions"].eachFile(FileType.DIRECTORIES) { ctxNames << it.name }
|
|
||||||
gtdDirs.waiting.eachFile(FileType.DIRECTORIES) { ctxNames << it.name }
|
|
||||||
ctxNames.unique().each { println it } }
|
|
||||||
|
|
||||||
/** #### `debug`
|
|
||||||
* Print out debug information. Currently this prints out the internal
|
|
||||||
* state of the CLI. I may add other subcommands if the need arises. */
|
|
||||||
protected void debug(LinkedList args) {
|
|
||||||
|
|
||||||
def command = args.poll()
|
|
||||||
|
|
||||||
if (!command || "state" == command) {
|
|
||||||
println "GTD CLI v${VERSION}"
|
|
||||||
println ""
|
|
||||||
println "-- General"
|
|
||||||
println " Running under nailgun? ${nailgunInst ? 'yes' : 'no'}"
|
|
||||||
println " Terminal width ${terminalWidth}"
|
|
||||||
println " Working directory ${workingDir.canonicalPath}"
|
|
||||||
println ""
|
|
||||||
println "-- GTD Directories"
|
|
||||||
gtdDirs.each { k, v -> println " ${k.padRight(12)} ${v.canonicalPath}" }
|
|
||||||
println ""
|
|
||||||
println "-- Logging"
|
|
||||||
println " Threshold ${loggingThreshold}"
|
|
||||||
log.trace " Message from TRACE"
|
|
||||||
log.debug " Message from DEBUG"
|
|
||||||
log.info " Message from INFO${EOL}"
|
|
||||||
log.warn " Message from WARN"
|
|
||||||
log.error " Message from ERROR" }
|
|
||||||
|
|
||||||
else if ("loglevel" == command) {
|
|
||||||
def level = args.poll()
|
|
||||||
|
|
||||||
if (!level)
|
|
||||||
log.error "debug loglevel command requires additional arguments."
|
|
||||||
|
|
||||||
else setLoggingThreshold(level) }
|
|
||||||
else log.error "Unrecognized debug command: '${command}'." }
|
|
||||||
|
|
||||||
/** #### `delegate`
|
|
||||||
* Implement the `delegate` command. This allows you to move an action
|
|
||||||
* from the next action list to the delegate list, providing the name of
|
|
||||||
* the responsible party and optionally renaming the item. For detailed
|
|
||||||
* information see the [online help][help-delegate] by running
|
|
||||||
* `gtd help delegate`.
|
|
||||||
*
|
|
||||||
* [help-delegate]: jlp://gtd.jdb-labs.com/cli/GTDCLI/help/delegate
|
|
||||||
*/
|
|
||||||
protected void delegateAction(LinkedList args) {
|
|
||||||
def selectedFilePath
|
|
||||||
|
|
||||||
if (!args) {
|
|
||||||
log.error("The 'gtd delegate' command requires an " +
|
|
||||||
"<action-file> parameter.")
|
|
||||||
return }
|
|
||||||
|
|
||||||
while ((selectedFilePath = args.poll())) {
|
|
||||||
|
|
||||||
Item item
|
|
||||||
File oldFile, newContextDir
|
|
||||||
File selectedFile = new File(selectedFilePath)
|
|
||||||
|
|
||||||
if (!selectedFile.isAbsolute())
|
|
||||||
selectedFile = new File(workingDir, selectedFilePath)
|
|
||||||
|
|
||||||
if (!selectedFile.exists() || !selectedFile.isFile()) {
|
|
||||||
log.error "File does not exist or is a directory:"
|
|
||||||
log.error "\t" + selectedFile.canonicalPath
|
|
||||||
continue }
|
|
||||||
|
|
||||||
item = new Item(selectedFile)
|
|
||||||
oldFile = item.file
|
|
||||||
|
|
||||||
/// Move to the waiting folder, with the name of the delegatee and
|
|
||||||
/// optionally a new next action.
|
|
||||||
def delegatee = prompt(
|
|
||||||
["Who is responsible for the next action? You may also update the next action",
|
|
||||||
"by including it after a colon (e.g. 'Delegatee Name: New next action.').",
|
|
||||||
""])
|
|
||||||
|
|
||||||
if (delegatee.indexOf(':') > 0) item.action = delegatee
|
|
||||||
else item.action = delegatee + ': ' + item.action
|
|
||||||
|
|
||||||
/// Check if this item was in a project folder.
|
|
||||||
if (inPath(gtdDirs.projects, oldFile)) {
|
|
||||||
|
|
||||||
/// Rename the file in the project folder
|
|
||||||
item.file = new File(oldFile.parentFile,
|
|
||||||
stringToFilename(item.toString()))
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
/// Move any copies of this item from the next actions folder
|
|
||||||
/// to the waiting folder.
|
|
||||||
findAllCopies(oldFile, gtdDirs['next-actions']).each { dupFile ->
|
|
||||||
println "Moving duplicate entry from the " +
|
|
||||||
"${dupFile.parentFile.name} context."
|
|
||||||
|
|
||||||
/// Retain the item's context if possible
|
|
||||||
newContextDir = new File(gtdDirs.waiting,
|
|
||||||
dupFile.parentFile.name)
|
|
||||||
|
|
||||||
/// Instead of creating a new Item object, let's just
|
|
||||||
/// create a copy of the existing one on the filesystem by
|
|
||||||
/// saving the existing object to a the new location.
|
|
||||||
if (newContextDir.exists() && newContextDir.isDirectory()) {
|
|
||||||
item.file = new File(newContextDir,
|
|
||||||
stringToFilename(item.toString())) }
|
|
||||||
|
|
||||||
else { item.file = new File(gtdDirs.waiting,
|
|
||||||
stringToFilename(item.toString())) }
|
|
||||||
|
|
||||||
item.save()
|
|
||||||
dupfile.delete() }}
|
|
||||||
|
|
||||||
/// Check if this item was in the next-action folder.
|
|
||||||
else if (inPath(gtdDirs["next-actions"], oldFile) ||
|
|
||||||
inPath(gtdDirs.waiting, oldFile)) {
|
|
||||||
|
|
||||||
/// Retain the item's context if possible.
|
|
||||||
newContextDir = new File(gtdDirs.waiting,
|
|
||||||
oldFile.parentFile.name)
|
|
||||||
|
|
||||||
/// Move the file to the waiting folder.
|
|
||||||
if (newContextDir.exists() && newContextDir.isDirectory()) {
|
|
||||||
item.file = new File(newContextDir,
|
|
||||||
stringToFilename(item.toString())) }
|
|
||||||
|
|
||||||
else { item.file = new File(gtdDirs.waiting,
|
|
||||||
stringToFilename(item.toString())) }
|
|
||||||
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
/// Rename any copies of this item from the projects folder.
|
|
||||||
findAllCopies(oldFile, gtdDirs.projects).each { dupFile ->
|
|
||||||
println "Renaming duplicate entry from the " +
|
|
||||||
"${dupFile.parentFile.name} project."
|
|
||||||
item.file = new File(dupFile.parentFile,
|
|
||||||
stringToFilename(item.toString()))
|
|
||||||
|
|
||||||
item.save()
|
|
||||||
dupFile.delete() } }
|
|
||||||
|
|
||||||
/// Delete the original file.
|
|
||||||
oldFile.delete() } }
|
|
||||||
|
|
||||||
/** #### `rename-project`
|
|
||||||
* Implement the `rename-project` command. This will rename the project
|
|
||||||
* directory in TODO as well as change the project reference in any of the
|
|
||||||
* items from the `next-actions` contexts.
|
|
||||||
*
|
|
||||||
* `gtd help rename-project`.
|
|
||||||
*
|
|
||||||
* [help-rename-project]: jlp://gtd.jdb-labs.com/cli/GTDCLI/help/rename-project
|
|
||||||
*/
|
|
||||||
protected void renameProject(LinkedList args) {
|
|
||||||
def projectName = args.poll()
|
|
||||||
def newName = args.poll()
|
|
||||||
|
|
||||||
if (!projectName || !newName) {
|
|
||||||
log.error "The 'gtd rename-project' command requires two " +
|
|
||||||
"parameters: <existing-project-name> and a <new-name>."
|
|
||||||
return }
|
|
||||||
|
|
||||||
def projectDir = new File(gtdDirs.projects, projectName)
|
|
||||||
if (!projectDir.exists() || !projectDir.isDirectory()) {
|
|
||||||
log.error "There is no directory named '$projectName' in the " +
|
|
||||||
"'projects' directory."
|
|
||||||
return }
|
|
||||||
|
|
||||||
def newDir = new File(gtdDirs.projects, newName)
|
|
||||||
if (newDir.exists()) {
|
|
||||||
log.error "There is already a project named '$newName'."
|
|
||||||
return }
|
|
||||||
|
|
||||||
// Perform the rename of the directory itself.
|
|
||||||
try { Files.move(projectDir.toPath(), newDir.toPath(), REPLACE_EXISTING) }
|
|
||||||
catch (Exception e) {
|
|
||||||
log.error "Unable to rename the project: ${e.localizedMessage}."
|
|
||||||
return }
|
|
||||||
|
|
||||||
// Update all of the items associated with this project.
|
|
||||||
def projectFiles = newDir.
|
|
||||||
listFiles({ File f ->
|
|
||||||
f.exists() && !f.isHidden() &&
|
|
||||||
f.isFile() && !f.name.startsWith('.') } as FileFilter)
|
|
||||||
|
|
||||||
def allProjectItems = projectFiles.collectMany { f ->
|
|
||||||
findAllCopies(f, gtdDirs.root).collect { new Item(it) } }
|
|
||||||
|
|
||||||
allProjectItems.each {
|
|
||||||
it.project = newName
|
|
||||||
it.save() }
|
|
||||||
|
|
||||||
println "Project renamed. ${allProjectItems.size()} items updated." }
|
|
||||||
|
|
||||||
private void print(String msg) { log.info(msg) }
|
|
||||||
private void println(String line) { log.info(line + EOL) }
|
|
||||||
|
|
||||||
/** #### `help`
|
/** #### `help`
|
||||||
* Implement the `help` command which provides the online-help. Users can
|
* Implement the `help` command which provides the online-help. Users can
|
||||||
@ -890,41 +582,20 @@ options are:
|
|||||||
top-level commands:
|
top-level commands:
|
||||||
|
|
||||||
help <command> Print detailed help about a command.
|
help <command> Print detailed help about a command.
|
||||||
|
|
||||||
process Process inbox items systematically.
|
process Process inbox items systematically.
|
||||||
|
|
||||||
done <action-file> Mark an action as done. This will automatically
|
done <action-file> Mark an action as done. This will automatically
|
||||||
take care of duplicates of the action in project
|
take care of duplicates of the action in project
|
||||||
or next-actions sub-folders.
|
or next-actions sub-folders.
|
||||||
|
|
||||||
calendar Show the tasks with specific days assigned to
|
calendar Show the tasks with specific days assigned to
|
||||||
them, sorted by date.
|
them, sorted by date.
|
||||||
|
|
||||||
list-copies <action-file> Given an action item, list all the other places
|
list-copies <action-file> Given an action item, list all the other places
|
||||||
there the same item is filed (cross-reference
|
there the same item is filed (cross-reference
|
||||||
with a project folder, for example).
|
with a project folder, for example).
|
||||||
|
|
||||||
new Interactively create a new action item in the
|
new Interactively create a new action item in the
|
||||||
current folder.
|
current folder.
|
||||||
|
|
||||||
tickler Search the tickler file for items that need to be
|
tickler Search the tickler file for items that need to be
|
||||||
delivered and move them to the *next-actions*
|
delivered and move them to the *next-actions*
|
||||||
folder.
|
folder."""
|
||||||
|
|
||||||
list, ls [<context> ...] List all the tasks for a given set of contexts
|
|
||||||
projects.
|
|
||||||
|
|
||||||
debug n
|
|
||||||
|
|
||||||
delegate Move an item from a next-action context or a
|
|
||||||
project folder to a waiting context and attach
|
|
||||||
the name of the party now responsible for the
|
|
||||||
item.
|
|
||||||
|
|
||||||
rename-project, rp <existing-project> <new-name>
|
|
||||||
|
|
||||||
Rename a project directory and update any task
|
|
||||||
items that reference it."""
|
|
||||||
} else {
|
} else {
|
||||||
def command = args.poll()
|
def command = args.poll()
|
||||||
|
|
||||||
@ -1028,56 +699,15 @@ file for any items that should become active (based on their <tickle> property)
|
|||||||
and moves them out of the tickler file and into the next-actions file."""
|
and moves them out of the tickler file and into the next-actions file."""
|
||||||
break
|
break
|
||||||
|
|
||||||
/// Online help for the `ls`/`list` command.
|
/// Online help for the `ls`/`list-context` command.
|
||||||
/// @org gtd.jdb-labs.com/cli/GTDCLI/help/ls
|
/// @org gtd.jdb-labs.com/cli/GTDCLI/help/ls
|
||||||
case ~/ls|list/: println """\
|
case ~/ls|list-context/: println """\
|
||||||
usage gtd list [<context> ...]
|
usage gtd ls [<context> ...]
|
||||||
or gtd ls [<context> ...]
|
|
||||||
|
|
||||||
This command lists all the tasks for a given context or project. The purpose is
|
This command lists all the tasks for a given context or project. The purpose is
|
||||||
to list in one place items that are sitting in the next-actions folder or the
|
to list in one place items that are sitting in the next-actions folder or the
|
||||||
waiting folder for a specific context or list items for a given project. If no
|
waiting folder for a specific context or list items for a given project. If no
|
||||||
context or project is named, all contexts are listed."""
|
context or project is named, all contexts are listed."""
|
||||||
break
|
|
||||||
|
|
||||||
/// Online help for the `delegate` command.
|
|
||||||
/// @org gtd.jdb-labs.com/cli/GTDCLI/help/delegate
|
|
||||||
case ~/delegate/: println """\
|
|
||||||
usage gtd delegate [<action-file> ...]
|
|
||||||
|
|
||||||
This command moves an action item from a next-action context or project folder
|
|
||||||
to the delegate folder. It allows the user to attach the name of the newly
|
|
||||||
responsible party and optionally rename the item."""
|
|
||||||
break
|
|
||||||
|
|
||||||
/// Online help for the `list-projects` command.
|
|
||||||
/// @org gtd.jdb-labs.com/cli/GTDCLI/hemp/list-projects
|
|
||||||
case ~/list-projects/: println """\
|
|
||||||
usage gtd list-projects
|
|
||||||
|
|
||||||
This command lists all of the project folders defined in this repository (all
|
|
||||||
the folders in the /projects folder."""
|
|
||||||
break
|
|
||||||
|
|
||||||
/// Online help for the `list-contexts` command.
|
|
||||||
/// @org gtd.jdb-labs.com/cli/GTDCLI/hemp/list-contexts
|
|
||||||
case ~/list-contexts/: println """\
|
|
||||||
usage gtd list-contexts
|
|
||||||
|
|
||||||
This command lists all of the context folders defined in this repository (all
|
|
||||||
the folders in the /next-actions and /waiting folders."""
|
|
||||||
break
|
|
||||||
|
|
||||||
/// Online help for the `rename-project` command.
|
|
||||||
/// @org gtd.jdb-labs.com/cli/GTDCLI/help/rename-project
|
|
||||||
case ~/delegate/: println """\
|
|
||||||
usage gtd rename-project <existing-project> <new-name>
|
|
||||||
or gtd rp <existing-project> <new-name>
|
|
||||||
|
|
||||||
This command renames a project directory and updates any items that reference
|
|
||||||
the project."""
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1107,7 +737,6 @@ the project."""
|
|||||||
contextFile = line ? new File(baseDir, line) : baseDir
|
contextFile = line ? new File(baseDir, line) : baseDir
|
||||||
|
|
||||||
while (!contextFile.exists() || !contextFile.isDirectory()) {
|
while (!contextFile.exists() || !contextFile.isDirectory()) {
|
||||||
log.warn "'$line' is not a valid context."
|
|
||||||
println "Available contexts:"
|
println "Available contexts:"
|
||||||
baseDir.eachDir { print "\t${it.name}"}
|
baseDir.eachDir { print "\t${it.name}"}
|
||||||
println ""
|
println ""
|
Loading…
x
Reference in New Issue
Block a user