diff --git a/client/pom.xml b/client/pom.xml
index 03addfb8..e557258f 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -95,6 +95,15 @@
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ ${project.basedir}
+
+
+
@@ -124,6 +133,7 @@
--no-fallback
--allow-incomplete-classpath
-H:IncludeResources=org/mvndaemon/mvnd/.*
+ -H:IncludeResources=mvnd-bash-completion.bash
-H:-ParseRuntimeOptions
-ea
diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/Completion.java b/client/src/main/java/org/mvndaemon/mvnd/client/Completion.java
new file mode 100644
index 00000000..590483dc
--- /dev/null
+++ b/client/src/main/java/org/mvndaemon/mvnd/client/Completion.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.client;
+
+import org.mvndaemon.mvnd.common.IoUtils;
+
+public class Completion {
+
+ public static String getCompletion(String shell) {
+ if (!"bash".equals(shell)) {
+ throw new IllegalArgumentException("Unexpected --completion value: '" + shell + "'; expected: 'bash'");
+ }
+ return IoUtils.readResource(Completion.class.getClassLoader(), "mvnd-bash-completion.bash");
+ }
+
+}
diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java b/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java
index b31ccd9d..ca0a3626 100644
--- a/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java
+++ b/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java
@@ -78,7 +78,8 @@ public class DefaultClient implements Client {
}
// Batch mode
- boolean batchMode = Environment.MAVEN_BATCH_MODE.hasCommandLineOption(args);
+ boolean batchMode = Environment.MAVEN_BATCH_MODE.hasCommandLineOption(args)
+ || Environment.COMPLETION.hasCommandLineOption(args);
// System properties
setSystemPropertiesFromCommandLine(args);
@@ -133,6 +134,12 @@ public class DefaultClient implements Client {
LOGGER.debug("Starting client");
final List args = new ArrayList<>(argv);
+ final String completionShell = Environment.COMPLETION.removeCommandLineOption(args);
+ if (completionShell != null) {
+ output.accept(Message.log(Completion.getCompletion(completionShell)));
+ return DefaultResult.success(argv);
+ }
+
boolean version = Environment.MAVEN_VERSION.hasCommandLineOption(args);
boolean showVersion = Environment.MAVEN_SHOW_VERSION.hasCommandLineOption(args);
boolean debug = Environment.MAVEN_DEBUG.hasCommandLineOption(args);
diff --git a/client/src/main/resources/mvnd-bash-completion.bash b/client/src/main/resources/mvnd-bash-completion.bash
new file mode 100644
index 00000000..478d9fd7
--- /dev/null
+++ b/client/src/main/resources/mvnd-bash-completion.bash
@@ -0,0 +1,286 @@
+#
+# Copyright 2019 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Adapted from https://github.com/juven/maven-bash-completion/blob/master/bash_completion.bash by Juven Xu and others
+# under Apache License Version 2.0
+
+function_exists()
+{
+ declare -F $1 > /dev/null
+ return $?
+}
+
+function_exists _get_comp_words_by_ref ||
+_get_comp_words_by_ref ()
+{
+ local exclude cur_ words_ cword_;
+ if [ "$1" = "-n" ]; then
+ exclude=$2;
+ shift 2;
+ fi;
+ __git_reassemble_comp_words_by_ref "$exclude";
+ cur_=${words_[cword_]};
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ cur)
+ cur=$cur_
+ ;;
+ prev)
+ prev=${words_[$cword_-1]}
+ ;;
+ words)
+ words=("${words_[@]}")
+ ;;
+ cword)
+ cword=$cword_
+ ;;
+ esac;
+ shift;
+ done
+}
+
+function_exists __ltrim_colon_completions ||
+__ltrim_colon_completions()
+{
+ if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
+ # Remove colon-word prefix from COMPREPLY items
+ local colon_word=${1%${1##*:}}
+ local i=${#COMPREPLY[*]}
+ while [[ $((--i)) -ge 0 ]]; do
+ COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
+ done
+ fi
+}
+
+function_exists __find_mvn_projects ||
+__find_mvn_projects()
+{
+ find . -name 'pom.xml' -not -path '*/target/*' -prune | while read LINE ; do
+ local withoutPom=${LINE%/pom.xml}
+ local module=${withoutPom#./}
+ if [[ -z ${module} ]]; then
+ echo "."
+ else
+ echo ${module}
+ fi
+ done
+}
+
+function_exists _realpath ||
+_realpath ()
+{
+ if [[ -f "$1" ]]
+ then
+ # file *must* exist
+ if cd "$(echo "${1%/*}")" &>/dev/null
+ then
+ # file *may* not be local
+ # exception is ./file.ext
+ # try 'cd .; cd -;' *works!*
+ local tmppwd="$PWD"
+ cd - &>/dev/null
+ else
+ # file *must* be local
+ local tmppwd="$PWD"
+ fi
+ else
+ # file *cannot* exist
+ return 1 # failure
+ fi
+
+ # suppress shell session termination messages on macOS
+ shell_session_save()
+ {
+ false
+ }
+
+ # reassemble realpath
+ echo "$tmppwd"/"${1##*/}"
+ return 1 #success
+}
+
+function_exists __pom_hierarchy ||
+__pom_hierarchy()
+{
+ local pom=`_realpath "pom.xml"`
+ POM_HIERARCHY+=("$pom")
+ while [ -n "$pom" ] && grep -q "" "$pom"; do
+ ## look for a new relativePath for parent pom.xml
+ local parent_pom_relative=`grep -e ".*" "$pom" | sed 's/.*//' | sed 's/<\/relativePath>.*//g'`
+
+ ## is present but not defined, assume ../pom.xml
+ if [ -z "$parent_pom_relative" ]; then
+ parent_pom_relative="../pom.xml"
+ fi
+
+ ## if pom exists continue else break
+ parent_pom=`_realpath "${pom%/*}/$parent_pom_relative"`
+ if [ -n "$parent_pom" ]; then
+ pom=$parent_pom
+ else
+ break
+ fi
+ POM_HIERARCHY+=("$pom")
+ done
+}
+
+_mvnd()
+{
+ local cur prev
+ COMPREPLY=()
+ POM_HIERARCHY=()
+ __pom_hierarchy
+ _get_comp_words_by_ref -n : cur prev
+
+ local mvnd_opts="-1"
+ local mvnd_long_opts="--completion|--purge|--serial|--status|--stop"
+ local mvnd_properties="-Djava.home|-Dmaven.multiModuleProjectDirectory|-Dmaven.repo.local|-Dmaven.settings|-Dmvnd.builder|-Dmvnd.daemonStorage|-Dmvnd.debug|-Dmvnd.duplicateDaemonGracePeriod|-Dmvnd.enableAssertions|-Dmvnd.expirationCheckDelay|-Dmvnd.home|-Dmvnd.idleTimeout|-Dmvnd.jvmArgs|-Dmvnd.keepAlive|-Dmvnd.logPurgePeriod|-Dmvnd.logback|-Dmvnd.maxHeapSize|-Dmvnd.maxLostKeepAlive|-Dmvnd.minHeapSize|-Dmvnd.minThreads|-Dmvnd.noBuffering|-Dmvnd.noDaemon|-Dmvnd.propertiesPath|-Dmvnd.registry|-Dmvnd.rollingWindowSize|-Dmvnd.serial|-Dmvnd.threads|-Duser.dir|-Duser.home"
+ local opts="-am|-amd|-B|-C|-c|-cpu|-D|-e|-emp|-ep|-f|-fae|-ff|-fn|-gs|-h|-l|-N|-npr|-npu|-nsu|-o|-P|-pl|-q|-rf|-s|-T|-t|-U|-up|-V|-v|-X|${mvnd_opts}"
+ local long_opts="--also-make|--also-make-dependents|--batch-mode|--strict-checksums|--lax-checksums|--check-plugin-updates|--define|--errors|--encrypt-master-password|--encrypt-password|--file|--fail-at-end|--fail-fast|--fail-never|--global-settings|--help|--log-file|--non-recursive|--no-plugin-registry|--no-plugin-updates|--no-snapshot-updates|--offline|--activate-profiles|--projects|--quiet|--resume-from|--settings|--threads|--toolchains|--update-snapshots|--update-plugins|--show-version|--version|--debug|${mvnd_long_opts}"
+
+ local common_clean_lifecycle="pre-clean|clean|post-clean"
+ local common_default_lifecycle="validate|initialize|generate-sources|process-sources|generate-resources|process-resources|compile|process-classes|generate-test-sources|process-test-sources|generate-test-resources|process-test-resources|test-compile|process-test-classes|test|prepare-package|package|pre-integration-test|integration-test|post-integration-test|verify|install|deploy"
+ local common_site_lifecycle="pre-site|site|post-site|site-deploy"
+ local common_lifecycle_phases="${common_clean_lifecycle}|${common_default_lifecycle}|${common_site_lifecycle}"
+
+ local plugin_goals_appengine="appengine:backends_configure|appengine:backends_delete|appengine:backends_rollback|appengine:backends_start|appengine:backends_stop|appengine:backends_update|appengine:debug|appengine:devserver|appengine:devserver_start|appengine:devserver_stop|appengine:endpoints_get_client_lib|appengine:endpoints_get_discovery_doc|appengine:enhance|appengine:rollback|appengine:set_default_version|appengine:start_module_version|appengine:stop_module_version|appengine:update|appengine:update_cron|appengine:update_dos|appengine:update_indexes|appengine:update_queues|appengine:vacuum_indexes"
+ local plugin_goals_android="android:apk|android:apklib|android:clean|android:deploy|android:deploy-dependencies|android:dex|android:emulator-start|android:emulator-stop|android:emulator-stop-all|android:generate-sources|android:help|android:instrument|android:manifest-update|android:pull|android:push|android:redeploy|android:run|android:undeploy|android:unpack|android:version-update|android:zipalign|android:devices"
+ local plugin_goals_ant="ant:ant|ant:clean"
+ local plugin_goals_antrun="antrun:run"
+ local plugin_goals_archetype="archetype:generate|archetype:create-from-project|archetype:crawl"
+ local plugin_goals_assembly="assembly:single|assembly:assembly"
+ local plugin_goals_build_helper="build-helper:add-resource|build-helper:add-source|build-helper:add-test-resource|build-helper:add-test-source|build-helper:attach-artifact|build-helper:bsh-property|build-helper:cpu-count|build-helper:help|build-helper:local-ip|build-helper:maven-version|build-helper:parse-version|build-helper:regex-properties|build-helper:regex-property|build-helper:released-version|build-helper:remove-project-artifact|build-helper:reserve-network-port|build-helper:timestamp-property"
+ local plugin_goals_buildnumber="buildnumber:create|buildnumber:create-timestamp|buildnumber:help|buildnumber:hgchangeset"
+ local plugin_goals_cargo="cargo:start|cargo:run|cargo:stop|cargo:deploy|cargo:undeploy|cargo:help"
+ local plugin_goals_checkstyle="checkstyle:checkstyle|checkstyle:check"
+ local plugin_goals_cobertura="cobertura:cobertura"
+ local plugin_goals_findbugs="findbugs:findbugs|findbugs:gui|findbugs:help"
+ local plugin_goals_dependency="dependency:analyze|dependency:analyze-dep-mgt|dependency:analyze-duplicate|dependency:analyze-only|dependency:analyze-report|dependency:build-classpath|dependency:copy|dependency:copy-dependencies|dependency:get|dependency:go-offline|dependency:help|dependency:list|dependency:list-repositories|dependency:properties|dependency:purge-local-repository|dependency:resolve|dependency:resolve-plugins|dependency:sources|dependency:tree|dependency:unpack|dependency:unpack-dependencies"
+ local plugin_goals_deploy="deploy:deploy-file"
+ local plugin_goals_ear="ear:ear|ear:generate-application-xml"
+ local plugin_goals_eclipse="eclipse:clean|eclipse:eclipse"
+ local plugin_goals_ejb="ejb:ejb"
+ local plugin_goals_enforcer="enforcer:enforce|enforcer:display-info"
+ local plugin_goals_exec="exec:exec|exec:java"
+ local plugin_goals_failsafe="failsafe:integration-test|failsafe:verify"
+ local plugin_goals_flyway="flyway:migrate|flyway:clean|flyway:info|flyway:validate|flyway:baseline|flyway:repair"
+ local plugin_goals_gpg="gpg:sign|gpg:sign-and-deploy-file"
+ local plugin_goals_grails="grails:clean|grails:config-directories|grails:console|grails:create-controller|grails:create-domain-class|grails:create-integration-test|grails:create-pom|grails:create-script|grails:create-service|grails:create-tag-lib|grails:create-unit-test|grails:exec|grails:generate-all|grails:generate-controller|grails:generate-views|grails:help|grails:init|grails:init-plugin|grails:install-templates|grails:list-plugins|grails:maven-clean|grails:maven-compile|grails:maven-functional-test|grails:maven-grails-app-war|grails:maven-test|grails:maven-war|grails:package|grails:package-plugin|grails:run-app|grails:run-app-https|grails:run-war|grails:set-version|grails:test-app|grails:upgrade|grails:validate|grails:validate-plugin|grails:war"
+ local plugin_goals_gwt="gwt:browser|gwt:clean|gwt:compile|gwt:compile-report|gwt:css|gwt:debug|gwt:eclipse|gwt:eclipseTest|gwt:generateAsync|gwt:help|gwt:i18n|gwt:mergewebxml|gwt:resources|gwt:run|gwt:run-codeserver|gwt:sdkInstall|gwt:source-jar|gwt:soyc|gwt:test"
+ local plugin_goals_help="help:active-profiles|help:all-profiles|help:describe|help:effective-pom|help:effective-settings|help:evaluate|help:expressions|help:help|help:system"
+ local plugin_goals_hibernate3="hibernate3:hbm2ddl|hibernate3:help"
+ local plugin_goals_idea="idea:clean|idea:idea"
+ local plugin_goals_install="install:install-file"
+ local plugin_goals_jacoco="jacoco:check|jacoco:dump|jacoco:help|jacoco:instrument|jacoco:merge|jacoco:prepare-agent|jacoco:prepare-agent-integration|jacoco:report|jacoco:report-integration|jacoco:restore-instrumented-classes"
+ local plugin_goals_javadoc="javadoc:javadoc|javadoc:jar|javadoc:aggregate"
+ local plugin_goals_jboss="jboss:start|jboss:stop|jboss:deploy|jboss:undeploy|jboss:redeploy"
+ local plugin_goals_jboss_as="jboss-as:add-resource|jboss-as:deploy|jboss-as:deploy-only|jboss-as:deploy-artifact|jboss-as:redeploy|jboss-as:redeploy-only|jboss-as:undeploy|jboss-as:undeploy-artifact|jboss-as:run|jboss-as:start|jboss-as:shutdown|jboss-as:execute-commands"
+ local plugin_goals_jetty="jetty:run|jetty:run-war|jetty:run-exploded|jetty:deploy-war|jetty:run-forked|jetty:start|jetty:stop|jetty:effective-web-xml"
+ local plugin_goals_jxr="jxr:jxr"
+ local plugin_goals_license="license:format|license:check"
+ local plugin_goals_liquibase="liquibase:changelogSync|liquibase:changelogSyncSQL|liquibase:clearCheckSums|liquibase:dbDoc|liquibase:diff|liquibase:dropAll|liquibase:help|liquibase:migrate|liquibase:listLocks|liquibase:migrateSQL|liquibase:releaseLocks|liquibase:rollback|liquibase:rollbackSQL|liquibase:status|liquibase:tag|liquibase:update|liquibase:updateSQL|liquibase:updateTestingRollback"
+ local plugin_goals_nexus_staging="nexus-staging:close|nexus-staging:deploy|nexus-staging:deploy-staged|nexus-staging:deploy-staged-repository|nexus-staging:drop|nexus-staging:help|nexus-staging:promote|nexus-staging:rc-close|nexus-staging:rc-drop|nexus-staging:rc-list|nexus-staging:rc-list-profiles|nexus-staging:rc-promote|nexus-staging:rc-release|nexus-staging:release"
+ local plugin_goals_pmd="pmd:pmd|pmd:cpd|pmd:check|pmd:cpd-check"
+ local plugin_goals_properties="properties:read-project-properties|properties:write-project-properties|properties:write-active-profile-properties|properties:set-system-properties"
+ local plugin_goals_release="release:clean|release:prepare|release:rollback|release:perform|release:stage|release:branch|release:update-versions"
+ local plugin_goals_repository="repository:bundle-create|repository:bundle-pack|repository:help"
+ local plugin_goals_scala="scala:add-source|scala:cc|scala:cctest|scala:compile|scala:console|scala:doc|scala:doc-jar|scala:help|scala:run|scala:script|scala:testCompile"
+ local plugin_goals_scm="scm:add|scm:checkin|scm:checkout|scm:update|scm:status"
+ local plugin_goals_site="site:site|site:deploy|site:run|site:stage|site:stage-deploy"
+ local plugin_goals_sonar="sonar:sonar|sonar:help"
+ local plugin_goals_source="source:aggregate|source:jar|source:jar-no-fork"
+ local plugin_goals_spotbugs="spotbugs:spotbugs|spotbugs:check|spotbugs:gui|spotbugs:help"
+ local plugin_goals_surefire="surefire:test"
+ local plugin_goals_tomcat6="tomcat6:help|tomcat6:run|tomcat6:run-war|tomcat6:run-war-only|tomcat6:stop|tomcat6:deploy|tomcat6:redeploy|tomcat6:undeploy"
+ local plugin_goals_tomcat7="tomcat7:help|tomcat7:run|tomcat7:run-war|tomcat7:run-war-only|tomcat7:deploy|tomcat7:redeploy|tomcat7:undeploy"
+ local plugin_goals_tomcat="tomcat:help|tomcat:start|tomcat:stop|tomcat:deploy|tomcat:undeploy"
+ local plugin_goals_liberty="liberty:create-server|liberty:start-server|liberty:stop-server|liberty:run-server|liberty:deploy|liberty:undeploy|liberty:java-dump-server|liberty:dump-server|liberty:package-server"
+ local plugin_goals_versions="versions:display-dependency-updates|versions:display-plugin-updates|versions:display-property-updates|versions:update-parent|versions:update-properties|versions:update-child-modules|versions:lock-snapshots|versions:unlock-snapshots|versions:resolve-ranges|versions:set|versions:use-releases|versions:use-next-releases|versions:use-latest-releases|versions:use-next-snapshots|versions:use-latest-snapshots|versions:use-next-versions|versions:use-latest-versions|versions:commit|versions:revert"
+ local plugin_goals_vertx="vertx:init|vertx:runMod|vertx:pullInDeps|vertx:fatJar"
+ local plugin_goals_war="war:war|war:exploded|war:inplace|war:manifest"
+ local plugin_goals_spring_boot="spring-boot:run|spring-boot:repackage"
+ local plugin_goals_jgitflow="jgitflow:feature-start|jgitflow:feature-finish|jgitflow:release-start|jgitflow:release-finish|jgitflow:hotfix-start|jgitflow:hotfix-finish|jgitflow:build-number"
+ local plugin_goals_wildfly="wildfly:add-resource|wildfly:deploy|wildfly:deploy-only|wildfly:deploy-artifact|wildfly:redeploy|wildfly:redeploy-only|wildfly:undeploy|wildfly:undeploy-artifact|wildfly:run|wildfly:start|wildfly:shutdown|wildfly:execute-commands"
+ local plugin_goals_formatter="formatter:format|formatter:help|formatter:validate"
+
+ ## some plugin (like jboss-as) has '-' which is not allowed in shell var name, to use '_' then replace
+ local common_plugins=`compgen -v | grep "^plugin_goals_.*" | sed 's/plugin_goals_//g' | tr '_' '-' | tr '\n' '|'`
+
+ local options="-Dmaven.test.skip=true|-DskipTests|-DskipITs|-Dtest|-Dit.test|-DfailIfNoTests|-Dmaven.surefire.debug|-DenableCiProfile|-Dpmd.skip=true|-Dcheckstyle.skip=true|-Dtycho.mode=maven|-Dmaven.javadoc.skip=true|-Dgwt.compiler.skip|-Dcobertura.skip=true|-Dfindbugs.skip=true||-DperformRelease=true|-Dgpg.skip=true|-DforkCount|${mvnd_properties}"
+
+ local profile_settings=`[ -e ~/.m2/settings.xml ] && grep -e "" -A 1 ~/.m2/settings.xml | grep -e ".*" | sed 's/.*//' | sed 's/<\/id>.*//g' | tr '\n' '|' `
+
+ local profiles="${profile_settings}|"
+ for item in ${POM_HIERARCHY[*]}
+ do
+ local profile_pom=`[ -e $item ] && grep -e "" -A 1 $item | grep -e ".*" | sed 's/.*//' | sed 's/<\/id>.*//g' | tr '\n' '|' `
+ local profiles="${profiles}|${profile_pom}"
+ done
+
+ local IFS=$'|\n'
+
+ if [[ ${cur} == -D* ]] ; then
+ COMPREPLY=( $(compgen -S ' ' -W "${options}" -- ${cur}) )
+
+ elif [[ ${prev} == -P ]] ; then
+ if [[ ${cur} == *,* ]] ; then
+ COMPREPLY=( $(compgen -S ',' -W "${profiles}" -P "${cur%,*}," -- ${cur##*,}) )
+ else
+ COMPREPLY=( $(compgen -S ',' -W "${profiles}" -- ${cur}) )
+ fi
+
+ elif [[ ${cur} == --* ]] ; then
+ COMPREPLY=( $(compgen -W "${long_opts}" -S ' ' -- ${cur}) )
+
+ elif [[ ${cur} == -* ]] ; then
+ COMPREPLY=( $(compgen -W "${opts}" -S ' ' -- ${cur}) )
+
+ elif [[ ${prev} == -pl ]] ; then
+ if [[ ${cur} == *,* ]] ; then
+ COMPREPLY=( $(compgen -W "$(__find_mvn_projects)" -S ',' -P "${cur%,*}," -- ${cur##*,}) )
+ else
+ COMPREPLY=( $(compgen -W "$(__find_mvn_projects)" -S ',' -- ${cur}) )
+ fi
+
+ elif [[ ${prev} == -rf || ${prev} == --resume-from ]] ; then
+ COMPREPLY=( $(compgen -d -S ' ' -- ${cur}) )
+
+ elif [[ ${cur} == *:* ]] ; then
+ local plugin
+ for plugin in $common_plugins; do
+ if [[ ${cur} == ${plugin}:* ]]; then
+ ## note that here is an 'unreplace', see the comment at common_plugins
+ var_name="plugin_goals_${plugin//-/_}"
+ COMPREPLY=( $(compgen -W "${!var_name}" -S ' ' -- ${cur}) )
+ fi
+ done
+
+ else
+ if echo "${common_lifecycle_phases}" | tr '|' '\n' | grep -q -e "^${cur}" ; then
+ COMPREPLY=( $(compgen -S ' ' -W "${common_lifecycle_phases}" -- ${cur}) )
+ elif echo "${common_plugins}" | tr '|' '\n' | grep -q -e "^${cur}"; then
+ COMPREPLY=( $(compgen -S ':' -W "${common_plugins}" -- ${cur}) )
+ fi
+ fi
+
+ __ltrim_colon_completions "$cur"
+}
+
+#complete -o default -F _mvn -o nospace mvn
+#complete -o default -F _mvn -o nospace mvnDebug
+#complete -o default -F _mvn -o nospace mvnw
+
+complete -o default -F _mvnd -o nospace mvnd
diff --git a/client/src/test/java/org/mvndaemon/mvnd/client/CompletionGenerator.java b/client/src/test/java/org/mvndaemon/mvnd/client/CompletionGenerator.java
new file mode 100644
index 00000000..84920086
--- /dev/null
+++ b/client/src/test/java/org/mvndaemon/mvnd/client/CompletionGenerator.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.client;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
+import org.mvndaemon.mvnd.common.Environment;
+import org.mvndaemon.mvnd.common.Environment.OptionOrigin;
+import org.mvndaemon.mvnd.common.IoUtils;
+
+public class CompletionGenerator {
+
+ @Test
+ void generate() throws IOException {
+
+ String template = IoUtils.readResource(Completion.class.getClassLoader(),
+ "completion-templates/mvnd-bash-completion.bash");
+
+ final String shortOpts = Stream.of(Environment.values())
+ .filter(env -> !env.isInternal())
+ .flatMap(env -> env.getOptionMap().entrySet().stream())
+ .filter(optEntry -> optEntry.getValue() == OptionOrigin.mvnd)
+ .map(Map.Entry::getKey)
+ .filter(opt -> !opt.startsWith("--"))
+ .sorted()
+ .collect(Collectors.joining("|"));
+
+ final String longOpts = Stream.of(Environment.values())
+ .filter(env -> !env.isInternal())
+ .flatMap(env -> env.getOptionMap().entrySet().stream())
+ .filter(optEntry -> optEntry.getValue() == OptionOrigin.mvnd)
+ .map(Map.Entry::getKey)
+ .filter(opt -> opt.startsWith("--"))
+ .sorted()
+ .collect(Collectors.joining("|"));
+
+ final String props = Stream.of(Environment.values())
+ .filter(env -> !env.isInternal())
+ .map(Environment::getProperty)
+ .filter(Objects::nonNull)
+ .sorted()
+ .map(prop -> "-D" + prop)
+ .collect(Collectors.joining("|"));
+
+ template = template.replace("%mvnd_opts%", shortOpts);
+ template = template.replace("%mvnd_long_opts%", longOpts);
+ template = template.replace("%mvnd_properties%", props);
+
+ final Path baseDir = Paths.get(System.getProperty("project.basedir", "."));
+
+ final byte[] bytes = template.getBytes(StandardCharsets.UTF_8);
+ Files.write(baseDir.resolve("src/main/resources/mvnd-bash-completion.bash"), bytes);
+ Files.write(baseDir.resolve("target/classes/mvnd-bash-completion.bash"), bytes);
+
+ }
+
+}
diff --git a/client/src/test/resources/completion-templates/mvnd-bash-completion.bash b/client/src/test/resources/completion-templates/mvnd-bash-completion.bash
index 5e0e3feb..a69835fe 100644
--- a/client/src/test/resources/completion-templates/mvnd-bash-completion.bash
+++ b/client/src/test/resources/completion-templates/mvnd-bash-completion.bash
@@ -1,3 +1,22 @@
+#
+# Copyright 2019 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Adapted from https://github.com/juven/maven-bash-completion/blob/master/bash_completion.bash by Juven Xu and others
+# under Apache License Version 2.0
+
function_exists()
{
declare -F $1 > /dev/null
@@ -118,7 +137,7 @@ __pom_hierarchy()
done
}
-_mvn()
+_mvnd()
{
local cur prev
COMPREPLY=()
@@ -126,8 +145,11 @@ _mvn()
__pom_hierarchy
_get_comp_words_by_ref -n : cur prev
- local opts="-am|-amd|-B|-C|-c|-cpu|-D|-e|-emp|-ep|-f|-fae|-ff|-fn|-gs|-h|-l|-N|-npr|-npu|-nsu|-o|-P|-pl|-q|-rf|-s|-T|-t|-U|-up|-V|-v|-X"
- local long_opts="--also-make|--also-make-dependents|--batch-mode|--strict-checksums|--lax-checksums|--check-plugin-updates|--define|--errors|--encrypt-master-password|--encrypt-password|--file|--fail-at-end|--fail-fast|--fail-never|--global-settings|--help|--log-file|--non-recursive|--no-plugin-registry|--no-plugin-updates|--no-snapshot-updates|--offline|--activate-profiles|--projects|--quiet|--resume-from|--settings|--threads|--toolchains|--update-snapshots|--update-plugins|--show-version|--version|--debug"
+ local mvnd_opts="%mvnd_opts%"
+ local mvnd_long_opts="%mvnd_long_opts%"
+ local mvnd_properties="%mvnd_properties%"
+ local opts="-am|-amd|-B|-C|-c|-cpu|-D|-e|-emp|-ep|-f|-fae|-ff|-fn|-gs|-h|-l|-N|-npr|-npu|-nsu|-o|-P|-pl|-q|-rf|-s|-T|-t|-U|-up|-V|-v|-X|${mvnd_opts}"
+ local long_opts="--also-make|--also-make-dependents|--batch-mode|--strict-checksums|--lax-checksums|--check-plugin-updates|--define|--errors|--encrypt-master-password|--encrypt-password|--file|--fail-at-end|--fail-fast|--fail-never|--global-settings|--help|--log-file|--non-recursive|--no-plugin-registry|--no-plugin-updates|--no-snapshot-updates|--offline|--activate-profiles|--projects|--quiet|--resume-from|--settings|--threads|--toolchains|--update-snapshots|--update-plugins|--show-version|--version|--debug|${mvnd_long_opts}"
local common_clean_lifecycle="pre-clean|clean|post-clean"
local common_default_lifecycle="validate|initialize|generate-sources|process-sources|generate-resources|process-resources|compile|process-classes|generate-test-sources|process-test-sources|generate-test-resources|process-test-resources|test-compile|process-test-classes|test|prepare-package|package|pre-integration-test|integration-test|post-integration-test|verify|install|deploy"
@@ -197,7 +219,7 @@ _mvn()
## some plugin (like jboss-as) has '-' which is not allowed in shell var name, to use '_' then replace
local common_plugins=`compgen -v | grep "^plugin_goals_.*" | sed 's/plugin_goals_//g' | tr '_' '-' | tr '\n' '|'`
- local options="-Dmaven.test.skip=true|-DskipTests|-DskipITs|-Dtest|-Dit.test|-DfailIfNoTests|-Dmaven.surefire.debug|-DenableCiProfile|-Dpmd.skip=true|-Dcheckstyle.skip=true|-Dtycho.mode=maven|-Dmaven.javadoc.skip=true|-Dgwt.compiler.skip|-Dcobertura.skip=true|-Dfindbugs.skip=true||-DperformRelease=true|-Dgpg.skip=true|-DforkCount"
+ local options="-Dmaven.test.skip=true|-DskipTests|-DskipITs|-Dtest|-Dit.test|-DfailIfNoTests|-Dmaven.surefire.debug|-DenableCiProfile|-Dpmd.skip=true|-Dcheckstyle.skip=true|-Dtycho.mode=maven|-Dmaven.javadoc.skip=true|-Dgwt.compiler.skip|-Dcobertura.skip=true|-Dfindbugs.skip=true||-DperformRelease=true|-Dgpg.skip=true|-DforkCount|${mvnd_properties}"
local profile_settings=`[ -e ~/.m2/settings.xml ] && grep -e "" -A 1 ~/.m2/settings.xml | grep -e ".*" | sed 's/.*//' | sed 's/<\/id>.*//g' | tr '\n' '|' `
@@ -257,6 +279,8 @@ _mvn()
__ltrim_colon_completions "$cur"
}
-complete -o default -F _mvn -o nospace mvn
-complete -o default -F _mvn -o nospace mvnDebug
-complete -o default -F _mvn -o nospace mvnw
+#complete -o default -F _mvn -o nospace mvn
+#complete -o default -F _mvn -o nospace mvnDebug
+#complete -o default -F _mvn -o nospace mvnw
+
+complete -o default -F _mvnd -o nospace mvnd
diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java
index 4a6584d5..e8c810fa 100644
--- a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java
+++ b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java
@@ -20,15 +20,17 @@ import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
-import java.util.List;
+import java.util.LinkedHashMap;
import java.util.Locale;
+import java.util.Map;
import java.util.Optional;
import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@@ -42,16 +44,20 @@ import java.util.stream.Stream;
*/
public enum Environment {
+ /**
+ * Print the completion for the given shell to stdout. Only --completion bash
is supported at this time.
+ */
+ COMPLETION(null, null, null, OptionType.STRING, Flags.OPTIONAL, "mvnd:--completion"),
/**
* Delete log files under the mvnd.registry
directory that are older than mvnd.logPurgePeriod
*/
- PURGE(null, null, null, OptionType.VOID, Flags.OPTIONAL, "--purge"),
+ PURGE(null, null, null, OptionType.VOID, Flags.OPTIONAL, "mvnd:--purge"),
/** Prints the status of daemon instances registered in the registry specified by mvnd.registry
*/
- STATUS(null, null, null, OptionType.VOID, Flags.OPTIONAL, "--status"),
+ STATUS(null, null, null, OptionType.VOID, Flags.OPTIONAL, "mvnd:--status"),
/** Stop all daemon instances registered in the registry specified by mvnd.registry
*/
- STOP(null, null, null, OptionType.VOID, Flags.OPTIONAL, "--stop"),
+ STOP(null, null, null, OptionType.VOID, Flags.OPTIONAL, "mvnd:--stop"),
/** Use one thread, no log buffering and the default project builder to behave like a standard maven */
- SERIAL("mvnd.serial", null, Boolean.FALSE, OptionType.VOID, Flags.OPTIONAL, "--serial"),
+ SERIAL("mvnd.serial", null, Boolean.FALSE, OptionType.VOID, Flags.OPTIONAL, "mvnd:-1", "mvnd:--serial"),
//
// Log properties
@@ -85,21 +91,21 @@ public enum Environment {
/** The path to the Maven local repository */
MAVEN_REPO_LOCAL("maven.repo.local", null, null, OptionType.PATH, Flags.NONE),
/** The location of the maven settings file */
- MAVEN_SETTINGS("maven.settings", null, null, OptionType.PATH, Flags.NONE, "-s", "--settings"),
+ MAVEN_SETTINGS("maven.settings", null, null, OptionType.PATH, Flags.NONE, "mvn:-s", "mvn:--settings"),
/** The root directory of the current multi module Maven project */
MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null, null, OptionType.PATH, Flags.NONE),
/** Log file */
- MAVEN_LOG_FILE(null, null, null, OptionType.PATH, Flags.INTERNAL, "-l", "--log-file"),
+ MAVEN_LOG_FILE(null, null, null, OptionType.PATH, Flags.INTERNAL, "mvn:-l", "mvn:--log-file"),
/** Batch mode */
- MAVEN_BATCH_MODE(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "-B", "--batch-mode"),
+ MAVEN_BATCH_MODE(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "mvn:-B", "mvn:--batch-mode"),
/** Debug */
- MAVEN_DEBUG(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "-X", "--debug"),
+ MAVEN_DEBUG(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "mvn:-X", "mvn:--debug"),
/** Version */
- MAVEN_VERSION(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "-v", "-version", "--version"),
+ MAVEN_VERSION(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "mvn:-v", "mvn:-version", "mvn:--version"),
/** Show version */
- MAVEN_SHOW_VERSION(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "-V", "--show-version"),
+ MAVEN_SHOW_VERSION(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "mvn:-V", "mvn:--show-version"),
/** Define */
- MAVEN_DEFINE(null, null, null, OptionType.STRING, Flags.INTERNAL, "-D", "--define"),
+ MAVEN_DEFINE(null, null, null, OptionType.STRING, Flags.INTERNAL, "mvn:-D", "mvn:--define"),
//
// mvnd properties
@@ -171,11 +177,11 @@ public enum Environment {
* The number of threads to pass to the daemon; same syntax as Maven's -T
/--threads
* option.
*/
- MVND_THREADS("mvnd.threads", null, null, OptionType.STRING, Flags.NONE, "-T", "--threads"),
+ MVND_THREADS("mvnd.threads", null, null, OptionType.STRING, Flags.NONE, "mvn:-T", "mvn:--threads"),
/**
* The builder implementation the daemon should use
*/
- MVND_BUILDER("mvnd.builder", null, "smart", OptionType.STRING, Flags.NONE, "-b", "--builder"),
+ MVND_BUILDER("mvnd.builder", null, "smart", OptionType.STRING, Flags.NONE, "mvn:-b", "mvn:--builder"),
/**
* An ID for a newly started daemon
*/
@@ -239,7 +245,7 @@ public enum Environment {
private final String default_;
private final int flags;
private final OptionType type;
- private final List options;
+ private final Map options;
Environment(String property, String environmentVariable, Object default_, OptionType type, int flags,
String... options) {
@@ -252,7 +258,25 @@ public enum Environment {
this.default_ = default_ != null ? default_.toString() : null;
this.flags = flags;
this.type = type;
- this.options = options.length == 0 ? Collections.emptyList() : Collections.unmodifiableList(Arrays.asList(options));
+ if (options.length == 0) {
+ this.options = Collections.emptyMap();
+ } else {
+ final Map optMap = new LinkedHashMap<>();
+ for (String opt : options) {
+ OPTION_ORIGIN_SEARCH: {
+ for (OptionOrigin oo : OptionOrigin.values()) {
+ if (opt.startsWith(oo.prefix)) {
+ optMap.put(opt.substring(oo.prefix.length()), oo);
+ break OPTION_ORIGIN_SEARCH;
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unexpected option prefix: '" + opt + "'; Options should start with any of "
+ + Stream.of(OptionOrigin.values()).map(oo -> oo.prefix).collect(Collectors.joining(",")));
+ }
+ }
+ this.options = Collections.unmodifiableMap(optMap);
+ }
}
public String getProperty() {
@@ -267,7 +291,11 @@ public enum Environment {
return default_;
}
- public List getOptions() {
+ public Set getOptions() {
+ return options.keySet();
+ }
+
+ public Map getOptionMap() {
return options;
}
@@ -332,7 +360,7 @@ public enum Environment {
public void addCommandLineOption(Collection args, String value) {
if (!options.isEmpty()) {
- args.add(options.get(0));
+ args.add(options.keySet().iterator().next());
args.add(type.normalize(value));
} else {
args.add("-D" + property + "=" + type.normalize(value));
@@ -379,10 +407,10 @@ public enum Environment {
prefixes = new String[] { "-D" + property + "=" };
} else if (property != null) {
prefixes = new String[options.size() + 1];
- options.toArray(prefixes);
+ options.keySet().toArray(prefixes);
prefixes[options.size()] = "-D" + property + "=";
} else {
- prefixes = options.toArray(new String[0]);
+ prefixes = options.keySet().toArray(new String[0]);
}
return prefixes;
}
@@ -412,10 +440,20 @@ public enum Environment {
return Stream.of(values)
.filter(env -> !env.isInternal())
.sorted(Comparator. comparing(env -> env.property != null ? env.property : "")
- .thenComparing(env -> !env.options.isEmpty() ? env.options.get(0) : ""))
+ .thenComparing(env -> !env.options.isEmpty() ? env.options.keySet().iterator().next() : ""))
.map(env -> new DocumentedEnumEntry<>(env, props.getProperty(env.name())));
}
+ public enum OptionOrigin {
+ mvn, mvnd;
+
+ private final String prefix;
+
+ private OptionOrigin() {
+ this.prefix = name() + ":";
+ }
+ }
+
public static class DocumentedEnumEntry {
private final E entry;
@@ -439,7 +477,7 @@ public enum Environment {
private static final int NONE = 0b0;
private static final int DISCRIMINATING = 0b1;
private static final int INTERNAL = 0b10;
- public static final int OPTIONAL = 0b100;
+ private static final int OPTIONAL = 0b100;
}
}
diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/IoUtils.java b/common/src/main/java/org/mvndaemon/mvnd/common/IoUtils.java
new file mode 100644
index 00000000..0d7f0555
--- /dev/null
+++ b/common/src/main/java/org/mvndaemon/mvnd/common/IoUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.common;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+
+public class IoUtils {
+ public static String readResource(ClassLoader cl, String resourcePath) {
+ final StringBuilder result = new StringBuilder();
+ final int bufSize = 1024;
+ try (Reader in = new BufferedReader(
+ new InputStreamReader(
+ cl.getResourceAsStream(resourcePath),
+ StandardCharsets.UTF_8),
+ bufSize)) {
+ int len = 0;
+ char[] buf = new char[bufSize];
+ while ((len = in.read(buf)) >= 0) {
+ result.append(buf, 0, len);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Could not read a class path resource: " + resourcePath, e);
+ }
+ return result.toString();
+
+ }
+}
diff --git a/daemon/src/main/java/org/apache/maven/cli/MvndHelpFormatter.java b/daemon/src/main/java/org/apache/maven/cli/MvndHelpFormatter.java
index c5c1e7d4..d92f6e30 100644
--- a/daemon/src/main/java/org/apache/maven/cli/MvndHelpFormatter.java
+++ b/daemon/src/main/java/org/apache/maven/cli/MvndHelpFormatter.java
@@ -20,8 +20,8 @@ import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
-import java.util.List;
import java.util.Locale;
+import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -90,7 +90,7 @@ public class MvndHelpFormatter {
}
}
- final List opts = env.getOptions();
+ final Set opts = env.getOptions();
if (!opts.isEmpty()) {
if (property != null) {
help.append(';');
diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/CompletionNativeIT.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/CompletionNativeIT.java
new file mode 100644
index 00000000..11b21067
--- /dev/null
+++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/CompletionNativeIT.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.it;
+
+import java.io.IOException;
+import javax.inject.Inject;
+import org.junit.jupiter.api.Test;
+import org.mvndaemon.mvnd.assertj.TestClientOutput;
+import org.mvndaemon.mvnd.client.Client;
+import org.mvndaemon.mvnd.junit.MvndNativeTest;
+import org.mvndaemon.mvnd.junit.MvndTestExtension;
+
+@MvndNativeTest(projectDir = MvndTestExtension.TEMP_EXTERNAL)
+public class CompletionNativeIT {
+
+ @Inject
+ Client client;
+
+ @Test
+ void completionBash() throws IOException, InterruptedException {
+ final TestClientOutput output = new TestClientOutput();
+
+ client.execute(output, "--completion", "bash").assertSuccess();
+
+ output.assertContainsMatchingSubsequence("mvnd_opts=\"[^\"]*-1[^\"]*\"");
+ output.assertContainsMatchingSubsequence("mvnd_long_opts=\"[^\"]*--purge[^\"]*\"");
+ output.assertContainsMatchingSubsequence("mvnd_properties=\"[^\"]*-Dmvnd.debug[^\"]*\"");
+ }
+
+ protected boolean isNative() {
+ return true;
+ }
+}
diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/CompletionTest.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/CompletionTest.java
new file mode 100644
index 00000000..2d915c14
--- /dev/null
+++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/CompletionTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.it;
+
+import org.mvndaemon.mvnd.junit.MvndTest;
+import org.mvndaemon.mvnd.junit.MvndTestExtension;
+
+@MvndTest(projectDir = MvndTestExtension.TEMP_EXTERNAL)
+public class CompletionTest extends CompletionNativeIT {
+}
diff --git a/pom.xml b/pom.xml
index f3694daf..ab19e457 100644
--- a/pom.xml
+++ b/pom.xml
@@ -259,6 +259,7 @@ limitations under the License.
pom.xml.versionsBackup
+ SCRIPT_STYLE
SLASHSTAR_STYLE
SLASHSTAR_STYLE
SCRIPT_STYLE