Skip to content

Commit fc1b013

Browse files
authored
Merge pull request #173 from rsteube/add-maven
added maven
2 parents baf8351 + d5fc4be commit fc1b013

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package action
2+
3+
import (
4+
"archive/zip"
5+
"encoding/xml"
6+
"fmt"
7+
"io/ioutil"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
12+
"github.com/mitchellh/go-homedir"
13+
"github.com/rsteube/carapace"
14+
)
15+
16+
type Plugin struct {
17+
XMLName xml.Name `xml:"plugin"`
18+
GoalPrefix string `xml:"goalPrefix"`
19+
Mojos []struct {
20+
Goal string `xml:"goal"`
21+
Description string `xml:"description"`
22+
} `xml:"mojos>mojo"`
23+
}
24+
25+
func (p Plugin) FormattedGoals() map[string]string {
26+
goals := make(map[string]string)
27+
for _, mojo := range p.Mojos {
28+
goal := fmt.Sprintf("%v:%v", p.GoalPrefix, mojo.Goal)
29+
description := strings.SplitAfter(mojo.Description, ".")[0]
30+
if len(description) > 60 {
31+
description = description[:57] + "..."
32+
}
33+
goals[goal] = description
34+
}
35+
return goals
36+
}
37+
38+
type Artifact struct {
39+
GroupId string `xml:"groupId"`
40+
ArtifactId string `xml:"artifactId"`
41+
Version string `xml:"version"`
42+
}
43+
44+
func (a Artifact) Location(repository string) string {
45+
return fmt.Sprintf("%v/%v/%v/%v/%v-%v.jar", repository, strings.Replace(a.GroupId, ".", "/", -1), a.ArtifactId, a.Version, a.ArtifactId, a.Version)
46+
}
47+
48+
// TODO parent pom plugins
49+
// TODO plugins locatad in pluginmanagement and profiles
50+
type Project struct {
51+
XMLName xml.Name `xml:"project"`
52+
Plugins []Artifact `xml:"build>plugins>plugin"`
53+
Profiles []string `xml:"profiles>profile>id"`
54+
}
55+
56+
func repositoryLocation() string {
57+
// TODO environment variable / settings override
58+
if repoLocation, err := homedir.Expand("~/.m2/repository"); err == nil {
59+
return repoLocation
60+
}
61+
return "" // TODO handle error
62+
}
63+
64+
// TODO caching
65+
func ActionGoalsAndPhases(file string) carapace.Action {
66+
return carapace.ActionCallback(func(args []string) carapace.Action {
67+
if project, err := loadProject(file); err != nil {
68+
return carapace.ActionMessage(err.Error())
69+
} else {
70+
goals := make(map[string]string)
71+
for _, plugin := range project.Plugins {
72+
if plugin := loadPlugin(plugin.Location(repositoryLocation())); plugin != nil {
73+
for key, value := range plugin.FormattedGoals() {
74+
goals[key] = value
75+
}
76+
}
77+
}
78+
79+
for key, value := range defaultGoalsAndPhases() {
80+
goals[key] = value
81+
}
82+
83+
vals := make([]string, 0)
84+
for key, value := range goals {
85+
vals = append(vals, key, value)
86+
}
87+
88+
return carapace.ActionValuesDescribed(vals...)
89+
}
90+
})
91+
}
92+
93+
func defaultGoalsAndPhases() (goals map[string]string) {
94+
goals = map[string]string{
95+
// clean lifecycle
96+
"pre-clean": "execute processes needed prior to the actual project cleaning",
97+
"clean": "remove all files generated by the previous build",
98+
"post-clean": "execute processes needed to finalize the project cleanin",
99+
100+
// default lifecycle
101+
"validate": "validate the project is correct and all necessary information is available.",
102+
"initialize": "initialize build state, e.g. set properties or create directories.",
103+
"generate-sources": "generate any source code for inclusion in compilation.",
104+
"process-sources": "process the source code, for example to filter any values.",
105+
"generate-resources": "generate resources for inclusion in the package.",
106+
"process-resources": "copy and process the resources into the destination directory, ready for packaging.",
107+
"compile": "compile the source code of the project.",
108+
"process-classes": "post-process the generated files from compilation.",
109+
"generate-test-sources": "generate any test source code for inclusion in compilation.",
110+
"process-test-sources": "process the test source code, for example to filter any values.",
111+
"generate-test-resources": "create resources for testing.",
112+
"process-test-resources": "copy and process the resources into the test destination directory.",
113+
"test-compile": "compile the test source code into the test destination directory",
114+
"process-test-classes": "post-process the generated files from test compilation.",
115+
"test": "run tests using a suitable unit testing framework.",
116+
"prepare-package": "perform any operations necessary to prepare a package before the actual packaging.",
117+
"package": "take the compiled code and package it in its distributable format, such as a JAR.",
118+
"pre-integration-test": "perform actions required before integration tests are executed.",
119+
"integration-test": "process and deploy the package into an environment where integration tests can be run.",
120+
"post-integration-test": "perform actions required after integration tests have been executed.",
121+
"verify": "run any checks to verify the package is valid and meets quality criteria.",
122+
"install": "install the package into the local repository.",
123+
"deploy": "copies the final package to the remote repository.",
124+
125+
// site lifecycle
126+
"pre-site": "execute processes needed prior to the actual project site generation",
127+
"site": "generate the project's site documentation",
128+
"post-site": "execute processes needed to finalize the site generation",
129+
"site-deploy": "deploy the generated site documentation to the specified web server",
130+
}
131+
132+
// TODO this traverses over all versions of a plugin while only the latest should be used
133+
_ = filepath.Walk(repositoryLocation()+"/org/apache/maven/plugins/", func(path string, info os.FileInfo, err error) error {
134+
if err != nil {
135+
return err
136+
}
137+
if !info.IsDir() && strings.HasSuffix(info.Name(), ".jar") {
138+
if plugin := loadPlugin(path); plugin != nil {
139+
for key, value := range plugin.FormattedGoals() {
140+
goals[key] = value
141+
}
142+
}
143+
}
144+
return nil
145+
})
146+
return
147+
}
148+
149+
func ActionProfiles(file string) carapace.Action {
150+
return carapace.ActionCallback(func(args []string) carapace.Action {
151+
if project, err := loadProject(file); err != nil {
152+
return carapace.ActionMessage(err.Error())
153+
} else {
154+
return carapace.ActionValues(project.Profiles...)
155+
}
156+
})
157+
}
158+
159+
func locatePom(file string) (pom string) {
160+
if file != "" {
161+
return file
162+
}
163+
if wd, err := os.Getwd(); err == nil {
164+
for {
165+
pom = wd + "/pom.xml"
166+
if _, err := os.Stat(pom); err == nil || wd == "/" || wd == "." {
167+
break
168+
}
169+
wd = filepath.Dir(wd)
170+
}
171+
}
172+
return
173+
}
174+
175+
func loadProject(file string) (project *Project, err error) {
176+
var content []byte
177+
if content, err = ioutil.ReadFile(locatePom(file)); err == nil {
178+
err = xml.Unmarshal(content, &project)
179+
}
180+
return
181+
}
182+
183+
func loadPlugin(file string) (plugin *Plugin) {
184+
if reader, err := zip.OpenReader(file); err == nil {
185+
defer reader.Close()
186+
for _, f := range reader.File {
187+
if f.Name == "META-INF/maven/plugin.xml" {
188+
if pluginFile, err := f.Open(); err == nil {
189+
defer pluginFile.Close()
190+
if content, err := ioutil.ReadAll(pluginFile); err == nil {
191+
err = xml.Unmarshal(content, &plugin)
192+
}
193+
}
194+
}
195+
}
196+
}
197+
return
198+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package cmd
2+
3+
import (
4+
"github.com/rsteube/carapace"
5+
"github.com/rsteube/carapace-bin/completers/mvn_completer/cmd/action"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
var rootCmd = &cobra.Command{
10+
Use: "mvn",
11+
Short: "",
12+
Run: func(cmd *cobra.Command, args []string) {},
13+
}
14+
15+
func Execute() error {
16+
return rootCmd.Execute()
17+
}
18+
func init() {
19+
carapace.Gen(rootCmd).Standalone()
20+
21+
rootCmd.Flags().StringP("activate-profiles", "P", "", "Comma-delimited list of profiles")
22+
rootCmd.Flags().Bool("also-make", false, "If project list is specified, also")
23+
rootCmd.Flags().Bool("also-make-dependents", false, "If project list is specified, also")
24+
rootCmd.Flags().BoolP("batch-mode", "B", false, "Run in non-interactive (batch)")
25+
rootCmd.Flags().StringP("builder", "b", "", "The id of the build strategy to use`")
26+
rootCmd.Flags().Bool("check-plugin-updates", false, "Ineffective, only kept for")
27+
rootCmd.Flags().BoolP("debug", "X", false, "Produce execution debug output")
28+
rootCmd.Flags().StringP("define", "D", "", "Define a system property")
29+
rootCmd.Flags().String("encrypt-master-password", "", "Encrypt master security password")
30+
rootCmd.Flags().String("encrypt-password", "", "Encrypt server password")
31+
rootCmd.Flags().BoolP("errors", "e", false, "Produce execution error messages")
32+
rootCmd.Flags().Bool("fail-at-end", false, "Only fail the build afterwards;")
33+
rootCmd.Flags().Bool("fail-fast", false, "Stop at first failure in")
34+
rootCmd.Flags().Bool("fail-never", false, "NEVER fail the build, regardless")
35+
rootCmd.Flags().StringP("file", "f", "", "Force the use of an alternate POM")
36+
rootCmd.Flags().String("global-settings", "", "Alternate path for the global")
37+
rootCmd.Flags().String("global-toolchains", "", "Alternate path for the global")
38+
rootCmd.Flags().BoolP("help", "h", false, "Display help information")
39+
rootCmd.Flags().BoolP("lax-checksums", "c", false, "Warn if checksums don't match")
40+
rootCmd.Flags().Bool("legacy-local-repository", false, "Use Maven 2 Legacy Local")
41+
rootCmd.Flags().StringP("log-file", "l", "", "Log file where all build output")
42+
rootCmd.Flags().Bool("no-plugin-registry", false, "Ineffective, only kept for")
43+
rootCmd.Flags().Bool("no-plugin-updates", false, "Ineffective, only kept for")
44+
rootCmd.Flags().Bool("no-snapshot-updates", false, "Suppress SNAPSHOT updates")
45+
rootCmd.Flags().Bool("no-transfer-progress", false, "Do not display transfer progress")
46+
rootCmd.Flags().BoolP("non-recursive", "N", false, "Do not recurse into sub-projects")
47+
rootCmd.Flags().BoolP("offline", "o", false, "Work offline")
48+
rootCmd.Flags().String("projects", "", "Comma-delimited list of specified")
49+
rootCmd.Flags().BoolP("quiet", "q", false, "Quiet output - only show errors")
50+
rootCmd.Flags().String("resume-from", "", "Resume reactor from specified")
51+
rootCmd.Flags().StringP("settings", "s", "", "Alternate path for the user")
52+
rootCmd.Flags().BoolP("show-version", "V", false, "Display version information")
53+
rootCmd.Flags().BoolP("strict-checksums", "C", false, "Fail the build if checksums don't")
54+
rootCmd.Flags().StringP("threads", "T", "", "Thread count, for instance 2.0C")
55+
rootCmd.Flags().StringP("toolchains", "t", "", "Alternate path for the user")
56+
rootCmd.Flags().Bool("update-plugins", false, "Ineffective, only kept for")
57+
rootCmd.Flags().BoolP("update-snapshots", "U", false, "Forces a check for missing")
58+
rootCmd.Flags().BoolP("version", "v", false, "Display version information")
59+
60+
carapace.Gen(rootCmd).FlagCompletion(carapace.ActionMap{
61+
"activate-profiles": carapace.ActionMultiParts(",", func(args, parts []string) carapace.Action {
62+
return action.ActionProfiles(rootCmd.Flag("file").Value.String()).Invoke(args).Filter(parts).ToA()
63+
}),
64+
"file": carapace.ActionFiles(".xml"),
65+
"global-settings": carapace.ActionFiles(".xml"),
66+
"global-toolchains": carapace.ActionFiles(".xml"),
67+
"log-file": carapace.ActionFiles(""),
68+
"settings": carapace.ActionFiles(".xml"),
69+
"toolchains": carapace.ActionFiles(".xml"),
70+
})
71+
72+
carapace.Gen(rootCmd).PositionalAnyCompletion(
73+
carapace.ActionCallback(func(args []string) carapace.Action {
74+
return action.ActionGoalsAndPhases(rootCmd.Flag("file").Value.String()).Invoke(args).Filter(args).ToA()
75+
}),
76+
)
77+
}

completers/mvn_completer/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package main
2+
3+
import "github.com/rsteube/carapace-bin/completers/mvn_completer/cmd"
4+
5+
func main() {
6+
cmd.Execute()
7+
}

0 commit comments

Comments
 (0)