├── .gitignore
├── .goreleaser.yml
├── .travis.yml
├── LICENSE
├── README.md
├── VERSION
├── api
├── api.go
├── build.go
├── builder.go
├── create.go
├── create_test.go
├── imports.go
├── install.go
├── install_test.go
├── legacy.go
├── list.go
├── list_test.go
├── project.go
├── shim.go
├── update.go
└── util.go
├── cmd
└── flogo
│ ├── gen
│ └── version.go
│ ├── imports.go
│ └── main.go
├── commands
├── build.go
├── create.go
├── imports.go
├── install.go
├── list.go
├── plugin.go
├── pluginhelper.go
├── root.go
└── update.go
├── common
├── build.go
├── env.go
├── plugin.go
└── project.go
├── docs
├── commands.md
└── plugins.md
├── examples
└── simple-plugin
│ ├── go.mod
│ └── myplugin.go
├── go.mod
├── go.sum
├── test
└── flogo.json
├── util
├── app.go
├── ast.go
├── contrib.go
├── engine.go
├── env.go
├── file.go
├── flogo.go
├── flogo_test.go
├── gosrchelper.go
├── gosrchelper_test.go
├── imports.go
├── mod.go
└── version.go
└── version.go
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | ## OS X Resources
3 | .DS_Store
4 | .vscode/
5 |
6 | # Binaries
7 | *.exe
8 | *.test
9 | *.prof
10 |
11 |
12 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
13 | *.o
14 | *.a
15 | *.so
16 |
17 | # Dynamic libs
18 | *.dll
19 | *.so
20 | *.dylib
21 |
22 | # Intellij
23 |
24 | .idea/
25 |
26 | # cli binary
27 | ./flogo
28 | dist
29 |
30 | go.mod.build
31 |
32 | cmd/flogo/currentversion.go
33 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | before:
2 | hooks:
3 | - go mod download
4 | - go generate ./...
5 | builds:
6 | - goos:
7 | - darwin
8 | - linux
9 | - windows
10 | goarch:
11 | - amd64
12 | env:
13 | - CGO_ENABLED=0
14 | - GO111MODULE=on
15 | main: ./cmd/flogo/
16 | binary: flogo
17 | archives:
18 | - format: binary
19 | replacements:
20 | darwin: osx
21 | linux: linux
22 | windows: win
23 | amd64: x86_64
24 | checksum:
25 | name_template: 'checksums.txt'
26 | changelog:
27 | sort: asc
28 | filters:
29 | exclude:
30 | - '^docs:'
31 | - '^test:'
32 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.12.x
4 | os:
5 | - linux
6 | env:
7 | - GO111MODULE=on
8 | install: skip
9 | script:
10 | - GO111MODULE=off go get ./... # to populate $GOPATH/src for tests
11 | - go build ./...
12 | - go test ./...
13 | deploy:
14 | - provider: script
15 | skip_cleanup: true
16 | script: curl -sL https://git.io/goreleaser | bash
17 | on:
18 | tags: true
19 | condition: $TRAVIS_OS_NAME = linux
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, TIBCO Software, Inc.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of TIBCO Software Inc. nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Serverless functions and edge microservices made painless
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Installation | Getting Started | Documentation | Contributing | License
18 |
19 |
20 |
21 | Project Flogo is an open source framework to simplify building efficient & modern serverless functions and edge microservices and this is the cli that makes it all happen.
22 |
23 | FLOGO CLI
24 | ======================
25 |
26 | The Flogo CLI is the primary tool to use when working with a Flogo application. It is used to create, modify and build Flogo applications
27 | ## Installation
28 | ### Prerequisites
29 | To get started with the Flogo CLI you'll need to have a few things
30 | * The Go programming language version 1.11 or later should be [installed](https://golang.org/doc/install).
31 | * In order to simplify dependency management, we're using **go mod**. For more information on **go mod**, visit the [Go Modules Wiki](https://github.com/golang/go/wiki/Modules).
32 |
33 | ### Install the cli
34 | To install the CLI, simply open a terminal and enter the below command
35 |
36 | ```
37 | $ go install github.com/project-flogo/cli/...@latest
38 | ```
39 |
40 | ### Build the CLI from source
41 | You can build the cli from source code as well, which is convenient if you're developing new features for it! To do that, follow these easy steps
42 |
43 | ```bash
44 | # Get the flogo CLI from GitHub
45 | $ git clone https://github.com/project-flogo/cli.git
46 |
47 | # Go to the directory
48 | $ cd cli
49 |
50 | # Optionally check out the branch you want to use
51 | $ git checkout test_branch
52 |
53 | # Run the install command
54 | $ go install ./...
55 | ```
56 |
57 | ## Getting started
58 | Getting started should be easy and fun, and so is getting started with the Flogo cli.
59 |
60 | First, create a file called `flogo.json` and with the below content (which is a simple app with an [HTTP trigger](https://tibcosoftware.github.io/flogo/development/webui/triggers/rest/))
61 |
62 | ```json
63 | {
64 | "name": "SampleApp",
65 | "type": "flogo:app",
66 | "version": "0.0.1",
67 | "appModel": "1.1.0",
68 | "imports": [
69 | "github.com/project-flogo/contrib/trigger/rest",
70 | "github.com/project-flogo/flow",
71 | "github.com/project-flogo/contrib/activity/log"
72 | ],
73 | "triggers": [
74 | {
75 | "id": "receive_http_message",
76 | "ref": "#rest",
77 | "name": "Receive HTTP Message",
78 | "description": "Simple REST Trigger",
79 | "settings": {
80 | "port": 9233
81 | },
82 | "handlers": [
83 | {
84 | "settings": {
85 | "method": "GET",
86 | "path": "/test"
87 | },
88 | "action": {
89 | "ref": "#flow",
90 | "settings": {
91 | "flowURI": "res://flow:sample_flow"
92 | }
93 | }
94 | }
95 | ]
96 | }
97 | ],
98 | "resources": [
99 | {
100 | "id": "flow:sample_flow",
101 | "data": {
102 | "name": "SampleFlow",
103 | "tasks": [
104 | {
105 | "id": "log_message",
106 | "name": "Log Message",
107 | "description": "Simple Log Activity",
108 | "activity": {
109 | "ref": "#log",
110 | "input": {
111 | "message": "Simple Log",
112 | "addDetails": "false"
113 | }
114 | }
115 | }
116 | ]
117 | }
118 | }
119 | ]
120 | }
121 | ```
122 |
123 | Based on this file we'll create a new flogo app
124 |
125 | ```bash
126 | $ flogo create -f flogo.json myApp
127 | ```
128 |
129 | From the app folder we can build the executable
130 |
131 | ```bash
132 | $ cd myApp
133 | $ flogo build -e
134 | ```
135 |
136 | Now that there is an executable we can run it!
137 |
138 | ```bash
139 | $ cd bin
140 | $ ./myApp
141 | ```
142 |
143 | The above commands will start the REST server and wait for messages to be sent to `http://localhost:9233/test`. To send a message you can use your browser, or a new terminal window and run
144 |
145 | ```bash
146 | $ curl http://localhost:9233/test
147 | ```
148 |
149 | _For more tutorials check out the [Labs](https://tibcosoftware.github.io/flogo/labs/) section in our documentation_
150 |
151 | ## Documentation
152 |
153 | There is documentation also available for [CLI Commands](docs/commands.md) and [CLI Plugins](docs/plugins.md).
154 |
155 | ## Contributing
156 | Want to contribute to Project Flogo? We've made it easy, all you need to do is fork the repository you intend to contribute to, make your changes and create a Pull Request! Once the pull request has been created, you'll be prompted to sign the CLA (Contributor License Agreement) online.
157 |
158 | Not sure where to start? No problem, you can browse the Project Flogo repos and look for issues tagged `kind/help-wanted` or `good first issue`. To make this even easier, we've added the links right here too!
159 | * Project Flogo: [kind/help-wanted](https://github.com/TIBCOSoftware/flogo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) and [good first issue](https://github.com/TIBCOSoftware/flogo/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
160 | * flogo cli: [kind/help-wanted](https://github.com/project-flogo/cli/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) and [good first issue](https://github.com/project-flogo/cli/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
161 | * flogo core: [kind/help-wanted](https://github.com/project-flogo/core/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) and [good first issue](https://github.com/project-flogo/core/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
162 | * flogo contrib: [kind/help-wanted](https://github.com/project-flogo/contrib/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) and [good first issue](https://github.com/project-flogo/contrib/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
163 |
164 | Another great way to contribute to Project Flogo is to check [flogo-contrib](https://github.com/project-flogo/contrib). That repository contains some basic contributions, such as activities, triggers, etc. Perhaps there is something missing? Create a new activity or trigger or fix a bug in an existing activity or trigger.
165 |
166 | If you have any questions, feel free to post an issue and tag it as a question, email flogo-oss@tibco.com or chat with the team and community:
167 |
168 | * The [project-flogo/Lobby](https://gitter.im/project-flogo/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) Gitter channel should be used for general discussions, start here for all things Flogo!
169 | * The [project-flogo/developers](https://gitter.im/project-flogo/developers?utm_source=share-link&utm_medium=link&utm_campaign=share-link) Gitter channel should be used for developer/contributor focused conversations.
170 |
171 | For additional details, refer to the [Contribution Guidelines](https://github.com/TIBCOSoftware/flogo/blob/master/CONTRIBUTING.md).
172 |
173 | ## License
174 | Flogo source code in [this](https://github.com/project-flogo/cli) repository is under a BSD-style license, refer to [LICENSE](https://github.com/project-flogo/cli/blob/master/LICENSE)
175 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | v0.11.1
--------------------------------------------------------------------------------
/api/api.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/project-flogo/cli/util"
5 | )
6 |
7 | var verbose = false
8 |
9 | func SetVerbose(enable bool) {
10 | verbose = enable
11 | util.SetVerbose(enable)
12 | }
13 |
14 | func Verbose() bool {
15 | return verbose
16 | }
17 |
18 | //TODO use a logger like struct for API that can be used to log or console output
19 |
--------------------------------------------------------------------------------
/api/build.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "go/parser"
6 | "go/printer"
7 | "go/token"
8 | "io/ioutil"
9 | "os"
10 | "path/filepath"
11 | "strings"
12 |
13 | "github.com/project-flogo/cli/common"
14 | "github.com/project-flogo/cli/util"
15 | )
16 |
17 | const (
18 | fileEmbeddedAppGo string = "embeddedapp.go"
19 | )
20 |
21 | func BuildProject(project common.AppProject, options common.BuildOptions) error {
22 |
23 | err := project.DepManager().AddReplacedContribForBuild()
24 | if err != nil {
25 | return err
26 | }
27 |
28 | buildPreProcessors := common.BuildPreProcessors()
29 |
30 | if len(buildPreProcessors) > 0 {
31 | for _, processor := range buildPreProcessors {
32 | err = processor.DoPreProcessing(project,options)
33 | if err != nil {
34 | return err
35 | }
36 | }
37 | }
38 |
39 | var builder common.Builder
40 | embedConfig := options.EmbedConfig
41 |
42 | if options.Shim != "" {
43 | builder = &ShimBuilder{shim:options.Shim}
44 | embedConfig = true
45 | } else {
46 | builder = &AppBuilder{}
47 | }
48 |
49 | if embedConfig {
50 | err = createEmbeddedAppGoFile(project)
51 | if err != nil {
52 | return err
53 | }
54 | } else {
55 | err = cleanupEmbeddedAppGoFile(project)
56 | if err != nil {
57 | return err
58 | }
59 | }
60 |
61 | if options.OptimizeImports {
62 | if Verbose() {
63 | fmt.Println("Optimizing imports...")
64 | }
65 | err := optimizeImports(project)
66 | defer restoreImports(project)
67 |
68 | if err != nil {
69 | return err
70 | }
71 | }
72 |
73 | err = builder.Build(project)
74 | if err != nil {
75 | return err
76 | }
77 |
78 | buildPostProcessors := common.BuildPostProcessors()
79 |
80 | if len(buildPostProcessors) > 0 {
81 | for _, processor := range buildPostProcessors {
82 | err = processor.DoPostProcessing(project)
83 | if err != nil {
84 | return err
85 | }
86 | }
87 | }
88 |
89 | return nil
90 | }
91 |
92 | func cleanupEmbeddedAppGoFile(project common.AppProject) error {
93 | embedSrcPath := filepath.Join(project.SrcDir(), fileEmbeddedAppGo)
94 |
95 | if _, err := os.Stat(embedSrcPath); err == nil {
96 | if Verbose() {
97 | fmt.Println("Removing embed configuration")
98 | }
99 | err = os.Remove(embedSrcPath)
100 | if err != nil {
101 | return err
102 | }
103 | }
104 | return nil
105 | }
106 |
107 | func createEmbeddedAppGoFile(project common.AppProject) error {
108 |
109 | embedSrcPath := filepath.Join(project.SrcDir(), fileEmbeddedAppGo)
110 |
111 | if Verbose() {
112 | fmt.Println("Embedding configuration in application...")
113 | }
114 |
115 | buf, err := ioutil.ReadFile(filepath.Join(project.Dir(), fileFlogoJson))
116 | if err != nil {
117 | return err
118 | }
119 | flogoJSON := string(buf)
120 |
121 | tplFile := tplEmbeddedAppGoFile
122 | if !isNewMain(project) {
123 | tplFile = tplEmbeddedAppOldGoFile
124 | }
125 |
126 | engineJSON := ""
127 |
128 | if util.FileExists(filepath.Join(project.Dir(), fileEngineJson)) {
129 | buf, err = ioutil.ReadFile(filepath.Join(project.Dir(), fileEngineJson))
130 | if err != nil {
131 | return err
132 | }
133 |
134 | engineJSON = string(buf)
135 | }
136 |
137 | data := struct {
138 | FlogoJSON string
139 | EngineJSON string
140 | }{
141 | flogoJSON,
142 | engineJSON,
143 | }
144 |
145 | f, err := os.Create(embedSrcPath)
146 | if err != nil {
147 | return err
148 | }
149 | RenderTemplate(f, tplFile, &data)
150 | _ = f.Close()
151 |
152 | return nil
153 | }
154 |
155 | func isNewMain(project common.AppProject) bool {
156 | mainGo := filepath.Join(project.SrcDir(), fileMainGo)
157 | buf, err := ioutil.ReadFile(mainGo)
158 | if err == nil {
159 | mainCode := string(buf)
160 | return strings.Contains(mainCode, "cfgEngine")
161 |
162 | }
163 |
164 | return false
165 | }
166 |
167 |
168 | var tplEmbeddedAppGoFile = `// Do not change this file, it has been generated using flogo-cli
169 | // If you change it and rebuild the application your changes might get lost
170 | package main
171 |
172 | // embedded flogo app descriptor file
173 | const flogoJSON string = ` + "`{{.FlogoJSON}}`" + `
174 | const engineJSON string = ` + "`{{.EngineJSON}}`" + `
175 |
176 | func init () {
177 | cfgJson = flogoJSON
178 | cfgEngine = engineJSON
179 | }
180 | `
181 |
182 | var tplEmbeddedAppOldGoFile = `// Do not change this file, it has been generated using flogo-cli
183 | // If you change it and rebuild the application your changes might get lost
184 | package main
185 |
186 | // embedded flogo app descriptor file
187 | const flogoJSON string = ` + "`{{.FlogoJSON}}`" + `
188 |
189 | func init () {
190 | cfgJson = flogoJSON
191 | }
192 | `
193 |
194 | func initMain(project common.AppProject, backupMain bool) error {
195 |
196 | //backup main if it exists
197 | mainGo := filepath.Join(project.SrcDir(), fileMainGo)
198 | mainGoBak := filepath.Join(project.SrcDir(), fileMainGo+".bak")
199 |
200 | if backupMain {
201 | if _, err := os.Stat(mainGo); err == nil {
202 | err = os.Rename(mainGo, mainGoBak)
203 | if err != nil {
204 | return err
205 | }
206 | } else if _, err := os.Stat(mainGoBak); err != nil {
207 | return fmt.Errorf("project corrupt, main missing")
208 | }
209 | } else {
210 | if _, err := os.Stat(mainGoBak); err == nil {
211 | err = os.Rename(mainGoBak, mainGo)
212 | if err != nil {
213 | return err
214 | }
215 | } else if _, err := os.Stat(mainGo); err != nil {
216 | return fmt.Errorf("project corrupt, main missing")
217 | }
218 | }
219 |
220 | return nil
221 | }
222 |
223 | func optimizeImports(project common.AppProject) error {
224 |
225 | appImports, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), true)
226 | if err != nil {
227 | return err
228 | }
229 |
230 | var unused []util.Import
231 | appImports.GetAllImports()
232 | for _, impDetails := range appImports.GetAllImportDetails() {
233 | if !impDetails.Referenced() && impDetails.IsCoreContrib() {
234 | unused = append(unused, impDetails.Imp)
235 | }
236 | }
237 |
238 | importsFile := filepath.Join(project.SrcDir(), fileImportsGo)
239 | importsFileOrig := filepath.Join(project.SrcDir(), fileImportsGo+".orig")
240 |
241 | err = util.CopyFile(importsFile, importsFileOrig)
242 | if err != nil {
243 | return err
244 | }
245 |
246 | fset := token.NewFileSet()
247 | file, err := parser.ParseFile(fset, importsFile, nil, parser.ImportsOnly)
248 | if err != nil {
249 | return err
250 | }
251 |
252 | for _, i := range unused {
253 | if Verbose() {
254 | fmt.Printf(" Removing Import: %s\n", i.GoImportPath())
255 | }
256 | util.DeleteImport(fset, file, i.GoImportPath())
257 | }
258 |
259 | f, err := os.Create(importsFile)
260 | defer f.Close()
261 | if err := printer.Fprint(f, fset, file); err != nil {
262 | return err
263 | }
264 |
265 | return nil
266 | }
267 |
268 | func restoreImports(project common.AppProject) {
269 |
270 | importsFile := filepath.Join(project.SrcDir(), fileImportsGo)
271 | importsFileOrig := filepath.Join(project.SrcDir(), fileImportsGo+".orig")
272 |
273 | if _, err := os.Stat(importsFileOrig); err == nil {
274 | err = util.CopyFile(importsFileOrig, importsFile)
275 | if err != nil {
276 | fmt.Fprintf(os.Stderr, "Error restoring imports file '%s': %v\n", importsFile, err)
277 | return
278 | }
279 |
280 | var err = os.Remove(importsFileOrig)
281 | if err != nil {
282 | fmt.Fprintf(os.Stderr, "Error removing backup imports file '%s': %v\n", importsFileOrig, err)
283 | fmt.Fprintf(os.Stderr, "Manually remove backup imports file '%s'\n", importsFileOrig)
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/api/builder.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "os/exec"
7 |
8 | "github.com/project-flogo/cli/common"
9 | "github.com/project-flogo/cli/util"
10 | )
11 |
12 | type AppBuilder struct {
13 |
14 | }
15 |
16 | func (*AppBuilder) Build(project common.AppProject) error {
17 |
18 | err := restoreMain(project)
19 | if err != nil {
20 | return err
21 | }
22 |
23 | err = simpleGoBuild(project)
24 | if err != nil {
25 | return err
26 | }
27 |
28 | return nil
29 | }
30 |
31 |
32 | func simpleGoBuild(project common.AppProject) error {
33 | if _, err := os.Stat(project.BinDir()); err != nil {
34 | if Verbose() {
35 | fmt.Println("Creating 'bin' directory")
36 | }
37 | err = os.MkdirAll(project.BinDir(), os.ModePerm)
38 | if err != nil {
39 | return err
40 | }
41 | }
42 |
43 | if Verbose() {
44 | fmt.Println("Performing 'go build'...")
45 | }
46 |
47 | err := util.ExecCmd(exec.Command("go", "build", "-o", project.Executable()), project.SrcDir())
48 | if err != nil {
49 | fmt.Println("Error in building", project.SrcDir())
50 | return err
51 | }
52 |
53 | return nil
54 | }
--------------------------------------------------------------------------------
/api/create.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 | "os/exec"
11 |
12 | "github.com/project-flogo/cli/common"
13 | "github.com/project-flogo/cli/util"
14 | )
15 |
16 | var fileSampleEngineMain = filepath.Join("examples", "engine", "main.go")
17 |
18 | func CreateProject(basePath, appName, appCfgPath, coreVersion string) (common.AppProject, error) {
19 |
20 | var err error
21 | var appJson string
22 |
23 | if appCfgPath != "" {
24 |
25 | if util.IsRemote(appCfgPath) {
26 |
27 | appJson, err = util.LoadRemoteFile(appCfgPath)
28 | if err != nil {
29 | return nil, fmt.Errorf("unable to load remote app file '%s' - %s", appCfgPath, err.Error())
30 | }
31 | } else {
32 | appJson, err = util.LoadLocalFile(appCfgPath)
33 | if err != nil {
34 | return nil, fmt.Errorf("unable to load app file '%s' - %s", appCfgPath, err.Error())
35 | }
36 | }
37 | } else {
38 | if len(appName) == 0 {
39 | return nil, fmt.Errorf("app name not specified")
40 | }
41 | }
42 |
43 | appName, err = getAppName(appName, appJson)
44 | if err != nil {
45 | return nil, err
46 | }
47 |
48 | fmt.Printf("Creating Flogo App: %s\n", appName)
49 |
50 | appDir, err := createAppDirectory(basePath, appName)
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | srcDir := filepath.Join(appDir, "src")
56 | dm := util.NewDepManager(srcDir)
57 |
58 | if Verbose() {
59 | fmt.Printf("Setting up app directory: %s\n", appDir)
60 | }
61 |
62 | err = setupAppDirectory(dm, appDir, coreVersion)
63 | if err != nil {
64 | return nil, err
65 | }
66 |
67 | if Verbose() {
68 | if appJson == "" {
69 | fmt.Println("Adding sample flogo.json")
70 | }
71 | }
72 | err = createAppJson(dm, appDir, appName, appJson)
73 | if err != nil {
74 | return nil, err
75 | }
76 |
77 | err = createMain(dm, appDir)
78 | if err != nil {
79 | return nil, err
80 | }
81 |
82 | project := NewAppProject(appDir)
83 |
84 | if Verbose() {
85 | fmt.Println("Importing Dependencies...")
86 | }
87 |
88 | err = importDependencies(project)
89 | if err != nil {
90 | return nil, err
91 | }
92 |
93 | if Verbose() {
94 | fmt.Printf("Created App: %s\n", appName)
95 | }
96 |
97 | return project, nil
98 | }
99 |
100 | // createAppDirectory creates the flogo app directory
101 | func createAppDirectory(basePath, appName string) (string, error) {
102 |
103 | var err error
104 |
105 | if basePath == "." {
106 | basePath, err = os.Getwd()
107 | if err != nil {
108 | return "", err
109 | }
110 | }
111 |
112 | appPath := filepath.Join(basePath, appName)
113 | err = os.Mkdir(appPath, os.ModePerm)
114 | if err != nil {
115 | return "", err
116 | }
117 |
118 | return appPath, nil
119 | }
120 |
121 | //setupAppDirectory sets up the flogo app directory
122 | func setupAppDirectory(dm util.DepManager, appPath, coreVersion string) error {
123 |
124 | err := os.Mkdir(filepath.Join(appPath, dirBin), os.ModePerm)
125 | if err != nil {
126 | return err
127 | }
128 |
129 | srcDir := filepath.Join(appPath, dirSrc)
130 | err = os.Mkdir(srcDir, os.ModePerm)
131 | if err != nil {
132 | return err
133 | }
134 |
135 | _, err = os.Create(filepath.Join(srcDir, fileImportsGo))
136 | if err != nil {
137 | return err
138 | }
139 | err = ioutil.WriteFile(filepath.Join(srcDir, fileImportsGo), []byte("package main\n"), 0644)
140 | if err != nil {
141 | return err
142 | }
143 |
144 | err = dm.Init()
145 | if err != nil {
146 | return err
147 | }
148 |
149 | flogoCoreImport := util.NewFlogoImport(flogoCoreRepo, "", coreVersion, "")
150 |
151 | //todo get the actual version installed from the go.mod file
152 | if coreVersion == "" {
153 | fmt.Printf("Installing: %s@latest\n", flogoCoreImport.CanonicalImport())
154 | } else {
155 | fmt.Printf("Installing: %s\n", flogoCoreImport.CanonicalImport())
156 | }
157 |
158 | // add & fetch the core library
159 | err = dm.AddDependency(flogoCoreImport)
160 | if err != nil {
161 | return err
162 | }
163 |
164 | return nil
165 | }
166 |
167 | // createAppJson create the flogo app json
168 | func createAppJson(dm util.DepManager, appDir, appName, appJson string) error {
169 |
170 | updatedJson, err := getAndUpdateAppJson(dm, appName, appJson)
171 | if err != nil {
172 | return err
173 | }
174 |
175 | err = ioutil.WriteFile(filepath.Join(appDir, fileFlogoJson), []byte(updatedJson), 0644)
176 | if err != nil {
177 | return err
178 | }
179 |
180 | return nil
181 | }
182 |
183 | // importDependencies import all dependencies
184 | func importDependencies(project common.AppProject) error {
185 |
186 | ai, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), true)
187 | if err != nil {
188 | return err
189 | }
190 |
191 | imports := ai.GetAllImports()
192 |
193 | if len(imports) == 0 {
194 | return nil
195 | }
196 |
197 | err = project.AddImports(true, false, imports...)
198 | if err != nil {
199 | return err
200 | }
201 |
202 | legacySupportRequired := false
203 |
204 | for _, details := range ai.GetAllImportDetails() {
205 |
206 | path, err := project.GetPath(details.Imp)
207 | if err != nil {
208 | return err
209 | }
210 |
211 | desc, err := util.GetContribDescriptor(path)
212 |
213 | if err != nil {
214 | return err
215 | }
216 |
217 | if desc != nil {
218 |
219 | cType := desc.GetContribType()
220 | if desc.IsLegacy {
221 | legacySupportRequired = true
222 | cType = "legacy " + desc.GetContribType()
223 | err := CreateLegacyMetadata(path, desc.GetContribType(), details.Imp.GoImportPath())
224 | if err != nil {
225 | return err
226 | }
227 | }
228 |
229 | fmt.Printf("Installed %s: %s\n", cType, details.Imp)
230 | //instStr := fmt.Sprintf("Installed %s:", cType)
231 | //fmt.Printf("%-20s %s\n", instStr, imp)
232 | }
233 | }
234 |
235 | if Verbose() {
236 | fmt.Printf("Tidying go mod...")
237 | }
238 |
239 | err = util.ExecCmd(exec.Command("go", "mod", "tidy"), project.SrcDir())
240 | if err != nil {
241 | fmt.Printf("Failed to clean deps: %s\n", err)
242 | }
243 |
244 | if legacySupportRequired {
245 | err := InstallLegacySupport(project)
246 | return err
247 | }
248 |
249 | return nil
250 | }
251 |
252 | func createMain(dm util.DepManager, appDir string) error {
253 |
254 | flogoCoreImport, err := util.NewFlogoImportFromPath(flogoCoreRepo)
255 | if err != nil {
256 | return err
257 | }
258 |
259 | corePath, err := dm.GetPath(flogoCoreImport)
260 | if err != nil {
261 | return err
262 | }
263 |
264 | bytes, err := ioutil.ReadFile(filepath.Join(corePath, fileSampleEngineMain))
265 | if err != nil {
266 | return err
267 | }
268 |
269 | err = ioutil.WriteFile(filepath.Join(appDir, dirSrc, fileMainGo), bytes, 0644)
270 | if err != nil {
271 | return err
272 | }
273 |
274 | return nil
275 | }
276 |
277 | func getAndUpdateAppJson(dm util.DepManager, appName, appJson string) (string, error) {
278 |
279 | if len(appJson) == 0 {
280 | appJson = emptyFlogoJson
281 | }
282 |
283 | descriptor, err := util.ParseAppDescriptor(appJson)
284 | if err != nil {
285 | return "", err
286 | }
287 |
288 | if appName != "" {
289 | // override the application name
290 |
291 | altJson := strings.Replace(appJson, `"`+descriptor.Name+`"`, `"`+appName+`"`, 1)
292 | altDescriptor, err := util.ParseAppDescriptor(altJson)
293 |
294 | //see if we can get away with simple replace so we don't reorder the existing json
295 | if err == nil && altDescriptor.Name == appName {
296 | appJson = altJson
297 | } else {
298 | //simple replace didn't work so we have to unmarshal & re-marshal the supplied json
299 | var appObj map[string]interface{}
300 | err := json.Unmarshal([]byte(appJson), &appObj)
301 | if err != nil {
302 | return "", err
303 | }
304 |
305 | appObj["name"] = appName
306 |
307 | updApp, err := json.MarshalIndent(appObj, "", " ")
308 | if err != nil {
309 | return "", err
310 | }
311 | appJson = string(updApp)
312 | }
313 |
314 | descriptor.Name = appName
315 | } else {
316 | appName = descriptor.Name
317 | }
318 |
319 | return appJson, nil
320 | }
321 |
322 | func getAppName(appName, appJson string) (string, error) {
323 |
324 | if appJson != "" && appName == "" {
325 | descriptor, err := util.ParseAppDescriptor(appJson)
326 | if err != nil {
327 | return "", err
328 | }
329 |
330 | return descriptor.Name, nil
331 | }
332 |
333 | return appName, nil
334 | }
335 | func GetTempDir() (string, error) {
336 |
337 | tempDir, err := ioutil.TempDir("", "flogo")
338 | if err != nil {
339 | return "", err
340 | }
341 | return tempDir, nil
342 | }
343 |
344 | var emptyFlogoJson = `
345 | {
346 | "name": "{{.AppName}}",
347 | "type": "flogo:app",
348 | "version": "0.0.1",
349 | "description": "My Flogo Application Description",
350 | "appModel": "1.1.0",
351 | "imports": [],
352 | "triggers": [],
353 | "resources":[]
354 | }
355 | `
356 |
--------------------------------------------------------------------------------
/api/create_test.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "os"
7 | "path/filepath"
8 | "testing"
9 |
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | var jsonString = `{
14 | "name": "_APP_NAME_",
15 | "type": "flogo:app",
16 | "version": "0.0.1",
17 | "description": "My flogo application description",
18 | "appModel": "1.0.0",
19 | "imports": [
20 | "github.com/TIBCOSoftware/flogo-contrib/activity/log",
21 | "github.com/project-flogo/contrib/trigger/rest",
22 | "github.com/project-flogo/flow"
23 | ],
24 | "triggers": [
25 | {
26 | "id": "my_rest_trigger",
27 | "type": "rest",
28 | "settings": {
29 | "port": "8888"
30 | },
31 | "handlers": [
32 | {
33 | "settings": {
34 | "method": "GET",
35 | "path": "/test/:val"
36 | },
37 | "actions": [
38 | {
39 | "type": "flow",
40 | "settings": {
41 | "flowURI": "res://flow:simple_flow"
42 | },
43 | "input": {
44 | "in": "=$.pathParams.val"
45 | }
46 | }
47 | ]
48 | }
49 | ]
50 | }
51 | ],
52 | "resources": [
53 | {
54 | "id": "flow:simple_flow",
55 | "data": {
56 | "name": "simple_flow",
57 | "metadata": {
58 | "input": [
59 | { "name": "in", "type": "string", "value": "test" }
60 | ],
61 | "output": [
62 | { "name": "out", "type": "string" }
63 | ]
64 | },
65 | "tasks": [
66 | {
67 | "id": "log",
68 | "name": "Log Message",
69 | "activity": {
70 | "type": "log",
71 | "input": {
72 | "message": "=$flow.in",
73 | "flowInfo": "false",
74 | "addToFlow": "false"
75 | }
76 | }
77 | }
78 | ],
79 | "links": []
80 | }
81 | }
82 | ]
83 | }
84 | `
85 |
86 | type TestEnv struct {
87 | currentDir string
88 | }
89 |
90 | func (t *TestEnv) getTestwd() (dir string, err error) {
91 | return t.currentDir, nil
92 | }
93 |
94 | func (t *TestEnv) cleanup() {
95 |
96 | os.RemoveAll(t.currentDir)
97 | }
98 |
99 | func TestCmdCreate_noflag(t *testing.T) {
100 | t.Log("Testing simple creation of project")
101 |
102 | tempDir, _ := GetTempDir()
103 | testEnv := &TestEnv{currentDir: tempDir}
104 |
105 | defer testEnv.cleanup()
106 |
107 | t.Logf("Current dir '%s'", testEnv.currentDir)
108 | _, err := CreateProject(testEnv.currentDir, "myApp", "", "")
109 | assert.Equal(t, nil, err)
110 |
111 | _, err = os.Stat(filepath.Join(tempDir, "myApp", "src", "go.mod"))
112 |
113 | assert.Equal(t, nil, err)
114 | _, err = os.Stat(filepath.Join(tempDir, "myApp", "flogo.json"))
115 |
116 | assert.Equal(t, nil, err)
117 |
118 | _, err = os.Stat(filepath.Join(tempDir, "myApp", "src", "main.go"))
119 | assert.Equal(t, nil, err)
120 | }
121 |
122 | func TestCmdCreate_flag(t *testing.T) {
123 | t.Log("Testing creation of project while the file is provided")
124 |
125 | tempDir, err := ioutil.TempDir("", "test")
126 | if err != nil {
127 | t.Fatal(err)
128 | }
129 |
130 | testEnv := &TestEnv{currentDir: tempDir}
131 |
132 | defer testEnv.cleanup()
133 |
134 | t.Logf("Current dir '%s'", testEnv.currentDir)
135 | os.Chdir(testEnv.currentDir)
136 | file, err := os.Create("flogo.json")
137 | if err != nil {
138 | t.Fatal(err)
139 | assert.Equal(t, true, false)
140 | }
141 | defer file.Close()
142 | fmt.Fprintf(file, jsonString)
143 | _, err = CreateProject(testEnv.currentDir, "flogo", "flogo.json", "")
144 | assert.Equal(t, nil, err)
145 |
146 | _, err = os.Stat(filepath.Join(tempDir, "flogo", "src", "go.mod"))
147 |
148 | assert.Equal(t, nil, err)
149 | _, err = os.Stat(filepath.Join(tempDir, "flogo", "flogo.json"))
150 |
151 | assert.Equal(t, nil, err)
152 |
153 | _, err = os.Stat(filepath.Join(tempDir, "flogo", "src", "main.go"))
154 | assert.Equal(t, nil, err)
155 | }
156 |
157 | func TestCmdCreate_masterCore(t *testing.T) {
158 | t.Log("Testing creation of project when the version of core is provided `master`")
159 |
160 | tempDir, err := ioutil.TempDir("", "test")
161 | if err != nil {
162 | t.Fatal(err)
163 | }
164 |
165 | testEnv := &TestEnv{currentDir: tempDir}
166 |
167 | defer testEnv.cleanup()
168 |
169 | t.Logf("Current dir '%s'", testEnv.currentDir)
170 | os.Chdir(testEnv.currentDir)
171 |
172 | _, err = CreateProject(testEnv.currentDir, "myApp", "", "master")
173 | assert.Equal(t, nil, err)
174 | }
175 |
176 | //todo fix this test, unreliable
177 | //func TestCmdCreate_versionCore(t *testing.T) {
178 | // t.Log("Testing creation of project when the version of core is provided `v0.9.0-alpha.4`")
179 | //
180 | // tempDir, err := ioutil.TempDir("", "test")
181 | // if err != nil {
182 | // t.Fatal(err)
183 | // }
184 | //
185 | // testEnv := &TestEnv{currentDir: tempDir}
186 | //
187 | // defer testEnv.cleanup()
188 | //
189 | // t.Logf("Current dir '%s'", testEnv.currentDir)
190 | // os.Chdir(testEnv.currentDir)
191 | //
192 | // _, err = CreateProject(testEnv.currentDir, "myApp", "", "v0.9.0-alpha.4")
193 | // assert.Equal(t, nil, err)
194 | //
195 | // _, err = os.Stat(filepath.Join(tempDir, "myApp", "src", "go.mod"))
196 | //
197 | // assert.Equal(t, nil, err)
198 | // _, err = os.Stat(filepath.Join(tempDir, "myApp", "flogo.json"))
199 | //
200 | // assert.Equal(t, nil, err)
201 | //
202 | // _, err = os.Stat(filepath.Join(tempDir, "myApp", "src", "main.go"))
203 | // assert.Equal(t, nil, err)
204 | //
205 | // data, err1 := ioutil.ReadFile(filepath.Join(tempDir, "myApp", "src", "go.mod"))
206 | // assert.Equal(t, nil, err1)
207 | //
208 | // //todo fix, not a reliable test giving that importing latest of flow which will affect this import
209 | // assert.Equal(t, true, strings.Contains(string(data), "v0.9.0-alpha.4"))
210 | // fmt.Println(string(data))
211 | //
212 | // appProject := NewAppProject(filepath.Join(testEnv.currentDir, "myApp"))
213 | //
214 | // err = appProject.Validate()
215 | // assert.Nil(t, err)
216 | //
217 | // common.SetCurrentProject(appProject)
218 | //
219 | // err = BuildProject(common.CurrentProject(), BuildOptions{})
220 | // assert.Nil(t, err)
221 | //}
222 |
--------------------------------------------------------------------------------
/api/imports.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "strings"
8 |
9 | "github.com/project-flogo/cli/common"
10 | "github.com/project-flogo/cli/util"
11 | "github.com/project-flogo/core/app"
12 | )
13 |
14 | func ListProjectImports(project common.AppProject) error {
15 |
16 | appImports, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), false)
17 | if err != nil {
18 | return err
19 | }
20 |
21 | for _, imp := range appImports.GetAllImports() {
22 |
23 | fmt.Fprintf(os.Stdout, " %s\n", imp)
24 | }
25 |
26 | return nil
27 | }
28 |
29 | func SyncProjectImports(project common.AppProject) error {
30 |
31 | appImports, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), false)
32 | if err != nil {
33 | return err
34 | }
35 | appImportsMap := make(map[string]util.Import)
36 | for _, imp := range appImports.GetAllImports() {
37 | appImportsMap[imp.GoImportPath()] = imp
38 | }
39 |
40 | goImports, err := project.GetGoImports(false)
41 | if err != nil {
42 | return err
43 | }
44 | goImportsMap := make(map[string]util.Import)
45 | for _, imp := range goImports {
46 | goImportsMap[imp.GoImportPath()] = imp
47 | }
48 |
49 | var toAdd []util.Import
50 | for goPath, imp := range appImportsMap {
51 | if _, ok := goImportsMap[goPath]; !ok {
52 | toAdd = append(toAdd, imp)
53 | if Verbose() {
54 | fmt.Println("Adding missing Go import: ", goPath)
55 | }
56 | }
57 | }
58 |
59 | if util.FileExists(filepath.Join(project.Dir(), fileEngineJson)) {
60 | engineImports, err := util.GetEngineImports(filepath.Join(project.Dir(), fileEngineJson), project.DepManager())
61 | if err != nil {
62 | return err
63 | }
64 |
65 | engImportsMap := make(map[string]util.Import)
66 | for _, imp := range engineImports.GetAllImports() {
67 | engImportsMap[imp.GoImportPath()] = imp
68 | }
69 |
70 | for goPath, imp := range engImportsMap {
71 | if _, ok := goImportsMap[goPath]; !ok {
72 | toAdd = append(toAdd, imp)
73 | if Verbose() {
74 | fmt.Println("Adding missing Go import: ", goPath)
75 | }
76 | }
77 | }
78 | }
79 |
80 | var toRemove []string
81 | for goPath := range goImportsMap {
82 | if _, ok := appImportsMap[goPath]; !ok {
83 | toRemove = append(toRemove, goPath)
84 | if Verbose() {
85 | fmt.Println("Removing extraneous Go import: ", goPath)
86 | }
87 | }
88 | }
89 |
90 | err = project.RemoveImports(toRemove...)
91 | if err != nil {
92 | return err
93 | }
94 |
95 | err = project.AddImports(false, false, toAdd...)
96 | if err != nil {
97 | return err
98 | }
99 |
100 | return nil
101 | }
102 |
103 | func ResolveProjectImports(project common.AppProject) error {
104 | if Verbose() {
105 | fmt.Fprintln(os.Stdout, "Synchronizing project imports")
106 | }
107 | err := SyncProjectImports(project)
108 | if err != nil {
109 | return err
110 | }
111 |
112 | if Verbose() {
113 | fmt.Fprintln(os.Stdout, "Reading flogo.json")
114 | }
115 | appDescriptor, err := readAppDescriptor(project)
116 | if err != nil {
117 | return err
118 | }
119 |
120 | if Verbose() {
121 | fmt.Fprintln(os.Stdout, "Updating flogo.json import versions")
122 | }
123 | err = updateDescriptorImportVersions(project, appDescriptor)
124 | if err != nil {
125 | return err
126 | }
127 |
128 | if Verbose() {
129 | fmt.Fprintln(os.Stdout, "Saving updated flogo.json")
130 | }
131 | err = writeAppDescriptor(project, appDescriptor)
132 | if err != nil {
133 | return err
134 | }
135 |
136 | return nil
137 | }
138 |
139 | func updateDescriptorImportVersions(project common.AppProject, appDescriptor *app.Config) error {
140 |
141 | goModImports, err := project.DepManager().GetAllImports()
142 | if err != nil {
143 | return err
144 | }
145 |
146 | appImports, err := util.ParseImports(appDescriptor.Imports)
147 | if err != nil {
148 | return err
149 | }
150 |
151 | var result []string
152 |
153 | for _, appImport := range appImports {
154 |
155 | if goModImport, ok := goModImports[appImport.ModulePath()]; ok {
156 | updatedImp := util.NewFlogoImportWithVersion(appImport, goModImport.Version())
157 | result = append(result, updatedImp.CanonicalImport())
158 | } else {
159 | //not found, look for import of parent package
160 | for pkg, goModImport := range goModImports {
161 | if strings.Contains(appImport.ModulePath(), pkg) {
162 | updatedImp := util.NewFlogoImportWithVersion(appImport, goModImport.Version())
163 | result = append(result, updatedImp.CanonicalImport())
164 | }
165 | }
166 | }
167 | }
168 |
169 | appDescriptor.Imports = result
170 |
171 | return nil
172 | }
173 |
174 |
175 |
--------------------------------------------------------------------------------
/api/install.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 |
9 | "github.com/project-flogo/cli/common"
10 | "github.com/project-flogo/cli/util"
11 | )
12 |
13 | func InstallPackage(project common.AppProject, pkg string) error {
14 |
15 | flogoImport, err := util.ParseImport(pkg)
16 | if err != nil {
17 | return err
18 | }
19 |
20 | err = project.AddImports(false, true, flogoImport)
21 | if err != nil {
22 | return err
23 | }
24 |
25 | path, err := project.GetPath(flogoImport)
26 | if Verbose() {
27 | fmt.Println("Installed path", path)
28 | }
29 | if err != nil {
30 | return err
31 | }
32 |
33 | legacySupportRequired := false
34 | desc, err := util.GetContribDescriptor(path)
35 | if desc != nil {
36 | cType := desc.GetContribType()
37 | if desc.IsLegacy {
38 | legacySupportRequired = true
39 | cType = "legacy " + desc.GetContribType()
40 | err := CreateLegacyMetadata(path, desc.GetContribType(), pkg)
41 | if err != nil {
42 | return err
43 | }
44 | }
45 |
46 | fmt.Printf("Installed %s: %s\n", cType, flogoImport)
47 | //instStr := fmt.Sprintf("Installed %s:", cType)
48 | //fmt.Printf("%-20s %s\n", instStr, imp)
49 | }
50 |
51 | if legacySupportRequired {
52 | err := InstallLegacySupport(project)
53 | if err != nil {
54 | return err
55 | }
56 | }
57 |
58 | return nil
59 | }
60 |
61 | func InstallReplacedPackage(project common.AppProject, replacedPath string, pkg string) error {
62 |
63 | err := project.DepManager().InstallReplacedPkg(pkg, replacedPath)
64 | if err != nil {
65 | return err
66 | }
67 | return InstallPackage(project, pkg+"@v0.0.0")
68 | }
69 |
70 | func InstallContribBundle(project common.AppProject, path string) error {
71 |
72 | file, err := ioutil.ReadFile(path)
73 | if err != nil {
74 | return err
75 | }
76 |
77 | var contribBundleDescriptor util.FlogoContribBundleDescriptor
78 |
79 | err = json.Unmarshal(file, &contribBundleDescriptor)
80 | if err != nil {
81 | return err
82 | }
83 |
84 | for _, contrib := range contribBundleDescriptor.Contribs {
85 | err := InstallPackage(project, contrib)
86 | if err != nil {
87 | fmt.Fprintf(os.Stderr, "Error installing contrib '%s': %s", contrib, err.Error())
88 | }
89 | }
90 |
91 | return nil
92 | }
93 |
--------------------------------------------------------------------------------
/api/install_test.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "github.com/project-flogo/cli/common"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | var newJsonString = `{
13 | "name": "temp",
14 | "type": "flogo:app",
15 | "version": "0.0.1",
16 | "description": "My flogo application description",
17 | "appModel": "1.0.0",
18 | "imports": [
19 | "github.com/project-flogo/flow",
20 | "github.com/skothari-tibco/flogoaztrigger",
21 | "github.com/project-flogo/contrib/activity/actreturn",
22 | "github.com/project-flogo/contrib/activity/log",
23 | "github.com/TIBCOSoftware/flogo-contrib/activity/rest"
24 | ],
25 | "triggers": [
26 | {
27 | "id": "my_rest_trigger",
28 | "ref": "github.com/skothari-tibco/flogoaztrigger",
29 | "handlers": [
30 | {
31 | "action": {
32 | "ref": "github.com/project-flogo/flow",
33 | "settings": {
34 | "flowURI": "res://flow:simple_flow"
35 | },
36 | "input": {
37 | "in": "inputA"
38 | },
39 | "output" :{
40 | "out": "=$.out"
41 | }
42 | }
43 | }
44 | ]
45 | }
46 | ],
47 | "resources": [
48 | {
49 | "id": "flow:simple_flow",
50 | "data": {
51 | "name": "simple_flow",
52 | "metadata": {
53 | "input": [
54 | { "name": "in", "type": "string", "value": "test" }
55 | ],
56 | "output": [
57 | { "name": "out", "type": "string" }
58 | ]
59 | },
60 | "tasks": [
61 | {
62 | "id": "log",
63 | "name": "Log Message",
64 | "activity": {
65 | "ref": "github.com/project-flogo/contrib/activity/log",
66 | "input": {
67 | "message": "=$flow.in",
68 | "flowInfo": "false",
69 | "addToFlow": "false"
70 | }
71 | }
72 | },
73 | {
74 | "id" :"return",
75 | "name" : "Activity Return",
76 | "activity":{
77 | "ref" : "github.com/project-flogo/contrib/activity/actreturn",
78 | "settings":{
79 | "mappings":{
80 | "out": "nameA"
81 | }
82 | }
83 | }
84 | }
85 | ],
86 | "links": [
87 | {
88 | "from":"log",
89 | "to":"return"
90 | }
91 | ]
92 | }
93 | }
94 | ]
95 | }
96 | `
97 |
98 | func TestInstallLegacyPkg(t *testing.T) {
99 | t.Log("Testing installation of package")
100 |
101 | tempDir, _ := GetTempDir()
102 |
103 | testEnv := &TestEnv{currentDir: tempDir}
104 |
105 | defer testEnv.cleanup()
106 |
107 | t.Logf("Current dir '%s'", testEnv.currentDir)
108 | _ = os.Chdir(testEnv.currentDir)
109 |
110 | _, err := CreateProject(testEnv.currentDir, "myApp", "", "v0.9.2")
111 |
112 | assert.Nil(t, err)
113 |
114 | err = InstallPackage(NewAppProject(filepath.Join(testEnv.currentDir, "myApp")), "github.com/TIBCOSoftware/flogo-contrib/activity/log")
115 | assert.Nil(t, err)
116 |
117 | appProject := NewAppProject(filepath.Join(testEnv.currentDir, "myApp"))
118 |
119 | err = appProject.Validate()
120 | assert.Nil(t, err)
121 |
122 | common.SetCurrentProject(appProject)
123 |
124 | err = BuildProject(common.CurrentProject(), common.BuildOptions{})
125 | assert.Nil(t, err)
126 |
127 | }
128 |
129 | func TestInstallPkg(t *testing.T) {
130 | t.Log("Testing installation of package")
131 |
132 | tempDir, _ := GetTempDir()
133 |
134 | testEnv := &TestEnv{currentDir: tempDir}
135 |
136 | defer testEnv.cleanup()
137 |
138 | t.Logf("Current dir '%s'", testEnv.currentDir)
139 | _ = os.Chdir(testEnv.currentDir)
140 |
141 | _, err := CreateProject(testEnv.currentDir, "myApp", "", "")
142 |
143 | assert.Nil(t, err)
144 |
145 | err = InstallPackage(NewAppProject(filepath.Join(testEnv.currentDir, "myApp")), "github.com/project-flogo/contrib/activity/noop")
146 | assert.Nil(t, err)
147 |
148 | appProject := NewAppProject(filepath.Join(testEnv.currentDir, "myApp"))
149 |
150 | err = appProject.Validate()
151 | assert.Nil(t, err)
152 |
153 | common.SetCurrentProject(appProject)
154 |
155 | err = BuildProject(common.CurrentProject(), common.BuildOptions{})
156 | assert.Nil(t, err)
157 | }
158 |
159 | func TestInstallPkgWithVersion(t *testing.T) {
160 | t.Log("Testing installation of package")
161 |
162 | tempDir, _ := GetTempDir()
163 |
164 | testEnv := &TestEnv{currentDir: tempDir}
165 |
166 | defer testEnv.cleanup()
167 |
168 | t.Logf("Current dir '%s'", testEnv.currentDir)
169 | _ = os.Chdir(testEnv.currentDir)
170 |
171 | _, err := CreateProject(testEnv.currentDir, "myApp", "", "")
172 |
173 | assert.Nil(t, err)
174 |
175 | err = InstallPackage(NewAppProject(filepath.Join(testEnv.currentDir, "myApp")), "github.com/project-flogo/contrib/activity/log@v0.9.0")
176 | assert.Nil(t, err)
177 |
178 | appProject := NewAppProject(filepath.Join(testEnv.currentDir, "myApp"))
179 |
180 | err = appProject.Validate()
181 | assert.Nil(t, err)
182 |
183 | common.SetCurrentProject(appProject)
184 |
185 | err = BuildProject(common.CurrentProject(), common.BuildOptions{})
186 | assert.Nil(t, err)
187 | }
188 |
189 | func TestListPkg(t *testing.T) {
190 | t.Log("Testing listing of packages")
191 |
192 | tempDir, _ := GetTempDir()
193 |
194 | testEnv := &TestEnv{currentDir: tempDir}
195 |
196 | defer testEnv.cleanup()
197 |
198 | t.Logf("Current dir '%s'", testEnv.currentDir)
199 | _ = os.Chdir(testEnv.currentDir)
200 |
201 | _, err := CreateProject(testEnv.currentDir, "myApp", "", "")
202 |
203 | assert.Equal(t, nil, err)
204 |
205 | err = ListContribs(NewAppProject(filepath.Join(testEnv.currentDir, "myApp")), true, "")
206 | assert.Equal(t, nil, err)
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/api/legacy.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | //Legacy Helper Functions
4 | import (
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | "os"
9 | "path/filepath"
10 | "strings"
11 | "text/template"
12 |
13 | "github.com/project-flogo/cli/common"
14 | "github.com/project-flogo/cli/util"
15 | )
16 |
17 | const (
18 | pkgLegacySupport = "github.com/project-flogo/legacybridge"
19 | )
20 |
21 | func InstallLegacySupport(project common.AppProject) error {
22 | //todo make sure we only install once
23 | pkgLegacySupportImport, err := util.NewFlogoImportFromPath(pkgLegacySupport)
24 | if err != nil {
25 | return err
26 | }
27 | err = project.AddImports(false, true, pkgLegacySupportImport)
28 | if err == nil {
29 | fmt.Println("Installed Legacy Support")
30 | }
31 | return err
32 | }
33 |
34 | func CreateLegacyMetadata(path, contribType, contribPkg string) error {
35 |
36 | var mdGoFilePath string
37 |
38 | tplMetadata := ""
39 |
40 | switch contribType {
41 | case "action":
42 | //ignore
43 | return nil
44 | case "trigger":
45 | fmt.Printf("Generating metadata for legacy trigger: %s\n", contribPkg)
46 | mdGoFilePath = filepath.Join(path, "trigger_metadata.go")
47 | tplMetadata = tplTriggerMetadataGoFile
48 | case "activity":
49 | fmt.Printf("Generating metadata for legacy actvity: %s\n", contribPkg)
50 | mdGoFilePath = filepath.Join(path, "activity_metadata.go")
51 | tplMetadata = tplActivityMetadataGoFile
52 | default:
53 | return nil
54 | }
55 |
56 | mdFilePath := filepath.Join(path, contribType+".json")
57 | pkg := filepath.Base(path)
58 |
59 | if idx := strings.Index(pkg, "@"); idx > 0 {
60 | pkg = pkg[:idx]
61 | }
62 |
63 | raw, err := ioutil.ReadFile(mdFilePath)
64 | if err != nil {
65 | return err
66 | }
67 |
68 | info := &struct {
69 | Package string
70 | MetadataJSON string
71 | }{
72 | Package: pkg,
73 | MetadataJSON: string(raw),
74 | }
75 |
76 | err = os.Chmod(path, 0777)
77 | if err != nil {
78 | return err
79 | }
80 | defer os.Chmod(path, 0555)
81 |
82 | f, err := os.Create(mdGoFilePath)
83 | if err != nil {
84 | return err
85 | }
86 | RenderTemplate(f, tplMetadata, info)
87 | f.Close()
88 |
89 | return nil
90 | }
91 |
92 | var tplActivityMetadataGoFile = `package {{.Package}}
93 |
94 | import (
95 | "github.com/project-flogo/legacybridge"
96 | "github.com/TIBCOSoftware/flogo-lib/core/activity"
97 | )
98 |
99 | var jsonMetadata = ` + "`{{.MetadataJSON}}`" + `
100 |
101 | // init create & register activity
102 | func init() {
103 | md := activity.NewMetadata(jsonMetadata)
104 | legacybridge.RegisterLegacyActivity(NewActivity(md))
105 | }
106 | `
107 |
108 | var tplTriggerMetadataGoFile = `package {{.Package}}
109 |
110 | import (
111 | "github.com/project-flogo/legacybridge"
112 | "github.com/TIBCOSoftware/flogo-lib/core/trigger"
113 | )
114 |
115 | var jsonMetadata = ` + "`{{.MetadataJSON}}`" + `
116 |
117 | // init create & register trigger factory
118 | func init() {
119 | md := trigger.NewMetadata(jsonMetadata)
120 | legacybridge.RegisterLegacyTriggerFactory(md.ID, NewFactory(md))
121 | }
122 | `
123 |
124 | //RenderTemplate renders the specified template
125 | func RenderTemplate(w io.Writer, text string, data interface{}) {
126 | t := template.New("top")
127 | t.Funcs(template.FuncMap{"trim": strings.TrimSpace})
128 | template.Must(t.Parse(text))
129 | if err := t.Execute(w, data); err != nil {
130 | panic(err)
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/api/list.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 | "path/filepath"
8 | "strings"
9 |
10 | "github.com/project-flogo/cli/common"
11 | "github.com/project-flogo/cli/util"
12 | )
13 |
14 | type ListFilter int
15 |
16 | func ListContribs(project common.AppProject, jsonFormat bool, filter string) error {
17 |
18 | ai, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), true)
19 | if err != nil {
20 | return err
21 | }
22 |
23 | var specs []*ContribSpec
24 |
25 | for _, details := range ai.GetAllImportDetails() {
26 |
27 | if !includeContrib(details, filter) {
28 | continue
29 | }
30 |
31 | specs = append(specs, getContribSpec(project, details))
32 |
33 | }
34 |
35 | for _, details := range ai.GetAllImportDetails() {
36 |
37 | if details.ContribDesc == nil {
38 | continue
39 | }
40 |
41 | if details.ContribDesc.Type == "flogo:function" {
42 | specs = append(specs, getContribSpec(project, details))
43 | }
44 | }
45 |
46 | if len(specs) == 0 {
47 | return nil
48 | }
49 |
50 | if jsonFormat {
51 | resp, err := json.MarshalIndent(specs, "", " ")
52 | if err != nil {
53 | return err
54 | }
55 |
56 | fmt.Fprintf(os.Stdout, "%v \n", string(resp))
57 | } else {
58 | for _, spec := range specs {
59 | fmt.Println("Contrib: " + spec.Name)
60 | fmt.Println(" Type : " + spec.Type)
61 | if spec.IsLegacy != nil {
62 | fmt.Println(" IsLegacy : true")
63 | }
64 | fmt.Println(" Homepage : " + spec.Homepage)
65 | fmt.Println(" Ref : " + spec.Ref)
66 | fmt.Println(" Path : " + spec.Path)
67 | fmt.Println(" Descriptor : " + spec.Path)
68 | fmt.Println(" Description: " + spec.Description)
69 | fmt.Println()
70 | }
71 | }
72 |
73 | return nil
74 | }
75 |
76 | func includeContrib(details *util.AppImportDetails, filter string) bool {
77 |
78 | if details.IsCoreContrib() {
79 |
80 | switch strings.ToLower(filter) {
81 | case "used":
82 | return details.Referenced()
83 | case "unused":
84 | return !details.Referenced()
85 | default:
86 | return true
87 | }
88 | }
89 |
90 | return false
91 |
92 | }
93 |
94 | type ContribSpec struct {
95 | Name string `json:"name"`
96 | Type string `json:"type"`
97 | Description string `json:"description"`
98 | Homepage string `json:"homepage"`
99 | Ref string `json:"ref"`
100 | Path string `json:"path"`
101 | Descriptor string `json:"descriptor"`
102 | IsLegacy interface{} `json:"isLegacy,omitempty"`
103 | }
104 |
105 | func getContribSpec(project common.AppProject, details *util.AppImportDetails) *ContribSpec {
106 | path, err := project.GetPath(details.Imp)
107 | if err != nil {
108 | return nil
109 | }
110 |
111 | if Verbose() {
112 | fmt.Println("Path of contrib", path, "for contrib", details.Imp)
113 | }
114 |
115 | desc := details.ContribDesc
116 |
117 | spec := &ContribSpec{}
118 | spec.Name = desc.Name
119 | spec.Type = desc.Type
120 | spec.Description = desc.Description
121 | spec.Homepage = desc.Homepage
122 | spec.Ref = details.Imp.ModulePath()
123 | spec.Path = path
124 |
125 | if desc.IsLegacy {
126 | spec.IsLegacy = true
127 | spec.Descriptor = desc.GetContribType() + ".json"
128 | } else {
129 | spec.Descriptor = "descriptor.json"
130 | }
131 |
132 | return spec
133 | }
134 | func ListOrphanedRefs(project common.AppProject, jsonFormat bool) error {
135 |
136 | ai, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), true)
137 | if err != nil {
138 | return err
139 | }
140 |
141 | orphaned := ai.GetOrphanedReferences()
142 |
143 | if jsonFormat {
144 | resp, err := json.MarshalIndent(orphaned, "", " ")
145 | if err != nil {
146 | return err
147 | }
148 |
149 | fmt.Fprintf(os.Stdout, "%v \n", string(resp))
150 | } else {
151 | for _, ref := range orphaned {
152 | fmt.Println(ref)
153 | }
154 | }
155 |
156 | return nil
157 | }
158 |
--------------------------------------------------------------------------------
/api/list_test.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "testing"
8 |
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | func TestListAllContribs(t *testing.T) {
13 | t.Log("Testing listing of all contribs")
14 |
15 | tempDir, _ := GetTempDir()
16 |
17 | testEnv := &TestEnv{currentDir: tempDir}
18 |
19 | defer testEnv.cleanup()
20 |
21 | t.Logf("Current dir '%s'", testEnv.currentDir)
22 | os.Chdir(testEnv.currentDir)
23 |
24 | _, err := CreateProject(testEnv.currentDir, "myApp", "", "")
25 |
26 | assert.Equal(t, nil, err)
27 |
28 | err = ListContribs(NewAppProject(filepath.Join(testEnv.currentDir, "myApp")), true, "all")
29 | assert.Equal(t, nil, err)
30 |
31 | }
32 |
33 | func TestListWithLegacyPkg(t *testing.T) {
34 | t.Log("Testing listing of legacy contribs")
35 |
36 | tempDir, _ := GetTempDir()
37 |
38 | testEnv := &TestEnv{currentDir: tempDir}
39 |
40 | defer testEnv.cleanup()
41 |
42 | t.Logf("Current dir '%s'", testEnv.currentDir)
43 |
44 | err := os.Chdir(tempDir)
45 |
46 | file, err := os.Create("flogo.json")
47 | if err != nil {
48 | t.Fatal(err)
49 | assert.Equal(t, true, false)
50 | }
51 | defer file.Close()
52 | fmt.Fprintf(file, newJsonString)
53 | _, err = CreateProject(testEnv.currentDir, "temp", "flogo.json", "")
54 | assert.Equal(t, nil, err)
55 |
56 | err = ListContribs(NewAppProject(filepath.Join(testEnv.currentDir, "temp")), true, "")
57 | assert.Equal(t, nil, err)
58 | }
59 |
--------------------------------------------------------------------------------
/api/project.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "go/parser"
6 | "go/printer"
7 | "go/token"
8 | "os"
9 | "path/filepath"
10 | "runtime"
11 | "strings"
12 |
13 | "github.com/project-flogo/cli/common"
14 | "github.com/project-flogo/cli/util"
15 | )
16 |
17 | const (
18 | flogoCoreRepo = "github.com/project-flogo/core"
19 | fileFlogoJson = "flogo.json"
20 | fileEngineJson = "engine.json"
21 | fileMainGo = "main.go"
22 | fileImportsGo = "imports.go"
23 | dirSrc = "src"
24 | dirBin = "bin"
25 | )
26 |
27 | var GOOSENV = os.Getenv("GOOS")
28 |
29 | type appProjectImpl struct {
30 | appDir string
31 | appName string
32 | srcDir string
33 | binDir string
34 | dm util.DepManager
35 | }
36 |
37 | func NewAppProject(appDir string) common.AppProject {
38 | project := &appProjectImpl{appDir: appDir}
39 | project.srcDir = filepath.Join(appDir, dirSrc)
40 | project.binDir = filepath.Join(appDir, dirBin)
41 | project.dm = util.NewDepManager(project.srcDir)
42 | project.appName = filepath.Base(appDir)
43 | return project
44 | }
45 |
46 | func (p *appProjectImpl) Validate() error {
47 | _, err := os.Stat(filepath.Join(p.appDir, fileFlogoJson))
48 | if os.IsNotExist(err) {
49 | return fmt.Errorf("not a valid flogo app project directory, missing flogo.json")
50 | }
51 |
52 | _, err = os.Stat(p.srcDir)
53 | if os.IsNotExist(err) {
54 | return fmt.Errorf("not a valid flogo app project directory, missing 'src' diretory")
55 | }
56 |
57 | _, err = os.Stat(filepath.Join(p.srcDir, fileImportsGo))
58 | if os.IsNotExist(err) {
59 | return fmt.Errorf("flogo app directory corrupt, missing 'src/imports.go' file")
60 | }
61 |
62 | _, err = os.Stat(filepath.Join(p.srcDir, "go.mod"))
63 | if os.IsNotExist(err) {
64 | return fmt.Errorf("flogo app directory corrupt, missing 'src/go.mod' file")
65 | }
66 |
67 | return nil
68 | }
69 |
70 | func (p *appProjectImpl) Name() string {
71 | return p.appName
72 | }
73 |
74 | func (p *appProjectImpl) Dir() string {
75 | return p.appDir
76 | }
77 |
78 | func (p *appProjectImpl) BinDir() string {
79 | return p.binDir
80 | }
81 |
82 | func (p *appProjectImpl) SrcDir() string {
83 | return p.srcDir
84 | }
85 |
86 | func (p *appProjectImpl) DepManager() util.DepManager {
87 | return p.dm
88 | }
89 |
90 | func (p *appProjectImpl) Executable() string {
91 |
92 | var execPath string
93 |
94 | execPath = filepath.Join(p.binDir, p.appName)
95 |
96 | if GOOSENV == "windows" || (runtime.GOOS == "windows" && GOOSENV == "") {
97 | // env or cross platform is windows
98 | execPath = filepath.Join(p.binDir, p.appName+".exe")
99 | }
100 |
101 | return execPath
102 | }
103 |
104 | func (p *appProjectImpl) GetPath(flogoImport util.Import) (string, error) {
105 | return p.dm.GetPath(flogoImport)
106 | }
107 |
108 | func (p *appProjectImpl) GetGoImports(withVersion bool) ([]util.Import, error) {
109 | importsFile := filepath.Join(p.SrcDir(), fileImportsGo)
110 |
111 | fset := token.NewFileSet()
112 | file, err := parser.ParseFile(fset, importsFile, nil, parser.ImportsOnly)
113 | if err != nil {
114 | return nil, err
115 | }
116 |
117 | //if withVersion, parse go.mod file for version information
118 | //goModImports, err := p.dm.GetAllImports()
119 | //if err != nil {
120 | // return nil, err
121 | //}
122 |
123 | var imports []util.Import
124 | for _, is := range file.Imports {
125 |
126 | imp, err := util.ParseImport(is.Path.Value)
127 | if err != nil {
128 | return nil, err
129 | }
130 |
131 | imports = append(imports, imp)
132 | }
133 |
134 | return imports, nil
135 | }
136 |
137 | func (p *appProjectImpl) addImportsInGo(ignoreError bool, imports ...util.Import) error {
138 | importsFile := filepath.Join(p.SrcDir(), fileImportsGo)
139 |
140 | fset := token.NewFileSet()
141 | file, err := parser.ParseFile(fset, importsFile, nil, parser.ImportsOnly)
142 | if err != nil {
143 | return err
144 | }
145 |
146 | for _, i := range imports {
147 | err := p.DepManager().AddDependency(i)
148 | if err != nil {
149 | if ignoreError {
150 | fmt.Printf("Warning: unable to install '%s'\n", i)
151 | continue
152 | }
153 |
154 | fmt.Fprintf(os.Stderr, "Error in installing '%s'\n", i)
155 |
156 | return err
157 | }
158 | util.AddImport(fset, file, i.GoImportPath())
159 | }
160 |
161 | f, err := os.Create(importsFile)
162 | defer f.Close()
163 | if err := printer.Fprint(f, fset, file); err != nil {
164 | return err
165 | }
166 |
167 | //p.dm.Finalize()
168 |
169 | return nil
170 | }
171 |
172 | func (p *appProjectImpl) addImportsInJson(ignoreError bool, imports ...util.Import) error {
173 |
174 | appDescriptor, err := readAppDescriptor(p)
175 | if err != nil {
176 | return err
177 | }
178 |
179 | // list existing imports in JSON to avoid duplicates
180 | existingImports := make(map[string]util.Import)
181 | jsonImports, _ := util.ParseImports(appDescriptor.Imports)
182 | for _, e := range jsonImports {
183 | existingImports[e.GoImportPath()] = e
184 | }
185 |
186 | for _, i := range imports {
187 | val, ok := existingImports[i.GoImportPath()]
188 | if !ok {
189 | //appDescriptor.Imports = append(appDescriptor.Imports, i.CanonicalImport())
190 | existingImports[i.GoImportPath()] = i
191 | } else {
192 | if i.CanonicalImport() != val.CanonicalImport() {
193 | delete(existingImports, val.GoImportPath())
194 | alias := i.Alias()
195 | if val.Alias() != "" && i.Alias() == "" {
196 | alias = val.Alias()
197 | }
198 | existingImports[i.GoImportPath()] = util.NewFlogoImport(i.ModulePath(), i.RelativeImportPath(), i.Version(), alias)
199 | }
200 | }
201 |
202 | }
203 | var newImport []string
204 | for _, val := range existingImports {
205 | newImport = append(newImport, val.CanonicalImport())
206 | }
207 | appDescriptor.Imports = newImport
208 |
209 | err = writeAppDescriptor(p, appDescriptor)
210 | if err != nil {
211 | return err
212 | }
213 |
214 | return nil
215 | }
216 |
217 | func (p *appProjectImpl) AddImports(ignoreError bool, addToJson bool, imports ...util.Import) error {
218 | err := p.addImportsInGo(ignoreError, imports...) // begin with Go imports as they are more likely to fail
219 | if err != nil {
220 | return err
221 | }
222 |
223 | if addToJson {
224 | err = p.addImportsInJson(ignoreError, imports...) // adding imports in JSON after Go imports ensure the flogo.json is self-sufficient
225 | }
226 |
227 | return err
228 | }
229 |
230 | func (p *appProjectImpl) RemoveImports(imports ...string) error {
231 |
232 | importsFile := filepath.Join(p.SrcDir(), fileImportsGo)
233 |
234 | fset := token.NewFileSet()
235 | file, err := parser.ParseFile(fset, importsFile, nil, parser.ImportsOnly)
236 | if err != nil {
237 | return err
238 | }
239 |
240 | for _, impPath := range imports {
241 | util.DeleteImport(fset, file, strings.Trim(impPath, "\""))
242 | }
243 |
244 | f, err := os.Create(importsFile)
245 | defer f.Close()
246 | if err := printer.Fprint(f, fset, file); err != nil {
247 | return err
248 | }
249 |
250 | return nil
251 | }
252 |
--------------------------------------------------------------------------------
/api/shim.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "os"
7 | "os/exec"
8 | "path"
9 | "path/filepath"
10 | "regexp"
11 | "strings"
12 |
13 | "github.com/project-flogo/cli/common"
14 | "github.com/project-flogo/cli/util"
15 | )
16 |
17 | const (
18 | fileShimSupportGo string = "shim_support.go"
19 | fileShimGo string = "shim.go"
20 | fileBuildGo string = "build.go"
21 | fileMakefile string = "Makefile"
22 | dirShim string = "shim"
23 | )
24 |
25 | var fileSampleShimSupport = filepath.Join("examples", "engine", "shim", fileShimSupportGo)
26 |
27 | var flogoImportPattern = regexp.MustCompile(`^(([^ ]*)[ ]+)?([^@:]*)@?([^:]*)?:?(.*)?$`)
28 |
29 |
30 | type ShimBuilder struct {
31 | appBuilder common.Builder
32 | shim string
33 | }
34 |
35 | func (sb *ShimBuilder) Build(project common.AppProject) error {
36 |
37 | err := backupMain(project)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | defer shimCleanup(project)
43 |
44 | err = createShimSupportGoFile(project)
45 | if err != nil {
46 | return err
47 | }
48 |
49 | if Verbose() {
50 | fmt.Println("Preparing shim...")
51 | }
52 | built, err := prepareShim(project, sb.shim)
53 | if err != nil {
54 | return err
55 | }
56 |
57 | if !built {
58 | fmt.Println("Using go build to build shim...")
59 |
60 | err := simpleGoBuild(project)
61 | if err != nil {
62 | return err
63 | }
64 | }
65 |
66 | return nil
67 | }
68 |
69 | func prepareShim(project common.AppProject, shim string) (bool, error) {
70 |
71 | buf, err := ioutil.ReadFile(filepath.Join(project.Dir(), fileFlogoJson))
72 | if err != nil {
73 | return false, err
74 | }
75 |
76 | flogoJSON := string(buf)
77 |
78 | descriptor, err := util.ParseAppDescriptor(flogoJSON)
79 | if err != nil {
80 | return false, err
81 | }
82 |
83 | err = registerImports(project, descriptor)
84 | if err != nil {
85 | return false, err
86 | }
87 |
88 | for _, trgCfg := range descriptor.Triggers {
89 | if trgCfg.Id == shim {
90 |
91 | ref := trgCfg.Ref
92 |
93 | if trgCfg.Ref != "" {
94 | found := false
95 | ref, found = GetAliasRef("flogo:trigger", trgCfg.Ref)
96 | if !found {
97 | return false, fmt.Errorf("unable to determine ref for trigger: %s", trgCfg.Id)
98 | }
99 | }
100 |
101 | refImport, err := util.NewFlogoImportFromPath(ref)
102 | if err != nil {
103 | return false, err
104 | }
105 |
106 | impPath, err := project.GetPath(refImport)
107 | if err != nil {
108 | return false, err
109 | }
110 |
111 | var shimFilePath string
112 |
113 | shimFilePath = filepath.Join(impPath, dirShim, fileShimGo)
114 |
115 | if _, err := os.Stat(shimFilePath); err == nil {
116 |
117 | err = util.CopyFile(shimFilePath, filepath.Join(project.SrcDir(), fileShimGo))
118 | if err != nil {
119 | return false, err
120 | }
121 |
122 | // Check if this shim based trigger has a gobuild file. If the trigger has a gobuild
123 | // execute that file, otherwise check if there is a Makefile to execute
124 | goBuildFilePath := filepath.Join(impPath, dirShim, fileBuildGo)
125 |
126 | makefilePath := filepath.Join(shimFilePath, dirShim, fileMakefile)
127 |
128 | if _, err := os.Stat(goBuildFilePath); err == nil {
129 | fmt.Println("Using build.go to build shim......")
130 |
131 | err = util.CopyFile(goBuildFilePath, filepath.Join(project.SrcDir(), fileBuildGo))
132 | if err != nil {
133 | return false, err
134 | }
135 |
136 | // Execute go run gobuild.go
137 | err = util.ExecCmd(exec.Command("go", "run", fileBuildGo), project.SrcDir())
138 | if err != nil {
139 | return false, err
140 | }
141 |
142 | return true, nil
143 | } else if _, err := os.Stat(makefilePath); err == nil {
144 | //look for Makefile and execute it
145 | fmt.Println("Using make file to build shim...")
146 |
147 | err = util.CopyFile(makefilePath, filepath.Join(project.SrcDir(), fileMakefile))
148 | if err != nil {
149 | return false, err
150 | }
151 |
152 | if Verbose() {
153 | fmt.Println("Make File:", makefilePath)
154 | }
155 |
156 | // Execute make
157 | cmd := exec.Command("make", "-C", project.SrcDir())
158 | cmd.Stdout = os.Stdout
159 | cmd.Stderr = os.Stderr
160 | cmd.Env = util.ReplaceEnvValue(os.Environ(), "GOPATH", project.Dir())
161 |
162 | err = cmd.Run()
163 | if err != nil {
164 | return false, err
165 | }
166 |
167 | return true, nil
168 | } else {
169 | return false, nil
170 | }
171 | }
172 |
173 | break
174 | }
175 | }
176 |
177 | return false, fmt.Errorf("unable to to find shim trigger: %s", shim)
178 | }
179 |
180 | func shimCleanup(project common.AppProject) {
181 |
182 | if Verbose() {
183 | fmt.Println("Cleaning up shim support files...")
184 | }
185 |
186 | err := util.DeleteFile(filepath.Join(project.SrcDir(), fileShimSupportGo))
187 | if err != nil {
188 | fmt.Printf("Unable to delete: %s", fileShimSupportGo)
189 | }
190 | err = util.DeleteFile(filepath.Join(project.SrcDir(), fileShimGo))
191 | if err != nil {
192 | fmt.Printf("Unable to delete: %s", fileShimGo)
193 | }
194 | err = util.DeleteFile(filepath.Join(project.SrcDir(), fileBuildGo))
195 | if err != nil {
196 | fmt.Printf("Unable to delete: %s", fileBuildGo)
197 | }
198 | }
199 |
200 | func createShimSupportGoFile(project common.AppProject) error {
201 |
202 | shimSrcPath := filepath.Join(project.SrcDir(), fileShimSupportGo)
203 |
204 | if Verbose() {
205 | fmt.Println("Creating shim support files...")
206 | }
207 |
208 | flogoCoreImport, err := util.NewFlogoImportFromPath(flogoCoreRepo)
209 | if err != nil {
210 | return err
211 | }
212 |
213 | corePath, err := project.GetPath(flogoCoreImport)
214 | if err != nil {
215 | return err
216 | }
217 |
218 | bytes, err := ioutil.ReadFile(filepath.Join(corePath, fileSampleShimSupport))
219 | if err != nil {
220 | return err
221 | }
222 |
223 | err = ioutil.WriteFile(shimSrcPath, bytes, 0644)
224 | if err != nil {
225 | return err
226 | }
227 |
228 | return nil
229 | }
230 |
231 | func registerImports(project common.AppProject, appDesc *util.FlogoAppDescriptor) error {
232 |
233 | for _, anImport := range appDesc.Imports {
234 | err := registerImport(project, anImport)
235 | if err != nil {
236 | return err
237 | }
238 | }
239 |
240 | return nil
241 | }
242 |
243 | func registerImport(project common.AppProject, anImport string) error {
244 |
245 | matches := flogoImportPattern.FindStringSubmatch(anImport)
246 |
247 | parts := strings.Split(matches[3], " ")
248 |
249 | var alias string
250 | var ref string
251 | numParts := len(parts)
252 | if numParts == 1 {
253 | ref = parts[0]
254 | alias = path.Base(ref)
255 |
256 | } else if numParts == 2 {
257 | alias = parts[0]
258 | ref = parts[1]
259 | } else {
260 | return fmt.Errorf("invalid import %s", anImport)
261 | }
262 |
263 | if alias == "" || ref == "" {
264 | return fmt.Errorf("invalid import %s", anImport)
265 | }
266 |
267 | ct, err := util.GetContribType(project.DepManager(), ref)
268 | if err != nil {
269 | return err
270 | }
271 |
272 | if ct != "" {
273 | RegisterAlias(ct, alias, ref)
274 | }
275 |
276 | return nil
277 | }
278 |
279 | var aliases = make(map[string]map[string]string)
280 |
281 | func RegisterAlias(contribType string, alias, ref string) {
282 |
283 | aliasToRefMap, exists := aliases[contribType]
284 | if !exists {
285 | aliasToRefMap = make(map[string]string)
286 | aliases[contribType] = aliasToRefMap
287 | }
288 |
289 | aliasToRefMap[alias] = ref
290 | }
291 |
292 | func GetAliasRef(contribType string, alias string) (string, bool) {
293 | if alias == "" {
294 | return "", false
295 | }
296 |
297 | if alias[0] == '#' {
298 | alias = alias[1:]
299 | }
300 | aliasToRefMap, exists := aliases[contribType]
301 | if !exists {
302 | return "", false
303 | }
304 |
305 | ref, exists := aliasToRefMap[alias]
306 | if !exists {
307 | return "", false
308 | }
309 |
310 | return ref, true
311 | }
312 |
--------------------------------------------------------------------------------
/api/update.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "os/exec"
6 |
7 | "github.com/project-flogo/cli/common"
8 | "github.com/project-flogo/cli/util"
9 | )
10 |
11 | func UpdatePkg(project common.AppProject, pkg string) error {
12 |
13 | if Verbose() {
14 | fmt.Printf("Updating Package: %s \n", pkg)
15 | }
16 |
17 | err := util.ExecCmd(exec.Command("go", "get", "-u", pkg), project.SrcDir())
18 | return err
19 | }
20 |
--------------------------------------------------------------------------------
/api/util.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "path/filepath"
9 |
10 | "github.com/project-flogo/cli/common"
11 | "github.com/project-flogo/core/app"
12 | )
13 |
14 | func readAppDescriptor(project common.AppProject) (*app.Config, error) {
15 |
16 | appDescriptorFile, err := os.Open(filepath.Join(project.Dir(), fileFlogoJson))
17 | if err != nil {
18 | return nil, err
19 | }
20 | defer appDescriptorFile.Close()
21 |
22 | appDescriptorData, err := ioutil.ReadAll(appDescriptorFile)
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | var appDescriptor app.Config
28 | err = json.Unmarshal([]byte(appDescriptorData), &appDescriptor)
29 | if err != nil {
30 | return nil, err
31 | }
32 |
33 | return &appDescriptor, nil
34 | }
35 |
36 | func writeAppDescriptor(project common.AppProject, appDescriptor *app.Config) error {
37 |
38 | appDescriptorUpdated, err := json.MarshalIndent(appDescriptor, "", " ")
39 | if err != nil {
40 | return err
41 | }
42 |
43 | appDescriptorUpdatedJson := string(appDescriptorUpdated)
44 |
45 | err = ioutil.WriteFile(filepath.Join(project.Dir(), fileFlogoJson), []byte(appDescriptorUpdatedJson), 0644)
46 | if err != nil {
47 | return err
48 | }
49 |
50 | return nil
51 | }
52 |
53 | func backupMain(project common.AppProject) error {
54 | mainGo := filepath.Join(project.SrcDir(), fileMainGo)
55 | mainGoBak := filepath.Join(project.SrcDir(), fileMainGo+".bak")
56 |
57 | if _, err := os.Stat(mainGo); err == nil {
58 | //main found, check for backup main in case we have to remove it
59 | if _, err := os.Stat(mainGoBak); err == nil {
60 |
61 | //remove old main backup
62 | if Verbose() {
63 | fmt.Printf("Removing old main backup: %s\n", mainGoBak)
64 | }
65 | err = os.Rename(mainGoBak, mainGo)
66 | if err != nil {
67 | return err
68 | }
69 | }
70 | if Verbose() {
71 | fmt.Println("Backing up main.go")
72 | }
73 | err = os.Rename(mainGo, mainGoBak)
74 | if err != nil {
75 | return err
76 | }
77 | }
78 |
79 | return nil
80 | }
81 |
82 | func restoreMain(project common.AppProject) error {
83 |
84 | mainGo := filepath.Join(project.SrcDir(), fileMainGo)
85 | mainGoBak := filepath.Join(project.SrcDir(), fileMainGo+".bak")
86 |
87 | if _, err := os.Stat(mainGo); err != nil {
88 | //main not found, check for backup main
89 | if _, err := os.Stat(mainGoBak); err == nil {
90 | if Verbose() {
91 | fmt.Printf("Restoring main from: %s\n", mainGoBak)
92 | }
93 | err = os.Rename(mainGoBak, mainGo)
94 | if err != nil {
95 | return err
96 | }
97 | } else if _, err := os.Stat(mainGo); err != nil {
98 | return fmt.Errorf("project corrupt, main missing")
99 | }
100 | }
101 |
102 | return nil
103 | }
104 |
--------------------------------------------------------------------------------
/cmd/flogo/gen/version.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | package main
4 |
5 | import (
6 | "github.com/project-flogo/cli/util"
7 | "os"
8 | )
9 |
10 | // This Go program is aimed at being called by go:generate from "cmd/flogo/main.go" to create a "currentversion.go" file
11 | // in the "cmd/flogo" subdirectory.
12 | //
13 | // Once this file is created, it will set at runtime the version of the CLI without relying on GOPATH nor Git command to
14 | // parse the version from the source. Hence it is possible to distribute the CLI as a fully static binary.
15 | //
16 | // Users getting the CLI with a classic "go get" command will still have the version retrieved from the directory
17 | // $GOPATH/src/github.com/project-flogo/cli
18 | func main() {
19 |
20 | _, currentVersion, _ := util.GetCLIInfo()
21 | wd, err := os.Getwd()
22 | if err != nil {
23 | return
24 | }
25 |
26 | util.CreateVersionFile(wd, currentVersion)
27 | }
28 |
--------------------------------------------------------------------------------
/cmd/flogo/imports.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | _ "os"
5 | )
6 |
--------------------------------------------------------------------------------
/cmd/flogo/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/project-flogo/cli/commands"
8 | "github.com/project-flogo/cli/util"
9 | )
10 |
11 | // Not set by default, will be filled by init() function in "./currentversion.go" file, if it exists.
12 | // This latter file is generated with a "go generate" command.
13 | var Version string = ""
14 |
15 | //go:generate go run gen/version.go
16 | func main() {
17 |
18 | if util.GetGoPath() == "" {
19 | _, _ = fmt.Fprintf(os.Stderr, "Error: GOPATH must be set before running flogo cli\n")
20 | os.Exit(1)
21 | }
22 |
23 | //Initialize the commands
24 | _ = os.Setenv("GO111MODULE", "on")
25 | commands.Initialize(Version)
26 | commands.Execute()
27 | }
28 |
--------------------------------------------------------------------------------
/commands/build.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "runtime"
8 |
9 | "github.com/project-flogo/cli/api"
10 | "github.com/project-flogo/cli/common"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var buildShim string
15 | var buildOptimize bool
16 | var buildEmbed bool
17 | var syncImport bool
18 | var flogoJsonFile string
19 |
20 | func init() {
21 | buildCmd.Flags().StringVarP(&buildShim, "shim", "", "", "use shim trigger")
22 | buildCmd.Flags().BoolVarP(&buildOptimize, "optimize", "o", false, "optimize build")
23 | buildCmd.Flags().BoolVarP(&buildEmbed, "embed", "e", false, "embed configuration in binary")
24 | buildCmd.Flags().StringVarP(&flogoJsonFile, "file", "f", "", "specify a flogo.json to build")
25 | buildCmd.Flags().BoolVarP(&syncImport, "sync", "s", false, "sync imports during build")
26 | rootCmd.AddCommand(buildCmd)
27 | }
28 |
29 | //Build the project.
30 | var buildCmd = &cobra.Command{
31 | Use: "build",
32 | Short: "build the flogo application",
33 | Long: `Build the flogo application.`,
34 | PersistentPreRun: func(cmd *cobra.Command, args []string) {},
35 | Run: func(cmd *cobra.Command, args []string) {
36 | var err error
37 | if flogoJsonFile == "" {
38 | preRun(cmd, args, verbose)
39 | options := common.BuildOptions{Shim: buildShim, OptimizeImports: buildOptimize, EmbedConfig: buildEmbed}
40 |
41 | if syncImport {
42 | err = api.SyncProjectImports(common.CurrentProject())
43 | if err != nil {
44 | fmt.Fprintf(os.Stderr, "Error synchronzing imports: %v\n", err)
45 | os.Exit(1)
46 | }
47 | }
48 |
49 | err = api.BuildProject(common.CurrentProject(), options)
50 | if err != nil {
51 | fmt.Fprintf(os.Stderr, "Error building project: %v\n", err)
52 | os.Exit(1)
53 | }
54 | } else {
55 | //If a jsonFile is specified in the build.
56 | //Create a new project in the temp folder and copy the bin.
57 |
58 | tempDir, err := api.GetTempDir()
59 | if err != nil {
60 | fmt.Fprintf(os.Stderr, "Error getting temp dir: %v\n", err)
61 | os.Exit(1)
62 | }
63 |
64 | api.SetVerbose(verbose)
65 | tempProject, err := api.CreateProject(tempDir, "", flogoJsonFile, "latest")
66 | if err != nil {
67 | fmt.Fprintf(os.Stderr, "Error creating temp project: %v\n", err)
68 | os.Exit(1)
69 | }
70 |
71 | common.SetCurrentProject(tempProject)
72 |
73 | options := common.BuildOptions{Shim: buildShim, OptimizeImports: buildOptimize, EmbedConfig: buildEmbed}
74 |
75 | err = api.BuildProject(common.CurrentProject(), options)
76 | if err != nil {
77 | fmt.Fprintf(os.Stderr, "Error building temp project: %v\n", err)
78 | os.Exit(1)
79 | }
80 |
81 | copyBin(verbose, tempProject)
82 | }
83 | },
84 | }
85 |
86 | func copyBin(verbose bool, tempProject common.AppProject) {
87 |
88 | currDir, err := os.Getwd()
89 | if err != nil {
90 | fmt.Fprintf(os.Stderr, "Error determining working directory: %v\n", err)
91 | os.Exit(1)
92 | }
93 |
94 | if verbose {
95 | fmt.Printf("Copying the binary from %s to %s \n", tempProject.BinDir(), currDir)
96 | }
97 |
98 | if runtime.GOOS == "windows" || api.GOOSENV == "windows" {
99 | err = os.Rename(tempProject.Executable(), filepath.Join(currDir, "main.exe"))
100 | if err != nil {
101 | fmt.Fprintf(os.Stderr, "Error renaming executable: %v\n", err)
102 | os.Exit(1)
103 | }
104 | } else {
105 | err = os.Rename(tempProject.Executable(), filepath.Join(currDir, tempProject.Name()))
106 | if err != nil {
107 | fmt.Fprintf(os.Stderr, "Error renaming executable: %v\n", err)
108 | os.Exit(1)
109 | }
110 | }
111 |
112 | if verbose {
113 | fmt.Printf("Removing the temp dir: %s\n ", tempProject.Dir())
114 | }
115 |
116 | err = os.RemoveAll(tempProject.Dir())
117 | if err != nil {
118 | fmt.Fprintf(os.Stderr, "Error removing temp dir: %v\n", err)
119 | os.Exit(1)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/commands/create.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/project-flogo/cli/api"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var flogoJsonPath string
12 | var coreVersion string
13 |
14 | func init() {
15 | CreateCmd.Flags().StringVarP(&flogoJsonPath, "file", "f", "", "specify a flogo.json to create project from")
16 | CreateCmd.Flags().StringVarP(&coreVersion, "cv", "", "", "specify core library version (ex. master)")
17 | rootCmd.AddCommand(CreateCmd)
18 | }
19 |
20 | var CreateCmd = &cobra.Command{
21 | Use: "create [flags] [appName]",
22 | Short: "create a flogo application project",
23 | Long: `Creates a flogo application project.`,
24 | Args: cobra.RangeArgs(0, 1),
25 | PersistentPreRun: func(cmd *cobra.Command, args []string) {},
26 | Run: func(cmd *cobra.Command, args []string) {
27 |
28 | api.SetVerbose(verbose)
29 | appName := ""
30 | if len(args) > 0 {
31 | appName = args[0]
32 | }
33 |
34 | currentDir, err := os.Getwd()
35 | if err != nil {
36 | fmt.Fprintf(os.Stderr, "Error determining working directory: %v\n", err)
37 | os.Exit(1)
38 | }
39 | _, err = api.CreateProject(currentDir, appName, flogoJsonPath, coreVersion)
40 | if err != nil {
41 | fmt.Fprintf(os.Stderr, "Error creating project: %v\n", err)
42 | os.Exit(1)
43 | }
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/commands/imports.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/project-flogo/cli/api"
8 | "github.com/project-flogo/cli/common"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func init() {
13 | rootCmd.AddCommand(importsCmd)
14 | importsCmd.AddCommand(importsSyncCmd)
15 | importsCmd.AddCommand(importsResolveCmd)
16 | importsCmd.AddCommand(importsListCmd)
17 | }
18 |
19 | var importsCmd = &cobra.Command{
20 | Use: "imports",
21 | Short: "manage project imports",
22 | Long: `Manage project imports of contributions and dependencies.`,
23 | Run: func(cmd *cobra.Command, args []string) {
24 |
25 | },
26 | }
27 |
28 | var importsSyncCmd = &cobra.Command{
29 | Use: "sync",
30 | Short: "sync Go imports to project imports",
31 | Long: `Synchronize Go imports to project imports.`,
32 | Run: func(cmd *cobra.Command, args []string) {
33 |
34 | err := api.SyncProjectImports(common.CurrentProject())
35 |
36 | if err != nil {
37 | fmt.Fprintf(os.Stderr, "Error synchronzing imports: %v\n", err)
38 | os.Exit(1)
39 | }
40 | },
41 | }
42 |
43 | var importsResolveCmd = &cobra.Command{
44 | Use: "resolve",
45 | Short: "resolve project imports to installed version",
46 | Long: `Resolves all project imports to current installed version.`,
47 | Run: func(cmd *cobra.Command, args []string) {
48 |
49 | err := api.ResolveProjectImports(common.CurrentProject())
50 |
51 | if err != nil {
52 | fmt.Fprintf(os.Stderr, "Error resolving import versions: %v\n", err)
53 | os.Exit(1)
54 | }
55 | },
56 | }
57 |
58 | var importsListCmd = &cobra.Command{
59 | Use: "list",
60 | Short: "list project imports",
61 | Long: `List all the project imports`,
62 | Run: func(cmd *cobra.Command, args []string) {
63 |
64 | err := api.ListProjectImports(common.CurrentProject())
65 |
66 | if err != nil {
67 | fmt.Fprintf(os.Stderr, "Error listing imports: %v\n", err)
68 | os.Exit(1)
69 | }
70 | },
71 | }
72 |
--------------------------------------------------------------------------------
/commands/install.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "github.com/project-flogo/cli/api"
9 | "github.com/project-flogo/cli/common"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | var replaceContrib string
14 | var contribBundleFile string
15 |
16 | func init() {
17 | installCmd.Flags().StringVarP(&replaceContrib, "replace", "r", "", "specify path to replacement contribution/dependency")
18 | installCmd.Flags().StringVarP(&contribBundleFile, "file", "f", "", "specify contribution bundle")
19 | rootCmd.AddCommand(installCmd)
20 | }
21 |
22 | var installCmd = &cobra.Command{
23 | Use: "install [flags] ",
24 | Short: "install a flogo contribution/dependency",
25 | Long: "Installs a flogo contribution or dependency",
26 | Run: func(cmd *cobra.Command, args []string) {
27 |
28 | if contribBundleFile != "" {
29 | err := api.InstallContribBundle(common.CurrentProject(), contribBundleFile)
30 | if err != nil {
31 | fmt.Fprintf(os.Stderr, "Error installing contribution bundle: %v\n", err)
32 | os.Exit(1)
33 | }
34 | }
35 |
36 | if replaceContrib != "" {
37 | replaceContrib = strings.Replace(replaceContrib, "@", " ", -1)
38 | err := api.InstallReplacedPackage(common.CurrentProject(), replaceContrib, args[0])
39 | if err != nil {
40 | fmt.Fprintf(os.Stderr, "Error installing contribution/dependency: %v\n", err)
41 | os.Exit(1)
42 | }
43 | } else {
44 | for _, pkg := range args {
45 | err := api.InstallPackage(common.CurrentProject(), pkg)
46 | if err != nil {
47 | fmt.Fprintf(os.Stderr, "Error installing contribution/dependency: %v\n", err)
48 | os.Exit(1)
49 | }
50 | }
51 | }
52 | },
53 | }
54 |
--------------------------------------------------------------------------------
/commands/list.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/project-flogo/cli/api"
8 | "github.com/project-flogo/cli/common"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | var json bool
13 | var orphaned bool
14 | var listFilter string
15 |
16 | func init() {
17 | listCmd.Flags().BoolVarP(&json, "json", "j", true, "print in json format")
18 | listCmd.Flags().BoolVarP(&orphaned, "orphaned", "", false, "list orphaned refs")
19 | listCmd.Flags().StringVarP(&listFilter, "filter", "", "", "apply list filter [used, unused]")
20 | rootCmd.AddCommand(listCmd)
21 | }
22 |
23 | var listCmd = &cobra.Command{
24 | Use: "list [flags]",
25 | Short: "list installed flogo contributions",
26 | Long: "List installed flogo contributions",
27 | Run: func(cmd *cobra.Command, args []string) {
28 |
29 | if orphaned {
30 | err := api.ListOrphanedRefs(common.CurrentProject(), json)
31 | if err != nil {
32 | fmt.Fprintf(os.Stderr, "Error getting orphaned refs: %v\n", err)
33 | os.Exit(1)
34 | }
35 |
36 | return
37 | }
38 |
39 | err := api.ListContribs(common.CurrentProject(), json, listFilter)
40 | if err != nil {
41 | fmt.Fprintf(os.Stderr, "Error getting list of contributions: %v\n", err)
42 | os.Exit(1)
43 | }
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/commands/plugin.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "github.com/project-flogo/cli/common"
6 | "github.com/spf13/cobra"
7 | "os"
8 | )
9 |
10 | func init() {
11 | pluginCmd.AddCommand(pluginInstallCmd)
12 | pluginCmd.AddCommand(pluginListCmd)
13 | pluginCmd.AddCommand(pluginUpdateCmd)
14 | pluginCmd.AddCommand(pluginRemoveCmd)
15 | rootCmd.AddCommand(pluginCmd)
16 | }
17 |
18 | var pluginCmd = &cobra.Command{
19 | Use: "plugin",
20 | Short: "manage CLI plugins",
21 | Long: "Manage CLI plugins",
22 | PersistentPreRun: func(cmd *cobra.Command, args []string) {
23 | common.SetVerbose(verbose)
24 | },
25 | }
26 |
27 | var pluginInstallCmd = &cobra.Command{
28 | Use: "install ",
29 | Short: "install CLI plugin",
30 | Long: "Installs a CLI plugin",
31 | Args: cobra.ExactArgs(1),
32 | Run: func(cmd *cobra.Command, args []string) {
33 |
34 | pluginPkg := args[0]
35 |
36 | fmt.Printf("Installing plugin: %s\n", pluginPkg)
37 |
38 | err := UpdateCLI(pluginPkg, UpdateOptAdd)
39 | if err != nil {
40 | fmt.Fprintf(os.Stderr, "Error adding plugin: %v\n", err)
41 | os.Exit(1)
42 | }
43 |
44 | fmt.Printf("Installed plugin: %s\n", pluginPkg)
45 | },
46 | }
47 |
48 | var pluginListCmd = &cobra.Command{
49 | Use: "list",
50 | Short: "list installed plugins",
51 | Long: "Lists installed CLI plugins",
52 | Run: func(cmd *cobra.Command, args []string) {
53 |
54 | for _, pluginPkg := range common.GetPluginPkgs() {
55 | fmt.Println(pluginPkg)
56 | }
57 | },
58 | }
59 |
60 | var pluginRemoveCmd = &cobra.Command{
61 | Use: "remove",
62 | Short: "remove installed plugins",
63 | Long: "Remove installed CLI plugins",
64 | Args: cobra.ExactArgs(1),
65 | Run: func(cmd *cobra.Command, args []string) {
66 |
67 | pluginPkg := args[0]
68 |
69 | fmt.Printf("Removing plugin: %s\n", pluginPkg)
70 |
71 | err := UpdateCLI(pluginPkg, UpdateOptRemove)
72 | if err != nil {
73 | fmt.Fprintf(os.Stderr, "Error adding plugin: %v\n", err)
74 | os.Exit(1)
75 | }
76 |
77 | fmt.Printf("Removed plugin: %s\n", pluginPkg)
78 | },
79 | }
80 |
81 | var pluginUpdateCmd = &cobra.Command{
82 | Use: "update ",
83 | Short: "update plugin",
84 | Long: "Updates the specified installed CLI plugin",
85 | Args: cobra.ExactArgs(1),
86 | Run: func(cmd *cobra.Command, args []string) {
87 |
88 | pluginPkg := args[0]
89 |
90 | fmt.Printf("Updating plugin: %s\n", pluginPkg)
91 |
92 | err := UpdateCLI(pluginPkg, UpdateOptUpdate)
93 | if err != nil {
94 | fmt.Fprintf(os.Stderr, "Error updating plugin: %v\n", err)
95 | os.Exit(1)
96 | }
97 |
98 | fmt.Printf("Updated plugin: %s\n", pluginPkg)
99 | },
100 | }
101 |
--------------------------------------------------------------------------------
/commands/pluginhelper.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "go/parser"
7 | "go/printer"
8 | "go/token"
9 | "os"
10 | "os/exec"
11 | "path/filepath"
12 | "runtime"
13 | "text/template"
14 | "time"
15 |
16 | "github.com/project-flogo/cli/common"
17 | "github.com/project-flogo/cli/util"
18 | )
19 |
20 | const (
21 | fileImportsGo = "imports.go"
22 |
23 | UpdateOptRebuild = iota
24 | UpdateOptAdd
25 | UpdateOptRemove
26 | UpdateOptUpdate
27 | )
28 |
29 | func UpdateCLI(pluginPkg string, updateOption int) error {
30 |
31 | exPath, err := os.Executable()
32 | if err != nil {
33 | return err
34 | }
35 |
36 | pluginSet := make(map[string]struct{})
37 |
38 | //add installed plugins
39 | installedPlugins := common.GetPluginPkgs()
40 | for _, aPluginPkg := range installedPlugins {
41 | pluginSet[aPluginPkg] = struct{}{}
42 | fmt.Println(aPluginPkg)
43 | }
44 |
45 | if updateOption == UpdateOptAdd {
46 | // add new plugin
47 | pluginSet[pluginPkg] = struct{}{}
48 | } else if updateOption == UpdateOptRemove {
49 | delete(pluginSet, pluginPkg)
50 | }
51 |
52 | path, ver, err := util.GetCLIInfo()
53 |
54 | tmpDir := os.TempDir()
55 | basePath := filepath.Join(tmpDir, "cli")
56 | cliCmdPath := filepath.Join(basePath, "cmd", "flogo")
57 |
58 | err = os.RemoveAll(basePath)
59 | if err != nil {
60 | fmt.Println("del err:", err)
61 | }
62 |
63 | err = util.Copy(path, basePath, false)
64 | if err != nil {
65 | return err
66 | }
67 |
68 | err = util.CreateVersionFile(cliCmdPath, ver)
69 | if err != nil {
70 | return err
71 | }
72 |
73 | err = createPluginListFile(basePath, pluginSet)
74 | if err != nil {
75 | return err
76 | }
77 |
78 | for plugin := range pluginSet {
79 | _, err := addPlugin(cliCmdPath, plugin)
80 | if err != nil {
81 | fmt.Println("error:", err)
82 | }
83 | }
84 |
85 | if updateOption == UpdateOptUpdate {
86 | err = util.ExecCmd(exec.Command("go", "get", "-u", pluginPkg), cliCmdPath)
87 | if err != nil {
88 | return err
89 | }
90 | }
91 |
92 | err = util.ExecCmd(exec.Command("go", "mod", "download"), basePath)
93 | if err != nil {
94 | return err
95 | }
96 |
97 | err = util.ExecCmd(exec.Command("go", "build"), cliCmdPath)
98 | if err != nil {
99 | //fmt.Fprintf(os.Stderr, "Error: %v\n", osErr)
100 | return err
101 | }
102 |
103 | cliExe := "flogo"
104 | if runtime.GOOS == "windows" || os.Getenv("GOOS") == "windows" {
105 | cliExe = cliExe + ".exe"
106 | }
107 |
108 | err = util.Copy(filepath.Join(cliCmdPath, cliExe), exPath, false)
109 | if err != nil {
110 | //fmt.Fprintf(os.Stderr, "Error: %v\n", osErr)
111 | return err
112 | }
113 |
114 | return nil
115 | }
116 |
117 | func addPlugin(cliCmdPath, pluginPkg string) (bool, error) {
118 |
119 | err := util.ExecCmd(exec.Command("go", "get", pluginPkg), cliCmdPath)
120 | if err != nil {
121 | return false, err
122 | }
123 |
124 | added, err := addPluginImport(cliCmdPath, pluginPkg)
125 | if err != nil {
126 | return added, err
127 | }
128 |
129 | return added, nil
130 | }
131 |
132 | func addPluginImport(cliCmdPath, pkg string) (bool, error) {
133 | importsFile := filepath.Join(cliCmdPath, fileImportsGo)
134 |
135 | fset := token.NewFileSet()
136 | file, _ := parser.ParseFile(fset, importsFile, nil, parser.ImportsOnly)
137 |
138 | if file.Imports == nil {
139 | return false, errors.New("no imports found")
140 | }
141 |
142 | successful := util.AddImport(fset, file, pkg)
143 |
144 | if successful {
145 | f, err := os.Create(importsFile)
146 | if err != nil {
147 | return false, err
148 | }
149 | defer f.Close()
150 | if err := printer.Fprint(f, fset, file); err != nil {
151 | return false, err
152 | }
153 | }
154 |
155 | return successful, nil
156 | }
157 |
158 | func createPluginListFile(basePath string, plugins map[string]struct{}) error {
159 |
160 | f, err := os.Create(filepath.Join(basePath, "common", "pluginlist.go"))
161 | if err != nil {
162 | return err
163 | }
164 | defer f.Close()
165 |
166 | err = pluginListTemplate.Execute(f, struct {
167 | Timestamp time.Time
168 | PluginList map[string]struct{}
169 | }{
170 | Timestamp: time.Now(),
171 | PluginList: plugins,
172 | })
173 |
174 | return err
175 | }
176 |
177 | var pluginListTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
178 | // {{ .Timestamp }}
179 | package common
180 |
181 | func init() {
182 |
183 | {{range $k, $v := .PluginList}}
184 | pluginPkgs = append(pluginPkgs, "{{$k}}")
185 | {{end}}
186 | }
187 | `))
188 |
--------------------------------------------------------------------------------
/commands/root.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "github.com/project-flogo/cli/api"
6 | "github.com/project-flogo/cli/common"
7 | "github.com/project-flogo/cli/util"
8 | "github.com/spf13/cobra"
9 | "os"
10 | )
11 |
12 | const (
13 | VersionTpl = `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "cli version %s" .Version}}
14 | `
15 | )
16 |
17 | var verbose bool
18 |
19 | //Root command
20 | var rootCmd = &cobra.Command{
21 | Use: "flogo [flags] [command]",
22 | Short: "flogo cli",
23 | Long: `flogo command line interface for flogo applications`,
24 |
25 | PersistentPreRun: func(cmd *cobra.Command, args []string) {
26 | preRun(cmd, args, verbose)
27 | },
28 | }
29 |
30 | func Initialize(version string) {
31 | rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "verbose output")
32 |
33 | if len(version) > 0 {
34 | rootCmd.Version = version // use version hardcoded by a "go generate" command
35 | } else {
36 | _, rootCmd.Version, _ = util.GetCLIInfo() // guess version from sources in $GOPATH/src
37 | }
38 |
39 | rootCmd.SetVersionTemplate(VersionTpl)
40 |
41 | //Get the list of commands from the registry of commands and add.
42 | commandList := common.GetPlugins()
43 |
44 | for _, command := range commandList {
45 |
46 | rootCmd.AddCommand(command)
47 | }
48 | }
49 |
50 | func Execute() {
51 |
52 | if err := rootCmd.Execute(); err != nil {
53 | fmt.Fprintf(os.Stderr, "Error: %v\n", err)
54 | os.Exit(1)
55 | }
56 | }
57 |
58 | func preRun(cmd *cobra.Command, args []string, verbose bool) {
59 | api.SetVerbose(verbose)
60 | common.SetVerbose(verbose)
61 |
62 | builtIn := cmd.Name() == "help" || cmd.Name() == "version"
63 |
64 | if len(os.Args) > 1 && !builtIn {
65 | currentDir, err := os.Getwd()
66 | if err != nil {
67 | fmt.Fprintf(os.Stderr, "Error determining working directory: %v\n", err)
68 | os.Exit(1)
69 | }
70 | appProject := api.NewAppProject(currentDir)
71 |
72 | err = appProject.Validate()
73 | if err != nil {
74 | fmt.Fprintf(os.Stderr, "Error validating project: %v\n", err)
75 | os.Exit(1)
76 | }
77 |
78 | common.SetCurrentProject(appProject)
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/commands/update.go:
--------------------------------------------------------------------------------
1 | package commands
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 |
8 | "github.com/project-flogo/cli/api"
9 | "github.com/project-flogo/cli/common"
10 | "github.com/project-flogo/cli/util"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var updateAll bool
15 |
16 | func init() {
17 | rootCmd.AddCommand(updateCmd)
18 | updateCmd.Flags().BoolVarP(&updateAll, "all", "", false, "update all contributions")
19 | }
20 |
21 | const (
22 | fJsonFile = "flogo.json"
23 | )
24 |
25 | var updateCmd = &cobra.Command{
26 | Use: "update [flags] ",
27 | Short: "update a project contribution/dependency",
28 | Long: `Updates a contribution or dependency in the project`,
29 | Run: func(cmd *cobra.Command, args []string) {
30 |
31 | updatePackage(common.CurrentProject(), args, updateAll)
32 |
33 | },
34 | }
35 |
36 | func updatePackage(project common.AppProject, args []string, all bool) {
37 |
38 | if !all {
39 | if len(args) < 1 {
40 | fmt.Fprintf(os.Stderr, "Contribution not specified")
41 | os.Exit(1)
42 | }
43 | err := api.UpdatePkg(project, args[0])
44 |
45 | if err != nil {
46 | fmt.Fprintf(os.Stderr, "Error updating contribution/dependency: %v\n", err)
47 | os.Exit(1)
48 | }
49 |
50 | } else {
51 | //Get all imports
52 | imports, err := util.GetAppImports(filepath.Join(project.Dir(), fJsonFile), project.DepManager(), true)
53 | if err != nil {
54 | fmt.Fprintf(os.Stderr, "Error updating all contributions: %v\n", err)
55 | os.Exit(1)
56 | }
57 | //Update each package in imports
58 | for _, imp := range imports.GetAllImports() {
59 |
60 | err = api.UpdatePkg(project, imp.GoGetImportPath())
61 |
62 | if err != nil {
63 | fmt.Fprintf(os.Stderr, "Error updating contribution/dependency: %v\n", err)
64 | os.Exit(1)
65 | }
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/common/build.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | type BuildOptions struct {
4 | OptimizeImports bool
5 | EmbedConfig bool
6 | Shim string
7 | }
8 |
9 | type Builder interface {
10 | Build(project AppProject) error
11 | }
12 |
13 | type BuildPreProcessor interface {
14 | DoPreProcessing(project AppProject, options BuildOptions) error
15 | }
16 |
17 | type BuildPostProcessor interface {
18 | DoPostProcessing(project AppProject) error
19 | }
20 |
21 | var buildPreProcessors []BuildPreProcessor
22 | var buildPostProcessors []BuildPostProcessor
23 |
24 | func RegisterBuildPreProcessor(processor BuildPreProcessor) {
25 | buildPreProcessors = append(buildPreProcessors, processor)
26 | }
27 |
28 | func BuildPreProcessors() []BuildPreProcessor {
29 | return buildPreProcessors
30 | }
31 |
32 | func RegisterBuildPostProcessor(processor BuildPostProcessor) {
33 | buildPostProcessors = append(buildPostProcessors, processor)
34 | }
35 |
36 | func BuildPostProcessors() []BuildPostProcessor {
37 | return buildPostProcessors
38 | }
39 |
40 |
41 |
--------------------------------------------------------------------------------
/common/env.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "github.com/project-flogo/cli/util"
5 | )
6 |
7 | var verbose = false
8 | var appProject AppProject
9 |
10 | func SetVerbose(enable bool) {
11 | verbose = enable
12 | util.SetVerbose(enable)
13 | }
14 |
15 | func Verbose() bool {
16 | return verbose
17 | }
18 |
19 | func CurrentProject() AppProject {
20 | return appProject
21 | }
22 |
23 | func SetCurrentProject(project AppProject) {
24 | appProject = project
25 | }
26 |
--------------------------------------------------------------------------------
/common/plugin.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 | )
6 |
7 | var commands []*cobra.Command
8 | var pluginPkgs []string
9 |
10 | func RegisterPlugin(command *cobra.Command) {
11 | commands = append(commands, command)
12 | }
13 |
14 | func GetPlugins() []*cobra.Command {
15 |
16 | tmp := make([]*cobra.Command, len(commands))
17 | copy(tmp, commands)
18 |
19 | return tmp
20 | }
21 |
22 | func GetPluginPkgs() []string{
23 | return pluginPkgs
24 | }
25 |
--------------------------------------------------------------------------------
/common/project.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import "github.com/project-flogo/cli/util"
4 |
5 | type AppProject interface {
6 | Validate() error
7 | Name() string
8 | Dir() string
9 | BinDir() string
10 | SrcDir() string
11 | Executable() string
12 | AddImports(ignoreError bool, addToJson bool, imports ...util.Import) error
13 | RemoveImports(imports ...string) error
14 | GetPath(flogoImport util.Import) (string, error)
15 | DepManager() util.DepManager
16 |
17 | GetGoImports(withVersion bool) ([]util.Import, error)
18 | }
19 |
--------------------------------------------------------------------------------
/docs/commands.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | # Commands
8 |
9 | - [build](#build) - Build the flogo application
10 | - [create](#create) - Create a flogo application project
11 | - [help](#help) - Help about any command
12 | - [imports](#imports) - Manage project dependency imports
13 | - [install](#install) - Install a flogo contribution/dependency
14 | - [list](#list) - List installed flogo contributions
15 | - [plugin](#plugin) - Manage CLI plugins
16 | - [update](#update) - Update an application contribution/dependency
17 |
18 | ### Global Flags
19 | ```
20 | --verbose verbose output
21 | ```
22 |
23 |
24 | ## build
25 |
26 | This command is used to build the application.
27 |
28 | ```
29 | Usage:
30 | flogo build [flags]
31 |
32 | Flags:
33 | -e, --embed embed configuration in binary
34 | -f, --file string specify a flogo.json to build
35 | -o, --optimize optimize build
36 | --shim string use shim trigger
37 | ```
38 | _**Note:** the optimize flag removes unused trigger, acitons and activites from the built binary._
39 |
40 |
41 | ### Examples
42 | Build the current project application
43 |
44 | ```bash
45 | $ flogo build
46 | ```
47 | Build an application directly from a flogo.json
48 |
49 | ```bash
50 | $ flogo build -f flogo.json
51 | ```
52 | _**Note:** this command will only generate the application binary for the specified json and can be run outside of a flogo application project_
53 |
54 | ## create
55 |
56 | This command is used to create a flogo application project.
57 |
58 | ```
59 | Usage:
60 | flogo create [flags] [appName]
61 |
62 | Flags:
63 | --cv string specify core library version (ex. master)
64 | -f, --file string specify a flogo.json to create project from
65 | ```
66 |
67 | _**Note:** when using the --cv flag to specify a version, the exact version specified might not be used the project. The application will install the version that satisfies all the dependency constraints. Typically this flag is used when trying to use the master version of the core library._
68 |
69 | ### Examples
70 |
71 | Create a base sample project with a specific name:
72 |
73 | ```
74 | $ flogo create my_app
75 | ```
76 |
77 | Create a project from an existing flogo application descriptor:
78 |
79 | ```
80 | $ flogo create -f myapp.json
81 | ```
82 |
83 | ## help
84 |
85 | This command shows help for any flogo commands.
86 |
87 | ```
88 | Usage:
89 | flogo help [command]
90 | ```
91 |
92 | ### Examples
93 | Get help for the build command:
94 |
95 | ```bash
96 | $ flogo help build
97 | ```
98 | ## imports
99 |
100 | This command helps manage project imports of contributions and dependencies.
101 |
102 | ```
103 | Usage:
104 | flogo imports [command]
105 |
106 | Available Commands:
107 | sync sync Go imports to project imports
108 | resolve resolve project imports to installed version
109 | list list project imports
110 | ```
111 |
112 | ## install
113 |
114 | This command is used to install a flogo contribution or dependency.
115 |
116 | ```
117 | Usage:
118 | flogo install [flags]
119 |
120 | Flags:
121 | -f, --file string specify contribution bundle
122 | -r, --replace string specify path to replacement contribution/dependency
123 | ```
124 |
125 | ### Examples
126 | Install the basic REST trigger:
127 |
128 | ```bash
129 | $ flogo install github.com/project-flogo/contrib/trigger/rest
130 | ```
131 | Install a contribution that you are currently developing on your computer:
132 |
133 | ```bash
134 | $ flogo install -r /tmp/dev/myactivity github.com/myuser/myactivity
135 | ```
136 |
137 | Install a contribution that is being developed by different person on their fork:
138 |
139 | ```bash
140 | $ flogo install -r github.com/otherusr/myactivity@master github.com/myuser/myactivity
141 | ```
142 |
143 | ## list
144 |
145 | This command lists installed contributions in your application
146 |
147 | ```
148 | Usage:
149 | flogo list [flags]
150 |
151 | Flags:
152 | --filter string apply list filter [used, unused]
153 | -j, --json print in json format (default true)
154 | --orphaned list orphaned refs
155 | ```
156 | _**Note** orphaned refs are `ref` entries that use an import alias (ex. `"ref": "#log"`) which has no corresponding import._
157 |
158 | ### Examples
159 | List all installed contributions:
160 |
161 | ```bash
162 | $ flogo list
163 | ```
164 | List all contributions directly used by the application:
165 |
166 | ```bash
167 | $ flogo list --filter used
168 | ```
169 | _**Note:** the results of this command are the only contributions that will be compiled into your application when using `flogo build` with the optimize flag_
170 |
171 |
172 | ## plugin
173 |
174 | This command is used to install a plugin to the Flogo CLI.
175 |
176 | ```
177 | Usage:
178 | flogo plugin [command]
179 |
180 | Available Commands:
181 | install install CLI plugin
182 | list list installed plugins
183 | update update plugin
184 | ```
185 |
186 | ### Examples
187 | List all installed plugins:
188 |
189 | ```bash
190 | $ flogo plugin list
191 | ```
192 | Install the legacy support plugin:
193 |
194 | ```bash
195 | $ flogo plugin install github.com/project-flogo/legacybridge/cli`
196 | ```
197 | _**Note:** more information on the legacy support plugin can be found [here](https://github.com/project-flogo/legacybridge/tree/master/cli)_
198 |
199 | Install and use custom plugin:
200 |
201 | ```
202 | $ flogo plugin install github.com/myuser/myplugin
203 |
204 | $ flogo `your_command`
205 | ```
206 |
207 | More information on Flogo CLI plugins can be found [here](plugins.md)
208 |
209 | ## update
210 |
211 | This command updates a contribution or dependency in the project.
212 |
213 | ```
214 | Usage:
215 | flogo update [flags]
216 |
217 | ```
218 | ### Examples
219 | Update you log activity to master:
220 |
221 | ```bash
222 | $ flogo update github.com/project-flogo/contrib/activity/log@master
223 | ```
224 |
225 | Update your flogo core library to latest master:
226 |
227 | ```bash
228 | $ flogo update github.com/project-flogo/core@master
229 | ```
230 |
--------------------------------------------------------------------------------
/docs/plugins.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | # Plugins
8 |
9 | The Flogo CLI has support for plugins. These plugins can be used to extend the Flogo CLI command.
10 |
11 | ## Creating a CLI plugin
12 |
13 | First lets setup the go project:
14 |
15 | ```bash
16 | # Create a directory for your plugin project
17 | $ mkdir myplugin
18 |
19 | # Go to the directory
20 | $ cd myplugin
21 |
22 | # Initialize the Go module information
23 | $ go mod init github.com/myuser/myplugin
24 |
25 | # Edit/Create the plugin code
26 | $ vi myplugin.go
27 | ```
28 |
29 | Next lets create the code for our simple plugin:
30 |
31 | ```go
32 | package myplugin
33 |
34 | import (
35 | "fmt"
36 | "github.com/project-flogo/cli/common" // Flogo CLI support code
37 | "github.com/spf13/cobra"
38 | )
39 |
40 | func init() {
41 | common.RegisterPlugin(myCmd)
42 | }
43 |
44 | var myCmd = &cobra.Command{
45 | Use: "mycmd",
46 | Short: "says hello world",
47 | Long: "This plugin command says hello world",
48 | Run: func(cmd *cobra.Command, args []string) {
49 | fmt.Println("Hello World")
50 | },
51 | }
52 | ```
53 | Once you save the code, we need to fix up the Go Module dependencies.
54 |
55 | ```bash
56 | $ go mod tidy
57 | ```
58 |
59 | Now you are ready to test out your plugin. First you must host your plugin in your git repository. Then you are ready to install and run your plugin
60 |
61 | ```
62 | # Install your plugin
63 | $ flogo plugin install github.com/myuser/myplugin
64 |
65 | # Run your new plugin command
66 | $ flogo mycmd
67 | ```
68 |
--------------------------------------------------------------------------------
/examples/simple-plugin/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/myuser/myplugin
2 |
3 | require (
4 | github.com/project-flogo/cli v0.9.0
5 | github.com/spf13/cobra v0.0.5
6 | )
7 |
8 | go 1.12
9 |
--------------------------------------------------------------------------------
/examples/simple-plugin/myplugin.go:
--------------------------------------------------------------------------------
1 | package myplugin
2 |
3 | import (
4 | "fmt"
5 | "github.com/project-flogo/cli/common" // Flogo CLI support code
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | func init() {
10 | common.RegisterPlugin(myCmd)
11 | }
12 |
13 | var myCmd = &cobra.Command{
14 | Use: "mycmd",
15 | Short: "says hello world",
16 | Long: `This plugin command says hello world`,
17 | Run: func(cmd *cobra.Command, args []string) {
18 | fmt.Println("Hello World")
19 | },
20 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/project-flogo/cli
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/coreos/go-semver v0.3.1
7 | github.com/msoap/byline v1.1.1
8 | github.com/project-flogo/core v1.6.5
9 | github.com/spf13/cobra v1.7.0
10 | github.com/stretchr/testify v1.8.0
11 | )
12 |
13 | require (
14 | github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
15 | github.com/cpuguy83/go-md2man v1.0.10 // indirect
16 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
17 | github.com/mitchellh/go-homedir v1.1.0 // indirect
18 | github.com/spf13/pflag v1.0.5 // indirect
19 | github.com/spf13/viper v1.3.2 // indirect
20 | go.uber.org/atomic v1.11.0 // indirect
21 | go.uber.org/multierr v1.11.0 // indirect
22 | go.uber.org/zap v1.24.0 // indirect
23 | )
24 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2 | github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
3 | github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
4 | github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
5 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
6 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
7 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
8 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
9 | github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
10 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
11 | github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
12 | github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
13 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
14 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
15 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
16 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
18 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
19 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
20 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
21 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
22 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
23 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
24 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
25 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
26 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
27 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
28 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
29 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
30 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
31 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
32 | github.com/msoap/byline v1.1.1 h1:imxWvm9wIHNGePF/peiOxcL1vgVLK3/qKsMW75XZn9c=
33 | github.com/msoap/byline v1.1.1/go.mod h1:E2oCrXddpzrmu4NmrwEv4Qiyweo62Yp3+w3IN3X2sq8=
34 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
35 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
36 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
37 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
38 | github.com/project-flogo/core v0.9.5-beta.1 h1:q+LHD1dJsN/e6fqSmgJ0CZ2H8P+zGw3B7gkbE3drM/g=
39 | github.com/project-flogo/core v0.9.5-beta.1/go.mod h1:QGWi7TDLlhGUaYH3n/16ImCuulbEHGADYEXyrcHhX7U=
40 | github.com/project-flogo/core v1.6.5 h1:YR0FYwdbf24e6UM7AAcFIyxpDO7jlhs+q5JTuJTAY6A=
41 | github.com/project-flogo/core v1.6.5/go.mod h1:fapTXUhLxDeAHyb6eMkuwnYswO8FpZJAMat055QVdJE=
42 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
43 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
44 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
45 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
46 | github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
47 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
48 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
49 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
50 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
51 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
52 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
53 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
54 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
55 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
56 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
57 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
58 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
59 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
60 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
61 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
62 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
63 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
64 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
65 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
66 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
67 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
68 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
69 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
70 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
71 | github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
72 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
73 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
74 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
75 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
76 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
77 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
78 | go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
79 | go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
80 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
81 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
82 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
83 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
84 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
85 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
86 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
87 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
88 | go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
89 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
90 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
91 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
92 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
93 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
94 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
95 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
96 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
97 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
98 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
99 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
100 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
101 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
102 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
103 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
104 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
105 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
106 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
107 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
108 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
109 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
110 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
111 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
112 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
113 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
114 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
115 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
116 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
117 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
118 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
119 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
120 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
121 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
122 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
123 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
124 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
125 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
126 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
127 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
128 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
129 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
130 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
131 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
132 |
--------------------------------------------------------------------------------
/test/flogo.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "_APP_NAME_",
3 | "type": "flogo:app",
4 | "version": "0.0.1",
5 | "description": "My flogo application description",
6 | "appModel": "1.1.0",
7 | "imports": [
8 | "github.com/project-flogo/contrib/trigger/rest",
9 | "github.com/project-flogo/flow",
10 | "github.com/project-flogo/contrib/activity/log"
11 | ],
12 | "triggers": [
13 | {
14 | "id": "my_rest_trigger",
15 | "ref": "#rest",
16 | "settings": {
17 | "port": "8888"
18 | },
19 | "handlers": [
20 | {
21 | "settings": {
22 | "method": "GET",
23 | "path": "/test/:val"
24 | },
25 | "action": {
26 | "ref": "#flow",
27 | "settings": {
28 | "flowURI": "res://flow:simple_flow"
29 | },
30 | "input": {
31 | "in": "$.pathParams.val"
32 | }
33 | }
34 | }
35 | ]
36 | }
37 | ],
38 | "resources": [
39 | {
40 | "id": "flow:simple_flow",
41 | "data": {
42 | "name": "simple_flow",
43 | "metadata": {
44 | "input": [
45 | { "name": "in", "type": "string", "value": "test" }
46 | ],
47 | "output": [
48 | { "name": "out", "type": "string" }
49 | ]
50 | },
51 | "tasks": [
52 | {
53 | "id": "log",
54 | "name": "Log Message",
55 | "activity": {
56 | "ref": "#log",
57 | "input": {
58 | "message": "$flow.in",
59 | "addDetails": false
60 | }
61 | }
62 | }
63 | ]
64 | }
65 | }
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/util/app.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "strings"
9 | )
10 |
11 | // PartialAppDescriptor is the descriptor for a Flogo application
12 | type PartialAppDescriptor struct {
13 | AppModel string `json:"appModel"`
14 | Imports []string `json:"imports"`
15 | Triggers []interface{} `json:"triggers"`
16 | Resources []interface{} `json:"resources"`
17 | Actions []interface{} `json:"actions"`
18 | }
19 |
20 | type void struct{}
21 |
22 | type AppImportDetails struct {
23 | Imp Import
24 | ContribDesc *FlogoContribDescriptor
25 | TopLevel bool // a toplevel import i.e. from imports section
26 | HasAliasRef bool // imports alias is used by a contrib reference
27 | HasDirectRef bool // a direct reference exists for this import
28 | }
29 |
30 | func (d *AppImportDetails) Referenced() bool {
31 | return d.HasAliasRef || d.HasDirectRef
32 | }
33 |
34 | func (d *AppImportDetails) IsCoreContrib() bool {
35 |
36 | if d.ContribDesc == nil {
37 | return false
38 | }
39 | ct := d.ContribDesc.GetContribType()
40 |
41 | switch ct {
42 | case "action", "trigger", "activity":
43 | return true
44 | default:
45 | return false
46 | }
47 | }
48 |
49 | type AppImports struct {
50 | imports map[string]*AppImportDetails
51 | orphanedRef map[string]void
52 |
53 | resolveContribs bool
54 | depManager DepManager
55 | }
56 |
57 | func (ai *AppImports) addImports(imports []string) error {
58 | for _, anImport := range imports {
59 | flogoImport, err := ParseImport(anImport)
60 | if err != nil {
61 | return err
62 | }
63 |
64 | if _, exists := ai.imports[flogoImport.GoImportPath()]; exists {
65 | //todo warn about duplicate import?
66 | continue
67 | }
68 |
69 | details, err := ai.newImportDetails(flogoImport)
70 | if err != nil {
71 | return err
72 | }
73 | details.TopLevel = true
74 |
75 | ai.imports[flogoImport.GoImportPath()] = details
76 | }
77 |
78 | return nil
79 | }
80 |
81 | func (ai *AppImports) addReference(ref string, contribType string) error {
82 | cleanedRef := strings.TrimSpace(ref)
83 |
84 | if cleanedRef[0] == '#' {
85 | if !ai.resolveContribs {
86 | // wont be able to determine contribTypes for existing imports, so just return
87 | return nil
88 | }
89 |
90 | alias := cleanedRef[1:]
91 | found := false
92 | for _, importDetails := range ai.imports {
93 |
94 | if importDetails.TopLevel {
95 | // alias refs can only be to toplevel imports
96 | if importDetails.Imp.CanonicalAlias() == alias && importDetails.ContribDesc != nil &&
97 | importDetails.ContribDesc.GetContribType() == contribType {
98 | importDetails.HasAliasRef = true
99 | found = true
100 | break
101 | }
102 | }
103 | }
104 |
105 | if !found {
106 | ai.orphanedRef[cleanedRef] = void{}
107 | }
108 |
109 | } else {
110 | flogoImport, err := ParseImport(ref)
111 | if err != nil {
112 | return err
113 | }
114 |
115 | if imp, exists := ai.imports[flogoImport.GoImportPath()]; exists {
116 | if !imp.TopLevel {
117 | //already accounted for
118 | return nil
119 | }
120 |
121 | imp.HasDirectRef = true
122 | return nil
123 | }
124 |
125 | //doesn't exists so add new import
126 | details, err := ai.newImportDetails(flogoImport)
127 | if err != nil {
128 | return err
129 | }
130 |
131 | ai.imports[flogoImport.GoImportPath()] = details
132 | }
133 |
134 | return nil
135 | }
136 |
137 | func (ai *AppImports) newImportDetails(anImport Import) (*AppImportDetails, error) {
138 | details := &AppImportDetails{Imp: anImport}
139 |
140 | if ai.resolveContribs {
141 | desc, err := GetContribDescriptorFromImport(ai.depManager, anImport)
142 | if err != nil {
143 | return nil, err
144 | }
145 | details.ContribDesc = desc
146 | }
147 |
148 | return details, nil
149 | }
150 |
151 | func (ai *AppImports) GetOrphanedReferences() []string {
152 | var refs []string
153 | for ref := range ai.orphanedRef {
154 | refs = append(refs, ref)
155 | }
156 |
157 | return refs
158 | }
159 |
160 | func (ai *AppImports) GetAllImports() []Import {
161 | var allImports []Import
162 | for _, details := range ai.imports {
163 | allImports = append(allImports, details.Imp)
164 | }
165 |
166 | return allImports
167 | }
168 |
169 | func (ai *AppImports) GetAllImportDetails() []*AppImportDetails {
170 |
171 | var allImports []*AppImportDetails
172 | for _, details := range ai.imports {
173 | allImports = append(allImports, details)
174 | }
175 |
176 | return allImports
177 | }
178 |
179 | func GetAppImports(appJsonFile string, depManager DepManager, resolveContribs bool) (*AppImports, error) {
180 | appJson, err := os.Open(appJsonFile)
181 | if err != nil {
182 | return nil, err
183 | }
184 |
185 | bytes, err := ioutil.ReadAll(appJson)
186 | if err != nil {
187 | return nil, err
188 | }
189 |
190 | appDesc := &PartialAppDescriptor{}
191 | err = json.Unmarshal(bytes, appDesc)
192 | if err != nil {
193 | fmt.Fprintf(os.Stderr, "Unable to unmarshal flogo app json: %s", appJsonFile)
194 | return nil, err
195 | }
196 |
197 | ai := &AppImports{depManager: depManager, resolveContribs: resolveContribs}
198 | ai.imports = make(map[string]*AppImportDetails)
199 | ai.orphanedRef = make(map[string]void)
200 |
201 | err = ai.addImports(appDesc.Imports)
202 | if err != nil {
203 | return nil, err
204 | }
205 |
206 | err = extractAppReferences(ai, appDesc)
207 |
208 | return ai, err
209 | }
210 |
211 | func extractAppReferences(ai *AppImports, appDesc *PartialAppDescriptor) error {
212 |
213 | //triggers
214 | for _, trg := range appDesc.Triggers {
215 | if trgMap, ok := trg.(map[string]interface{}); ok {
216 |
217 | // a ref should exists for every trigger
218 | if refVal, ok := trgMap["ref"]; ok {
219 | if strVal, ok := refVal.(string); ok {
220 | err := ai.addReference(strVal, "trigger")
221 | if err != nil {
222 | return err
223 | }
224 | }
225 | }
226 |
227 | // actions are under handlers, so assume an action contribType
228 | err := extractReferences(ai, trgMap["handlers"], "action")
229 | if err != nil {
230 | return err
231 | }
232 | }
233 | }
234 |
235 | //in actions section, refs should be to actions
236 | err := extractReferences(ai, appDesc.Actions, "action") //action
237 | if err != nil {
238 | return err
239 | }
240 |
241 | //in resources section, refs should be to activities
242 | err = extractReferences(ai, appDesc.Resources, "activity") //activity
243 | if err != nil {
244 | return err
245 | }
246 |
247 | return nil
248 | }
249 |
250 | func extractReferences(ai *AppImports, item interface{}, contribType string) error {
251 | switch t := item.(type) {
252 | case map[string]interface{}:
253 | for key, val := range t {
254 | if strVal, ok := val.(string); ok {
255 | if key == "ref" {
256 | err := ai.addReference(strVal, contribType)
257 | if err != nil {
258 | return err
259 | }
260 | }
261 | } else {
262 | err := extractReferences(ai, val, contribType)
263 | if err != nil {
264 | return err
265 | }
266 | }
267 | }
268 | case []interface{}:
269 | for _, val := range t {
270 | err := extractReferences(ai, val, contribType)
271 | if err != nil {
272 | return err
273 | }
274 | }
275 | default:
276 | }
277 |
278 | return nil
279 | }
280 |
--------------------------------------------------------------------------------
/util/ast.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "go/ast"
6 | "go/token"
7 | "strconv"
8 | "strings"
9 | )
10 |
11 | // Copyright 2013 The Go Authors. All rights reserved.
12 | // Use of this source code is governed by a BSD-style
13 | // license that can be found in the LICENSE file.
14 |
15 | // code from: https://github.com/golang/tools/blob/master/go/ast/astutil/imports.go
16 |
17 | // AddImport adds the import path to the file f, if absent.
18 | func AddImport(fset *token.FileSet, f *ast.File, ipath string) (added bool) {
19 | return addNamedImport(fset, f, "_", ipath)
20 | }
21 |
22 | // AddNamedImport adds the import path to the file f, if absent.
23 | // If name is not empty, it is used to rename the import.
24 | //
25 | // For example, calling
26 | // AddNamedImport(fset, f, "pathpkg", "path")
27 | // adds
28 | // import pathpkg "path"
29 | func addNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) {
30 | if imports(f, ipath) {
31 | return false
32 | }
33 |
34 | newImport := &ast.ImportSpec{
35 | Path: &ast.BasicLit{
36 | Kind: token.STRING,
37 | Value: strconv.Quote(ipath),
38 | },
39 | }
40 | if name != "" {
41 | newImport.Name = &ast.Ident{Name: name}
42 | }
43 |
44 | // Find an import decl to add to.
45 | // The goal is to find an existing import
46 | // whose import path has the longest shared
47 | // prefix with ipath.
48 | var (
49 | bestMatch = -1 // length of longest shared prefix
50 | lastImport = -1 // index in f.Decls of the file's final import decl
51 | impDecl *ast.GenDecl // import decl containing the best match
52 | impIndex = -1 // spec index in impDecl containing the best match
53 |
54 | isThirdPartyPath = isThirdParty(ipath)
55 | )
56 | for i, decl := range f.Decls {
57 | gen, ok := decl.(*ast.GenDecl)
58 | if ok && gen.Tok == token.IMPORT {
59 | lastImport = i
60 | // Do not add to import "C", to avoid disrupting the
61 | // association with its doc comment, breaking cgo.
62 | if declImports(gen, "C") {
63 | continue
64 | }
65 |
66 | // Match an empty import decl if that's all that is available.
67 | if len(gen.Specs) == 0 && bestMatch == -1 {
68 | impDecl = gen
69 | }
70 |
71 | // Compute longest shared prefix with imports in this group and find best
72 | // matched import spec.
73 | // 1. Always prefer import spec with longest shared prefix.
74 | // 2. While match length is 0,
75 | // - for stdlib package: prefer first import spec.
76 | // - for third party package: prefer first third party import spec.
77 | // We cannot use last import spec as best match for third party package
78 | // because grouped imports are usually placed last by goimports -local
79 | // flag.
80 | // See issue #19190.
81 | seenAnyThirdParty := false
82 | for j, spec := range gen.Specs {
83 | impspec := spec.(*ast.ImportSpec)
84 | p := importPath(impspec)
85 | n := matchLen(p, ipath)
86 | if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) {
87 | bestMatch = n
88 | impDecl = gen
89 | impIndex = j
90 | }
91 | seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p)
92 | }
93 | }
94 | }
95 |
96 | // If no import decl found, add one after the last import.
97 | if impDecl == nil {
98 | impDecl = &ast.GenDecl{
99 | Tok: token.IMPORT,
100 | }
101 | if lastImport >= 0 {
102 | impDecl.TokPos = f.Decls[lastImport].End()
103 | } else {
104 | // There are no existing imports.
105 | // Our new import, preceded by a blank line, goes after the package declaration
106 | // and after the comment, if any, that starts on the same line as the
107 | // package declaration.
108 | impDecl.TokPos = f.Package
109 |
110 | file := fset.File(f.Package)
111 | pkgLine := file.Line(f.Package)
112 | for _, c := range f.Comments {
113 | if file.Line(c.Pos()) > pkgLine {
114 | break
115 | }
116 | // +2 for a blank line
117 | impDecl.TokPos = c.End() + 2
118 | }
119 | }
120 | f.Decls = append(f.Decls, nil)
121 | copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
122 | f.Decls[lastImport+1] = impDecl
123 | }
124 |
125 | // Insert new import at insertAt.
126 | insertAt := 0
127 | if impIndex >= 0 {
128 | // insert after the found import
129 | insertAt = impIndex + 1
130 | }
131 | impDecl.Specs = append(impDecl.Specs, nil)
132 | copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
133 | impDecl.Specs[insertAt] = newImport
134 | pos := impDecl.Pos()
135 | if insertAt > 0 {
136 | // If there is a comment after an existing import, preserve the comment
137 | // position by adding the new import after the comment.
138 | if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil {
139 | pos = spec.Comment.End()
140 | } else {
141 | // Assign same position as the previous import,
142 | // so that the sorter sees it as being in the same block.
143 | pos = impDecl.Specs[insertAt-1].Pos()
144 | }
145 | }
146 | if newImport.Name != nil {
147 | newImport.Name.NamePos = pos
148 | }
149 | newImport.Path.ValuePos = pos
150 | newImport.EndPos = pos
151 |
152 | // Clean up parens. impDecl contains at least one spec.
153 | if len(impDecl.Specs) == 1 {
154 | // Remove unneeded parens.
155 | impDecl.Lparen = token.NoPos
156 | } else if !impDecl.Lparen.IsValid() {
157 | // impDecl needs parens added.
158 | impDecl.Lparen = impDecl.Specs[0].Pos()
159 | }
160 |
161 | f.Imports = append(f.Imports, newImport)
162 |
163 | if len(f.Decls) <= 1 {
164 | return true
165 | }
166 |
167 | // Merge all the import declarations into the first one.
168 | var first *ast.GenDecl
169 | for i := 0; i < len(f.Decls); i++ {
170 | decl := f.Decls[i]
171 | gen, ok := decl.(*ast.GenDecl)
172 | if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
173 | continue
174 | }
175 | if first == nil {
176 | first = gen
177 | continue // Don't touch the first one.
178 | }
179 | // We now know there is more than one package in this import
180 | // declaration. Ensure that it ends up parenthesized.
181 | first.Lparen = first.Pos()
182 | // Move the imports of the other import declaration to the first one.
183 | for _, spec := range gen.Specs {
184 | spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
185 | first.Specs = append(first.Specs, spec)
186 | }
187 | f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
188 | i--
189 | }
190 |
191 | return true
192 | }
193 |
194 | func isThirdParty(importPath string) bool {
195 | // Third party package import path usually contains "." (".com", ".org", ...)
196 | // This logic is taken from golang.org/x/tools/imports package.
197 | return strings.Contains(importPath, ".")
198 | }
199 |
200 | // DeleteImport deletes the import path from the file f, if present.
201 | func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
202 | return deleteNamedImport(fset, f, "_", path)
203 | }
204 |
205 | // DeleteNamedImport deletes the import with the given name and path from the file f, if present.
206 | func deleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
207 | var delspecs []*ast.ImportSpec
208 | var delcomments []*ast.CommentGroup
209 |
210 | // Find the import nodes that import path, if any.
211 | for i := 0; i < len(f.Decls); i++ {
212 | decl := f.Decls[i]
213 | gen, ok := decl.(*ast.GenDecl)
214 | if !ok || gen.Tok != token.IMPORT {
215 | continue
216 | }
217 | for j := 0; j < len(gen.Specs); j++ {
218 | spec := gen.Specs[j]
219 | impspec := spec.(*ast.ImportSpec)
220 | if impspec.Name == nil && name != "" {
221 | continue
222 | }
223 | if impspec.Name != nil && impspec.Name.Name != name {
224 | continue
225 | }
226 | if importPath(impspec) != path {
227 | continue
228 | }
229 |
230 | // We found an import spec that imports path.
231 | // Delete it.
232 | delspecs = append(delspecs, impspec)
233 | deleted = true
234 | copy(gen.Specs[j:], gen.Specs[j+1:])
235 | gen.Specs = gen.Specs[:len(gen.Specs)-1]
236 |
237 | // If this was the last import spec in this decl,
238 | // delete the decl, too.
239 | if len(gen.Specs) == 0 {
240 | copy(f.Decls[i:], f.Decls[i+1:])
241 | f.Decls = f.Decls[:len(f.Decls)-1]
242 | i--
243 | break
244 | } else if len(gen.Specs) == 1 {
245 | if impspec.Doc != nil {
246 | delcomments = append(delcomments, impspec.Doc)
247 | }
248 | if impspec.Comment != nil {
249 | delcomments = append(delcomments, impspec.Comment)
250 | }
251 | for _, cg := range f.Comments {
252 | // Found comment on the same line as the import spec.
253 | if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line {
254 | delcomments = append(delcomments, cg)
255 | break
256 | }
257 | }
258 |
259 | spec := gen.Specs[0].(*ast.ImportSpec)
260 |
261 | // Move the documentation right after the import decl.
262 | if spec.Doc != nil {
263 | for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line {
264 | fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
265 | }
266 | }
267 | for _, cg := range f.Comments {
268 | if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line {
269 | for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line {
270 | fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
271 | }
272 | break
273 | }
274 | }
275 | }
276 | if j > 0 {
277 | lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
278 | lastLine := fset.Position(lastImpspec.Path.ValuePos).Line
279 | line := fset.Position(impspec.Path.ValuePos).Line
280 |
281 | // We deleted an entry but now there may be
282 | // a blank line-sized hole where the import was.
283 | if line-lastLine > 1 {
284 | // There was a blank line immediately preceding the deleted import,
285 | // so there's no need to close the hole.
286 | // Do nothing.
287 | } else if line != fset.File(gen.Rparen).LineCount() {
288 | // There was no blank line. Close the hole.
289 | fset.File(gen.Rparen).MergeLine(line)
290 | }
291 | }
292 | j--
293 | }
294 | }
295 |
296 | // Delete imports from f.Imports.
297 | for i := 0; i < len(f.Imports); i++ {
298 | imp := f.Imports[i]
299 | for j, del := range delspecs {
300 | if imp == del {
301 | copy(f.Imports[i:], f.Imports[i+1:])
302 | f.Imports = f.Imports[:len(f.Imports)-1]
303 | copy(delspecs[j:], delspecs[j+1:])
304 | delspecs = delspecs[:len(delspecs)-1]
305 | i--
306 | break
307 | }
308 | }
309 | }
310 |
311 | // Delete comments from f.Comments.
312 | for i := 0; i < len(f.Comments); i++ {
313 | cg := f.Comments[i]
314 | for j, del := range delcomments {
315 | if cg == del {
316 | copy(f.Comments[i:], f.Comments[i+1:])
317 | f.Comments = f.Comments[:len(f.Comments)-1]
318 | copy(delcomments[j:], delcomments[j+1:])
319 | delcomments = delcomments[:len(delcomments)-1]
320 | i--
321 | break
322 | }
323 | }
324 | }
325 |
326 | if len(delspecs) > 0 {
327 | panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
328 | }
329 |
330 | return
331 | }
332 |
333 | //
334 | //// RewriteImport rewrites any import of path oldPath to path newPath.
335 | //func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
336 | // for _, imp := range f.Imports {
337 | // if importPath(imp) == oldPath {
338 | // rewrote = true
339 | // // record old End, because the default is to compute
340 | // // it using the length of imp.Path.Value.
341 | // imp.EndPos = imp.End()
342 | // imp.Path.Value = strconv.Quote(newPath)
343 | // }
344 | // }
345 | // return
346 | //}
347 | //
348 | //// UsesImport reports whether a given import is used.
349 | //func UsesImport(f *ast.File, path string) (used bool) {
350 | // spec := importSpec(f, path)
351 | // if spec == nil {
352 | // return
353 | // }
354 | //
355 | // name := spec.Name.String()
356 | // switch name {
357 | // case "":
358 | // // If the package name is not explicitly specified,
359 | // // make an educated guess. This is not guaranteed to be correct.
360 | // lastSlash := strings.LastIndex(path, "/")
361 | // if lastSlash == -1 {
362 | // name = path
363 | // } else {
364 | // name = path[lastSlash+1:]
365 | // }
366 | // case "_", ".":
367 | // // Not sure if this import is used - err on the side of caution.
368 | // return true
369 | // }
370 | //
371 | // ast.Walk(visitFn(func(n ast.Node) {
372 | // sel, ok := n.(*ast.SelectorExpr)
373 | // if ok && isTopName(sel.X, name) {
374 | // used = true
375 | // }
376 | // }), f)
377 | //
378 | // return
379 | //}
380 |
381 | //type visitFn func(node ast.Node)
382 | //
383 | //func (fn visitFn) Visit(node ast.Node) ast.Visitor {
384 | // fn(node)
385 | // return fn
386 | //}
387 |
388 | // imports returns true if f imports path.
389 | func imports(f *ast.File, path string) bool {
390 | return importSpec(f, path) != nil
391 | }
392 |
393 | // importSpec returns the import spec if f imports path,
394 | // or nil otherwise.
395 | func importSpec(f *ast.File, path string) *ast.ImportSpec {
396 | for _, s := range f.Imports {
397 | if importPath(s) == path {
398 | return s
399 | }
400 | }
401 | return nil
402 | }
403 |
404 | // importPath returns the unquoted import path of s,
405 | // or "" if the path is not properly quoted.
406 | func importPath(s *ast.ImportSpec) string {
407 | t, err := strconv.Unquote(s.Path.Value)
408 | if err == nil {
409 | return t
410 | }
411 | return ""
412 | }
413 |
414 | // declImports reports whether gen contains an import of path.
415 | func declImports(gen *ast.GenDecl, path string) bool {
416 | if gen.Tok != token.IMPORT {
417 | return false
418 | }
419 | for _, spec := range gen.Specs {
420 | impspec := spec.(*ast.ImportSpec)
421 | if importPath(impspec) == path {
422 | return true
423 | }
424 | }
425 | return false
426 | }
427 |
428 | // matchLen returns the length of the longest path segment prefix shared by x and y.
429 | func matchLen(x, y string) int {
430 | n := 0
431 | for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ {
432 | if x[i] == '/' {
433 | n++
434 | }
435 | }
436 | return n
437 | }
438 |
439 | //// isTopName returns true if n is a top-level unresolved identifier with the given name.
440 | //func isTopName(n ast.Expr, name string) bool {
441 | // id, ok := n.(*ast.Ident)
442 | // return ok && id.Name == name && id.Obj == nil
443 | //}
444 | //
445 | //// Imports returns the file imports grouped by paragraph.
446 | //func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
447 | // var groups [][]*ast.ImportSpec
448 | //
449 | // for _, decl := range f.Decls {
450 | // genDecl, ok := decl.(*ast.GenDecl)
451 | // if !ok || genDecl.Tok != token.IMPORT {
452 | // break
453 | // }
454 | //
455 | // group := []*ast.ImportSpec{}
456 | //
457 | // var lastLine int
458 | // for _, spec := range genDecl.Specs {
459 | // importSpec := spec.(*ast.ImportSpec)
460 | // pos := importSpec.Path.ValuePos
461 | // line := fset.Position(pos).Line
462 | // if lastLine > 0 && pos > 0 && line-lastLine > 1 {
463 | // groups = append(groups, group)
464 | // group = []*ast.ImportSpec{}
465 | // }
466 | // group = append(group, importSpec)
467 | // lastLine = line
468 | // }
469 | // groups = append(groups, group)
470 | // }
471 | //
472 | // return groups
473 | //}
474 |
--------------------------------------------------------------------------------
/util/contrib.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 | )
11 |
12 | const (
13 | fileDescriptorJson string = "descriptor.json"
14 | )
15 |
16 | var contribDescriptors = []string{"descriptor.json", "activity.json", "trigger.json", "action.json"}
17 |
18 | // FlogoAppDescriptor is the descriptor for a Flogo application
19 | type FlogoContribDescriptor struct {
20 | Name string `json:"name"`
21 | Type string `json:"type"`
22 | Version string `json:"version"`
23 | Description string `json:"description"`
24 | Homepage string `json:"homepage"`
25 | Shim string `json:"shim"`
26 | Ref string `json:"ref"` //legacy
27 |
28 | IsLegacy bool `json:"-"`
29 | }
30 |
31 | type FlogoContribBundleDescriptor struct {
32 | Name string `json:"name"`
33 | Description string `json:"description"`
34 | Contribs []string `json:"contributions"`
35 | }
36 |
37 | func (d *FlogoContribDescriptor) GetContribType() string {
38 | parts := strings.Split(d.Type, ":")
39 | if len(parts) > 1 {
40 | return parts[1]
41 | }
42 | return ""
43 | }
44 |
45 |
46 | func GetContribDescriptorFromImport(depManager DepManager, contribImport Import) (*FlogoContribDescriptor, error) {
47 |
48 | contribPath, err := depManager.GetPath(contribImport)
49 | if err != nil {
50 | return nil, err
51 | }
52 |
53 | return GetContribDescriptor(contribPath)
54 | }
55 |
56 | func GetContribDescriptor(contribPath string) (*FlogoContribDescriptor, error) {
57 |
58 | var descriptorPath string
59 | oldDescriptor := false
60 |
61 | for _, descriptorName := range contribDescriptors {
62 | dPath := filepath.Join(contribPath, descriptorName)
63 | if _, err := os.Stat(dPath); err == nil {
64 | if descriptorName != "descriptor.json" {
65 | oldDescriptor = true
66 | }
67 | descriptorPath = dPath
68 | }
69 | }
70 |
71 | if descriptorPath == "" {
72 | //descriptor not found
73 | return nil, nil
74 | }
75 |
76 | desc, err := ReadContribDescriptor(descriptorPath)
77 | if err != nil {
78 | return nil, err
79 | }
80 |
81 | if desc.Ref != "" && oldDescriptor {
82 | desc.IsLegacy = true
83 | }
84 |
85 | return desc, nil
86 | }
87 |
88 | func ReadContribDescriptor(descriptorFile string) (*FlogoContribDescriptor, error) {
89 |
90 | descriptorJson, err := os.Open(descriptorFile)
91 | if err != nil {
92 | return nil, err
93 | }
94 |
95 | bytes, err := ioutil.ReadAll(descriptorJson)
96 | if err != nil {
97 | return nil, err
98 | }
99 |
100 | descriptor := &FlogoContribDescriptor{}
101 |
102 | err = json.Unmarshal(bytes, descriptor)
103 | if err != nil {
104 | return nil, fmt.Errorf("failed to parse descriptor '%s': %s", descriptorFile, err.Error())
105 | }
106 |
107 | return descriptor, nil
108 | }
109 |
110 | func GetContribType(depManager DepManager, ref string) (string, error) {
111 |
112 | refAsFlogoImport, err := NewFlogoImportFromPath(ref)
113 | if err != nil {
114 | return "", err
115 | }
116 |
117 | impPath, err := depManager.GetPath(refAsFlogoImport)//(refAsFlogoImport)
118 | if err != nil {
119 | return "", err
120 | }
121 | var descriptorPath string
122 |
123 | if _, err := os.Stat(filepath.Join(impPath, fileDescriptorJson)); err == nil {
124 | descriptorPath = filepath.Join(impPath, fileDescriptorJson)
125 |
126 | } else if _, err := os.Stat(filepath.Join(impPath, "activity.json")); err == nil {
127 | descriptorPath = filepath.Join(impPath, "activity.json")
128 | } else if _, err := os.Stat(filepath.Join(impPath, "trigger.json")); err == nil {
129 | descriptorPath = filepath.Join(impPath, "trigger.json")
130 | } else if _, err := os.Stat(filepath.Join(impPath, "action.json")); err == nil {
131 | descriptorPath = filepath.Join(impPath, "action.json")
132 | }
133 |
134 | if _, err := os.Stat(descriptorPath); descriptorPath != "" && err == nil {
135 |
136 | desc, err := ReadContribDescriptor(descriptorPath)
137 | if err != nil {
138 | return "", err
139 | }
140 |
141 | return desc.Type, nil
142 | }
143 |
144 | return "", nil
145 | }
146 |
--------------------------------------------------------------------------------
/util/engine.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "strings"
9 | )
10 |
11 | // PartialEngineDescriptor is the descriptor for a Flogo application
12 | type PartialEngineDescriptor struct {
13 | Imports []string `json:"imports"`
14 | Services []*EngineServiceDetails `json:"services"`
15 | }
16 |
17 | type EngineServiceDetails struct {
18 | Ref string
19 | }
20 |
21 | type EngineImportDetails struct {
22 | Imp Import
23 | TopLevel bool // a toplevel import i.e. from imports section
24 | ServiceRef bool // imports is used by a service
25 |
26 | HasAliasRef bool // imports alias is used by a contrib reference
27 | HasDirectRef bool // a direct reference exists for this import
28 | }
29 |
30 | type EngineImports struct {
31 | imports map[string]*EngineImportDetails
32 | orphanedRef map[string]void
33 | depManager DepManager
34 | }
35 |
36 | func (ai *EngineImports) addImports(imports []string) error {
37 | for _, anImport := range imports {
38 | flogoImport, err := ParseImport(anImport)
39 | if err != nil {
40 | return err
41 | }
42 |
43 | if _, exists := ai.imports[flogoImport.GoImportPath()]; exists {
44 | //todo warn about duplicate import?
45 | continue
46 | }
47 |
48 | details := &EngineImportDetails{Imp: flogoImport, TopLevel:true}
49 | ai.imports[flogoImport.GoImportPath()] = details
50 | }
51 |
52 | return nil
53 | }
54 |
55 | func (ai *EngineImports) addReference(ref string, isService bool) error {
56 | cleanedRef := strings.TrimSpace(ref)
57 |
58 | if cleanedRef[0] == '#' {
59 |
60 | alias := cleanedRef[1:]
61 | found := false
62 | for _, importDetails := range ai.imports {
63 |
64 | if importDetails.TopLevel {
65 | // alias refs can only be to toplevel imports
66 | if importDetails.Imp.CanonicalAlias() == alias {
67 | importDetails.HasAliasRef = true
68 | importDetails.ServiceRef = isService
69 | found = true
70 | break
71 | }
72 | }
73 | }
74 |
75 | if !found {
76 | ai.orphanedRef[cleanedRef] = void{}
77 | }
78 |
79 | } else {
80 | flogoImport, err := ParseImport(ref)
81 | if err != nil {
82 | return err
83 | }
84 |
85 | if imp, exists := ai.imports[flogoImport.GoImportPath()]; exists {
86 | if !imp.TopLevel {
87 | //already accounted for
88 | return nil
89 | }
90 |
91 | imp.HasDirectRef = true
92 | imp.ServiceRef = true
93 | return nil
94 | }
95 |
96 | //doesn't exists so add new import
97 | details := &EngineImportDetails{Imp: flogoImport, ServiceRef:isService}
98 | ai.imports[flogoImport.GoImportPath()] = details
99 | }
100 |
101 | return nil
102 | }
103 |
104 | func (ai *EngineImports) GetOrphanedReferences() []string {
105 | var refs []string
106 | for ref := range ai.orphanedRef {
107 | refs = append(refs, ref)
108 | }
109 |
110 | return refs
111 | }
112 |
113 | func (ai *EngineImports) GetAllImports() []Import {
114 | var allImports []Import
115 | for _, details := range ai.imports {
116 | allImports = append(allImports, details.Imp)
117 | }
118 |
119 | return allImports
120 | }
121 |
122 | func (ai *EngineImports) GetAllImportDetails() []*EngineImportDetails {
123 |
124 | var allImports []*EngineImportDetails
125 | for _, details := range ai.imports {
126 | allImports = append(allImports, details)
127 | }
128 |
129 | return allImports
130 | }
131 |
132 | func GetEngineImports(engJsonFile string, depManager DepManager) (*EngineImports, error) {
133 | engJson, err := os.Open(engJsonFile)
134 | if err != nil {
135 | return nil, err
136 | }
137 |
138 | bytes, err := ioutil.ReadAll(engJson)
139 | if err != nil {
140 | return nil, err
141 | }
142 |
143 | engDesc := &PartialEngineDescriptor{}
144 | err = json.Unmarshal(bytes, engDesc)
145 | if err != nil {
146 | fmt.Fprintf(os.Stderr, "Unable to unmarshal flogo engine json: %s", engJsonFile)
147 | return nil, err
148 | }
149 |
150 | imports := make(map[string]void)
151 |
152 | if len(engDesc.Imports) > 0 {
153 | for _, imp := range engDesc.Imports {
154 | imports[imp] = void{}
155 | }
156 | }
157 |
158 | if len(engDesc.Services) > 0 {
159 | for _, service := range engDesc.Services {
160 | imports[service.Ref] = void{}
161 | }
162 | }
163 |
164 |
165 | ai := &EngineImports{depManager: depManager}
166 | ai.imports = make(map[string]*EngineImportDetails)
167 | ai.orphanedRef = make(map[string]void)
168 |
169 | err = ai.addImports(engDesc.Imports)
170 | if err != nil {
171 | return nil, err
172 | }
173 |
174 | // add service refs/imports
175 | for _, serviceDetails := range engDesc.Services {
176 | err := ai.addReference(serviceDetails.Ref, true)
177 | if err != nil {
178 | return nil, err
179 | }
180 | }
181 |
182 | return ai, err
183 | }
184 |
--------------------------------------------------------------------------------
/util/env.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "strings"
4 |
5 | func ReplaceEnvValue(env []string, envKey string, newValue string) []string {
6 | for key, entry := range env {
7 |
8 | if strings.HasPrefix(entry, envKey+"=") {
9 | env[key] = envKey + "=" + newValue
10 | break
11 | }
12 | }
13 |
14 | return env
15 | }
16 |
--------------------------------------------------------------------------------
/util/file.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "io/ioutil"
7 | "net/http"
8 | "os"
9 | "path"
10 | "path/filepath"
11 | "strings"
12 | )
13 |
14 | func IsRemote(path string) bool {
15 | return strings.HasPrefix(path, "http")
16 | }
17 |
18 | func LoadRemoteFile(sourceURL string) (string, error) {
19 |
20 | resp, err := http.Get(sourceURL)
21 | if err != nil {
22 | return "", err
23 | }
24 |
25 | defer resp.Body.Close()
26 |
27 | buf, err := ioutil.ReadAll(resp.Body)
28 | if err != nil {
29 | return "", err
30 | }
31 |
32 | return string(buf), nil
33 | }
34 |
35 | func LoadLocalFile(path string) (string, error) {
36 |
37 | buf, err := ioutil.ReadFile(path)
38 | if err != nil {
39 | return "", err
40 | }
41 |
42 | return string(buf), nil
43 | }
44 |
45 | func Rename(srcPath string) error {
46 | oldPath := fmt.Sprintf("%s.old", srcPath)
47 | _ = os.Remove(oldPath)
48 |
49 | err := os.Rename(srcPath, oldPath)
50 | if err != nil {
51 | return err
52 | }
53 |
54 | return nil
55 | }
56 |
57 | func CopyFile(srcFile, destFile string) error {
58 | input, err := ioutil.ReadFile(srcFile)
59 | if err != nil {
60 | return err
61 | }
62 |
63 | err = ioutil.WriteFile(destFile, input, 0644)
64 | if err != nil {
65 | return err
66 | }
67 |
68 | return nil
69 | }
70 |
71 | func Copy(src, dst string, copyMode bool) error {
72 | info, err := os.Lstat(src)
73 | if err != nil {
74 | return err
75 | }
76 |
77 | if info.IsDir() {
78 | return copyDir(src, dst, info, copyMode)
79 | }
80 |
81 | return copyFile(src, dst, info, copyMode)
82 | }
83 |
84 | func copyDir(srcDir, dstDir string, info os.FileInfo, copyMode bool) error {
85 |
86 | if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
87 | return err
88 | }
89 |
90 | if copyMode {
91 | defer os.Chmod(dstDir, info.Mode())
92 | }
93 |
94 | items, err := ioutil.ReadDir(srcDir)
95 | if err != nil {
96 | return err
97 | }
98 |
99 | for _, item := range items {
100 |
101 | srcPath := path.Join(srcDir, item.Name())
102 | dstPath := path.Join(dstDir, item.Name())
103 |
104 | if item.IsDir() {
105 | if err = copyDir(srcPath, dstPath, item, copyMode); err != nil {
106 | return err
107 | }
108 | } else {
109 | if err = copyFile(srcPath, dstPath, item, copyMode); err != nil {
110 | return err
111 | }
112 | }
113 | }
114 |
115 | return nil
116 | }
117 |
118 | func copyFile(src, dst string, srcInfo os.FileInfo, copyMode bool) error {
119 | var err error
120 |
121 | if err = os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
122 | return err
123 | }
124 |
125 | var sf *os.File
126 | if sf, err = os.Open(src); err != nil {
127 | return err
128 | }
129 | defer sf.Close()
130 |
131 | var df *os.File
132 | if df, err = os.Create(dst); err != nil {
133 | return err
134 | }
135 | defer df.Close()
136 |
137 | if err = os.Chmod(sf.Name(), srcInfo.Mode()); err != nil {
138 | return err
139 | }
140 |
141 | if _, err = io.Copy(df, sf); err != nil {
142 | return err
143 | }
144 |
145 | return nil
146 | }
147 |
148 | func FileExists(path string) bool {
149 | info, err := os.Stat(path)
150 | if os.IsNotExist(err) {
151 | return false
152 | }
153 | return !info.IsDir()
154 | }
155 |
156 | func DirExists(path string) bool {
157 | info, err := os.Stat(path)
158 | if os.IsNotExist(err) {
159 | return false
160 | }
161 | return info.IsDir()
162 | }
163 |
164 | func DeleteFile(path string) error {
165 |
166 | if _, err := os.Stat(path); err == nil {
167 | err = os.Remove(path)
168 | if err != nil {
169 | return err
170 | }
171 | }
172 |
173 | return nil
174 | }
--------------------------------------------------------------------------------
/util/flogo.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | // ParseAppDescriptor parse the application descriptor
8 | func ParseAppDescriptor(appJson string) (*FlogoAppDescriptor, error) {
9 | descriptor := &FlogoAppDescriptor{}
10 |
11 | err := json.Unmarshal([]byte(appJson), descriptor)
12 |
13 | if err != nil {
14 | return nil, err
15 | }
16 |
17 | return descriptor, nil
18 | }
19 |
20 | // FlogoAppDescriptor is the descriptor for a Flogo application
21 | type FlogoAppDescriptor struct {
22 | Name string `json:"name"`
23 | Type string `json:"type"`
24 | Version string `json:"version"`
25 | Description string `json:"description"`
26 | AppModel string `json:"appModel,omitempty"`
27 | Imports []string `json:"imports"`
28 |
29 | Triggers []*FlogoTriggerConfig `json:"triggers"`
30 | }
31 |
32 | type FlogoTriggerConfig struct {
33 | Id string `json:"id"`
34 | Ref string `json:"ref"`
35 | Type string `json:"type"`
36 | }
37 |
--------------------------------------------------------------------------------
/util/flogo_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "testing"
7 | )
8 |
9 | func TestName(t *testing.T) {
10 | line := ` "ref":"github.com/TIBCOSoftware/flogo-contrib/activity/log",`
11 |
12 | if idx := strings.Index(line, "\"ref\""); idx > -1 {
13 |
14 | fmt.Printf("line: '%s'\n", line)
15 |
16 | startPkgIdx := strings.Index(line[idx+6:], "\"")
17 |
18 | fmt.Printf("line frag: '%s'\n", line[idx+6+startPkgIdx:])
19 | pkg := strings.Split(line[idx+6+startPkgIdx:], "\"")[1]
20 | //pkg := strings.Split(",")[0]
21 | //pkg := strings.Split(line, ":")[1]
22 | //pkg = strings.TrimSpace(pkg)
23 | //pkg = pkg[1 : len(pkg)-2]
24 | fmt.Printf("package: '%s'\n", pkg)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/util/gosrchelper.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "os/exec"
8 | "path/filepath"
9 | "strings"
10 |
11 | "github.com/coreos/go-semver/semver"
12 | )
13 |
14 | var goPathCached string
15 |
16 | func FindOldPackageSrc(pkg string) (srcPath, srcVer string, err error) {
17 |
18 | goPath := GetGoPath()
19 |
20 | pkgParts := strings.Split(pkg, "/")
21 | path := filepath.Join(goPath, "src", filepath.Join(pkgParts...))
22 |
23 | if _, e := os.Stat(path); !os.IsNotExist(e) {
24 | // path/to/whatever exists
25 | v := GetPackageVersionFromSource(pkg)
26 |
27 | return path, v, nil
28 | }
29 |
30 | return "", "", newPkgNotFoundError(pkg)
31 | }
32 |
33 | func FindGoModPackageSrc(pkg string, version string, latest bool) (srcPath, srcVer string, err error) {
34 |
35 | pkgParts := strings.Split(pkg, "/")
36 | if len(pkgParts) < 2 {
37 | return "", "", fmt.Errorf("invalid package: %s", pkg)
38 | }
39 |
40 | name := pkgParts[len(pkgParts)-1]
41 | path := pkgParts[:len(pkgParts)-1]
42 |
43 | goPath := GetGoPath()
44 | flogoPkgPath := filepath.Join(goPath, "pkg", "mod", filepath.Join(path...))
45 |
46 | if _, e := os.Stat(flogoPkgPath); os.IsNotExist(e) {
47 | return "", "", newPkgNotFoundError(pkg)
48 | }
49 |
50 | var files []string
51 |
52 | err = filepath.Walk(flogoPkgPath, visit(name, &files))
53 | if err != nil {
54 | return "", "", newPkgNotFoundError(pkg)
55 | }
56 |
57 | if latest {
58 |
59 | lf := ""
60 | var lv *semver.Version
61 |
62 | for _, file := range files {
63 |
64 | parts := strings.SplitN(file, "@v", 2)
65 |
66 | if lf == "" {
67 | lf = file
68 | lv, err = semver.NewVersion(parts[1])
69 | if err != nil {
70 | return "", "", err
71 | }
72 | continue
73 | }
74 |
75 | sv, err := semver.NewVersion(parts[1])
76 | if err != nil {
77 | return "", "", err
78 | }
79 |
80 | if lv.LessThan(*sv) {
81 | lf = file
82 | lv = sv
83 | }
84 | }
85 |
86 | if lf == "" {
87 | return "", "", newPkgNotFoundError(pkg)
88 | }
89 |
90 | return lf, lv.String(), nil
91 | } else {
92 |
93 | for _, file := range files {
94 |
95 | parts := strings.SplitN(file, "@v", 2)
96 | if parts[1] == version {
97 | return file, version, nil
98 | }
99 | }
100 | }
101 |
102 | return "", "", newPkgNotFoundError(pkg)
103 | }
104 |
105 | func visit(name string, files *[]string) filepath.WalkFunc {
106 | return func(path string, info os.FileInfo, err error) error {
107 | if err != nil {
108 | log.Fatal(err)
109 | }
110 | if !info.IsDir() {
111 | return nil
112 | }
113 |
114 | if strings.HasPrefix(info.Name(), name+"@v") {
115 | *files = append(*files, path)
116 | }
117 |
118 | return nil
119 | }
120 | }
121 |
122 | func GetGoPath() string {
123 |
124 | if goPathCached != "" {
125 | return goPathCached
126 | }
127 |
128 | set := false
129 | goPathCached, set = os.LookupEnv("GOPATH")
130 | if !set {
131 | out, err := exec.Command("go", "env", "GOPATH").Output()
132 | if err != nil {
133 | log.Fatal(err)
134 | }
135 | goPathCached = strings.TrimSuffix(string(out), "\n")
136 | }
137 |
138 | return goPathCached
139 | }
140 |
141 | func newPkgNotFoundError(pkg string) error {
142 | return &pkgNotFoundError{pkg: pkg}
143 | }
144 |
145 | type pkgNotFoundError struct {
146 | pkg string
147 | }
148 |
149 | func (e *pkgNotFoundError) Error() string {
150 | return fmt.Sprintf("Package '%s' not found", e.pkg)
151 | }
152 |
153 | func IsPkgNotFoundError(err error) bool {
154 | _, ok := err.(*pkgNotFoundError)
155 | return ok
156 | }
--------------------------------------------------------------------------------
/util/gosrchelper_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "github.com/stretchr/testify/assert"
6 | "testing"
7 | )
8 |
9 | func TestFindGoModPackageSrc(t *testing.T) {
10 | str, ver, err := FindGoModPackageSrc("github.com/project-flogo/core", "", true)
11 | if err != nil {
12 | fmt.Println("err:", err)
13 | t.FailNow()
14 | }
15 |
16 | fmt.Println("path: ", str)
17 | fmt.Println("ver: ", ver)
18 | }
19 |
20 |
21 | func TestFindOldPackageSrc(t *testing.T) {
22 | str, ver, err := FindOldPackageSrc("github.com/project-flogo/cli")
23 | if err != nil {
24 | fmt.Println("err:", err)
25 | t.FailNow()
26 | }
27 |
28 | fmt.Println("path: ", str)
29 | fmt.Println("ver: ", ver)
30 | }
31 |
32 |
33 | func TestFindGoModPackageSrcNotFound(t *testing.T) {
34 | _, _, err := FindGoModPackageSrc("github.com/project-blah/core", "", true)
35 | assert.True(t, IsPkgNotFoundError(err))
36 | fmt.Println("err: ", err)
37 | }
38 |
39 |
40 | func TestFindOldPackageSrcNotFound(t *testing.T) {
41 | _, _, err := FindOldPackageSrc("github.com/project-blah/core")
42 | assert.True(t, IsPkgNotFoundError(err))
43 | fmt.Println("err: ", err)
44 | }
45 |
--------------------------------------------------------------------------------
/util/imports.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "path"
7 | "regexp"
8 | )
9 |
10 | /* util.Import struct defines the different fields which can be extracted from a Flogo import
11 | these imports are stored in flogo.json in the "imports" array, for instance:
12 |
13 | "imports": [
14 | "github.com/project-flogo/contrib@v0.9.0-alpha.4:/activity/log",
15 | "github.com/project-flogo/contrib/activity/rest@v0.9.0"
16 | "rest_activity github.com/project-flogo/contrib@v0.9.0:/activity/rest",
17 | "rest_trigger github.com/project-flogo/contrib:/trigger/rest",
18 | "github.com/project-flogo/flow"
19 | ]
20 |
21 | */
22 |
23 | type FlogoImport struct {
24 | modulePath string
25 | relativeImportPath string
26 | version string
27 | alias string
28 | }
29 |
30 | func NewFlogoImportFromPath(flogoImportPath string) (Import, error) {
31 | flogoImport, err := ParseImport(flogoImportPath)
32 | if err != nil {
33 | return nil, err
34 | }
35 | return flogoImport, nil
36 | }
37 |
38 | func NewFlogoImport(modulePath, relativeImportPath, version, alias string) Import {
39 | return &FlogoImport{modulePath: modulePath, relativeImportPath: relativeImportPath, version: version, alias: alias}
40 | }
41 |
42 | func NewFlogoImportWithVersion(imp Import, version string) Import {
43 | return &FlogoImport{modulePath: imp.ModulePath(), relativeImportPath: imp.RelativeImportPath(), version: version, alias: imp.Alias()}
44 | }
45 |
46 | type Import interface {
47 | fmt.Stringer
48 |
49 | ModulePath() string
50 | RelativeImportPath() string
51 | Version() string
52 | Alias() string
53 |
54 | CanonicalImport() string // canonical import is used in flogo.json imports array and to check for equality of imports
55 | GoImportPath() string // the import path used in .go files
56 | GoGetImportPath() string // the import path used by "go get" command
57 | GoModImportPath() string // the import path used by "go mod edit" command
58 | IsClassic() bool // an import is "classic" if it has no : character separator, hence no relative import path
59 | CanonicalAlias() string // canonical alias is the alias used in the flogo.json
60 | }
61 |
62 | type Imports []Import
63 |
64 | func (flogoImport *FlogoImport) ModulePath() string {
65 | return flogoImport.modulePath
66 | }
67 |
68 | func (flogoImport *FlogoImport) RelativeImportPath() string {
69 | return flogoImport.relativeImportPath
70 | }
71 |
72 | func (flogoImport *FlogoImport) Version() string {
73 | return flogoImport.version
74 | }
75 |
76 | func (flogoImport *FlogoImport) Alias() string {
77 | return flogoImport.alias
78 | }
79 |
80 | func (flogoImport *FlogoImport) CanonicalImport() string {
81 | alias := ""
82 | if flogoImport.alias != "" {
83 | alias = flogoImport.alias + " "
84 | }
85 | version := ""
86 | if flogoImport.version != "" {
87 | version = "@" + flogoImport.version
88 | }
89 | relativeImportPath := ""
90 | if flogoImport.relativeImportPath != "" {
91 | relativeImportPath = ":" + flogoImport.relativeImportPath
92 | }
93 |
94 | return alias + flogoImport.modulePath + version + relativeImportPath
95 | }
96 |
97 | func (flogoImport *FlogoImport) CanonicalAlias() string {
98 | if flogoImport.alias != "" {
99 | return flogoImport.alias
100 | } else {
101 | return path.Base(flogoImport.GoImportPath())
102 | }
103 | }
104 |
105 | func (flogoImport *FlogoImport) GoImportPath() string {
106 | return flogoImport.modulePath + flogoImport.relativeImportPath
107 | }
108 |
109 | func (flogoImport *FlogoImport) GoGetImportPath() string {
110 | version := "@latest"
111 | if flogoImport.version != "" {
112 | version = "@" + flogoImport.version
113 | }
114 | return flogoImport.modulePath + flogoImport.relativeImportPath + version
115 | }
116 |
117 | func (flogoImport *FlogoImport) GoModImportPath() string {
118 | version := "@latest"
119 | if flogoImport.version != "" {
120 | version = "@" + flogoImport.version
121 | }
122 | return flogoImport.modulePath + version
123 | }
124 |
125 | func (flogoImport *FlogoImport) IsClassic() bool {
126 | return flogoImport.relativeImportPath == ""
127 | }
128 |
129 | func (flogoImport *FlogoImport) String() string {
130 | version := ""
131 | if flogoImport.version != "" {
132 | version = " " + flogoImport.version
133 | }
134 | relativeImportPath := ""
135 | if flogoImport.relativeImportPath != "" {
136 | relativeImportPath = flogoImport.relativeImportPath
137 | }
138 |
139 | return flogoImport.modulePath + relativeImportPath + version
140 | }
141 |
142 | var flogoImportPattern = regexp.MustCompile(`^(([^ ]*)[ ]+)?([^@:]*)@?([^:]*)?:?(.*)?$`) // extract import path even if there is an alias and/or a version
143 |
144 | func ParseImports(flogoImports []string) (Imports, error) {
145 | var result Imports
146 |
147 | for _, flogoImportPath := range flogoImports {
148 | flogoImport, err := ParseImport(flogoImportPath)
149 | if err != nil {
150 | return nil, err
151 | }
152 | result = append(result, flogoImport)
153 | }
154 |
155 | return result, nil
156 | }
157 |
158 | func ParseImport(flogoImport string) (Import, error) {
159 | if !flogoImportPattern.MatchString(flogoImport) {
160 | return nil, errors.New(fmt.Sprintf("The Flogo import '%s' cannot be parsed.", flogoImport))
161 | }
162 |
163 | matches := flogoImportPattern.FindStringSubmatch(flogoImport)
164 |
165 | result := &FlogoImport{modulePath: matches[3], relativeImportPath: matches[5], version: matches[4], alias: matches[2]}
166 |
167 | return result, nil
168 | }
169 |
--------------------------------------------------------------------------------
/util/mod.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "encoding/json"
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | "net/http"
11 | "os"
12 | "os/exec"
13 | "path/filepath"
14 | "regexp"
15 | "strings"
16 |
17 | "github.com/msoap/byline"
18 | )
19 |
20 | type DepManager interface {
21 | Init() error
22 | AddDependency(flogoImport Import) error
23 | GetPath(flogoImport Import) (string, error)
24 | AddReplacedContribForBuild() error
25 | InstallReplacedPkg(string, string) error
26 | GetAllImports() (map[string]Import, error)
27 | }
28 |
29 | func NewDepManager(sourceDir string) DepManager {
30 | return &ModDepManager{srcDir: sourceDir, localMods: make(map[string]string)}
31 | }
32 |
33 | type ModDepManager struct {
34 | srcDir string
35 | localMods map[string]string
36 | }
37 |
38 | func (m *ModDepManager) Init() error {
39 |
40 | err := ExecCmd(exec.Command("go", "mod", "init", "main"), m.srcDir)
41 | if err == nil {
42 | return err
43 | }
44 |
45 | return nil
46 | }
47 |
48 | func (m *ModDepManager) AddDependency(flogoImport Import) error {
49 |
50 | // todo: optimize the following
51 |
52 | // use "go mod edit" (instead of "go get") as first method
53 | err := ExecCmd(exec.Command("go", "mod", "edit", "-require", flogoImport.GoModImportPath()), m.srcDir)
54 | if err != nil {
55 | return err
56 | }
57 |
58 |
59 | err = ExecCmd(exec.Command("go", "mod", "verify"), m.srcDir)
60 | if err == nil {
61 | err = ExecCmd(exec.Command("go", "mod", "download", flogoImport.ModulePath()), m.srcDir)
62 | }
63 |
64 | if err != nil {
65 | // if the resolution fails and the Flogo import is "classic"
66 | // (meaning it does not separate module path from Go import path):
67 | // 1. remove the import manually ("go mod edit -droprequire") would fail
68 | // 2. try with "go get" instead
69 | if flogoImport.IsClassic() {
70 | m.RemoveImport(flogoImport)
71 |
72 | err = ExecCmd(exec.Command("go", "get", flogoImport.GoGetImportPath()), m.srcDir)
73 | }
74 | }
75 |
76 | if err != nil {
77 | return err
78 | }
79 |
80 | return nil
81 | }
82 |
83 | // GetPath gets the path of where the
84 | func (m *ModDepManager) GetPath(flogoImport Import) (string, error) {
85 |
86 | currentDir, err := os.Getwd()
87 | if err != nil {
88 | return "", err
89 | }
90 |
91 | pkg := flogoImport.ModulePath()
92 |
93 | path, ok := m.localMods[pkg]
94 | if ok && path != "" {
95 |
96 | return path, nil
97 | }
98 | defer os.Chdir(currentDir)
99 |
100 | os.Chdir(m.srcDir)
101 |
102 | file, err := os.Open(filepath.Join(m.srcDir, "go.mod"))
103 | defer file.Close()
104 |
105 | var pathForPartial string
106 |
107 | scanner := bufio.NewScanner(file)
108 | for scanner.Scan() {
109 |
110 | line := scanner.Text()
111 | reqComponents := strings.Fields(line)
112 | //It is the line in the go.mod which is not useful, so ignore.
113 | if len(reqComponents) < 2 || (reqComponents[0] == "require" && reqComponents[1] == "(") {
114 | continue
115 | }
116 |
117 | //typically package is 1st component and version is the 2nd component
118 | reqPkg := reqComponents[0]
119 | version := reqComponents[1]
120 | if reqComponents[0] == "require" {
121 | //starts with require, so package is 2nd component and version is the 3rd component
122 | reqPkg = reqComponents[1]
123 | version = reqComponents[2]
124 | }
125 |
126 | if strings.HasPrefix(pkg, reqPkg) {
127 |
128 | hasFull := strings.Contains(line, pkg)
129 | tempPath := strings.Split(reqPkg, "/")
130 |
131 | tempPath = toLower(tempPath)
132 | lastIdx := len(tempPath) - 1
133 |
134 | tempPath[lastIdx] = tempPath[lastIdx] + "@" + version
135 |
136 | pkgPath := filepath.Join(tempPath...)
137 |
138 | if !hasFull {
139 | remaining := pkg[len(reqPkg):]
140 | tempPath = strings.Split(remaining, "/")
141 | remainingPath := filepath.Join(tempPath...)
142 |
143 | pathForPartial = filepath.Join(os.Getenv("GOPATH"), "pkg", "mod", pkgPath, remainingPath)
144 | } else {
145 | return filepath.Join(os.Getenv("GOPATH"), "pkg", "mod", pkgPath, flogoImport.RelativeImportPath()), nil
146 | }
147 | }
148 | }
149 | return pathForPartial, nil
150 | }
151 |
152 | func (m *ModDepManager) RemoveImport(flogoImport Import) error {
153 |
154 | currentDir, err := os.Getwd()
155 | if err != nil {
156 | return err
157 | }
158 |
159 | modulePath := flogoImport.ModulePath()
160 |
161 | defer os.Chdir(currentDir)
162 |
163 | os.Chdir(m.srcDir)
164 |
165 | file, err := os.Open(filepath.Join(m.srcDir, "go.mod"))
166 | if err != nil {
167 | return err
168 | }
169 | defer file.Close()
170 |
171 | modulePath = strings.Replace(modulePath, "/", "\\/", -1)
172 | modulePath = strings.Replace(modulePath, ".", "\\.", -1)
173 | importRegex := regexp.MustCompile(`\s*` + modulePath + `\s+` + flogoImport.Version() + `.*`)
174 |
175 | lr := byline.NewReader(file)
176 |
177 | lr.MapString(func(line string) string {
178 | if importRegex.MatchString(line) {
179 | return ""
180 | } else {
181 | return line
182 | }
183 | })
184 |
185 | updatedGoMod, err := lr.ReadAll()
186 | if err != nil {
187 | return err
188 | }
189 |
190 | file, err = os.Create(filepath.Join(m.srcDir, "go.mod"))
191 | if err != nil {
192 | return err
193 | }
194 | defer file.Close()
195 |
196 | file.Write(updatedGoMod)
197 |
198 | return nil
199 | }
200 | func (m *ModDepManager) GetAllImports() (map[string]Import, error) {
201 | file, err := ioutil.ReadFile(filepath.Join(m.srcDir, "go.mod"))
202 | if err != nil {
203 | return nil, err
204 | }
205 |
206 | content := string(file)
207 |
208 | imports := strings.Split(content[strings.Index(content, "(")+1:strings.Index(content, ")")], "\n")
209 | result := make(map[string]Import)
210 |
211 | for _, pkg := range imports {
212 | if pkg != " " && pkg != "" {
213 |
214 | mods := strings.Split(strings.TrimSpace(pkg), " ")
215 |
216 | modImport, err := ParseImport(strings.Join(mods[:2], "@"))
217 | if err != nil {
218 | return nil, err
219 | }
220 |
221 | result[modImport.GoImportPath()] = modImport
222 | }
223 | }
224 |
225 | return result, nil
226 | }
227 |
228 | //This function converts capotal letters in package name
229 | // to !(smallercase). Eg C => !c . As this is the way
230 | // go.mod saves every repository in the $GOPATH/pkg/mod.
231 | func toLower(s []string) []string {
232 | result := make([]string, len(s))
233 | for i := 0; i < len(s); i++ {
234 | var b bytes.Buffer
235 | for _, c := range s[i] {
236 | if c >= 65 && c <= 90 {
237 | b.WriteRune(33)
238 | b.WriteRune(c + 32)
239 | } else {
240 | b.WriteRune(c)
241 | }
242 | }
243 | result[i] = b.String()
244 | }
245 | return result
246 | }
247 |
248 | var verbose = false
249 |
250 | func SetVerbose(enable bool) {
251 | verbose = enable
252 | }
253 |
254 | func Verbose() bool {
255 | return verbose
256 | }
257 |
258 | func ExecCmd(cmd *exec.Cmd, workingDir string) error {
259 |
260 | if workingDir != "" {
261 | cmd.Dir = workingDir
262 | }
263 |
264 | var out bytes.Buffer
265 |
266 | if verbose {
267 | cmd.Stdout = os.Stdout
268 | cmd.Stderr = os.Stderr
269 | } else {
270 | cmd.Stdout = nil
271 | cmd.Stderr = &out
272 | }
273 |
274 | err := cmd.Run()
275 |
276 | if err != nil {
277 | return fmt.Errorf(string(out.Bytes()))
278 | }
279 |
280 | return nil
281 | }
282 |
283 | func (m *ModDepManager) AddReplacedContribForBuild() error {
284 |
285 | err := ExecCmd(exec.Command("go", "mod", "download"), m.srcDir)
286 | if err != nil {
287 | return err
288 | }
289 |
290 | text, err := ioutil.ReadFile(filepath.Join(m.srcDir, "go.mod"))
291 | if err != nil {
292 | return err
293 | }
294 |
295 | data := string(text)
296 |
297 | index := strings.Index(data, "replace")
298 | if index != -1 {
299 | localModules := strings.Split(data[index-1:], "\n")
300 |
301 | for _, val := range localModules {
302 | if val != "" {
303 | mods := strings.Split(val, " ")
304 | //If the length of mods is more than 4 it contains the versions of package
305 | //so it is stating to use different version of pkg rather than
306 | // the local pkg.
307 | if len(mods) < 5 {
308 |
309 | m.localMods[mods[1]] = mods[3]
310 | } else {
311 |
312 | m.localMods[mods[1]] = filepath.Join(os.Getenv("GOPATH"), "pkg", "mod", mods[3]+"@"+mods[4])
313 | }
314 |
315 | }
316 |
317 | }
318 | return nil
319 | }
320 | return nil
321 | }
322 |
323 | func (m *ModDepManager) InstallReplacedPkg(pkg1 string, pkg2 string) error {
324 |
325 | m.localMods[pkg1] = pkg2
326 |
327 | f, err := os.OpenFile(filepath.Join(m.srcDir, "go.mod"), os.O_APPEND|os.O_WRONLY, 0777)
328 | if err != nil {
329 | return err
330 | }
331 | defer f.Close()
332 |
333 | if _, err = f.WriteString(fmt.Sprintf("replace %v => %v", pkg1, pkg2)); err != nil {
334 | return err
335 | }
336 |
337 | err = ExecCmd(exec.Command("go", "mod", "download"), m.srcDir)
338 | if err != nil {
339 | return err
340 | }
341 | return nil
342 | }
343 |
344 | type Resp struct {
345 | Name string `json:"name"`
346 | }
347 |
348 | func getLatestVersion(path string) string {
349 |
350 | //To get the latest version number use the GitHub API.
351 | resp, err := http.Get("https://api.github.com/repos/TIBCOSoftware/flogo-contrib/releases/latest")
352 | if err != nil {
353 | log.Fatalln(err)
354 | }
355 |
356 | body, err := ioutil.ReadAll(resp.Body)
357 | if err != nil {
358 | log.Fatalln(err)
359 | }
360 |
361 | var result Resp
362 |
363 | err = json.Unmarshal(body, &result)
364 | if err != nil {
365 | return ""
366 | }
367 |
368 | return result.Name
369 |
370 | }
371 |
--------------------------------------------------------------------------------
/util/version.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "log"
5 | "os"
6 | "os/exec"
7 | "path/filepath"
8 | "regexp"
9 | "strings"
10 | "text/template"
11 | "time"
12 | )
13 |
14 | const (
15 | cliPackage = "github.com/project-flogo/cli"
16 | )
17 |
18 | func GetCLIInfo() (string, string, error) {
19 |
20 | path, ver, err := FindOldPackageSrc(cliPackage)
21 |
22 | if IsPkgNotFoundError(err) {
23 | //must be using the new go mod layout
24 | path, ver, err = FindGoModPackageSrc(cliPackage, "", true)
25 | if err != nil {
26 | workingDir, _ := os.Getwd()
27 | ver = GetPackageVersionFromGit(workingDir)
28 | }
29 | }
30 |
31 | return path, ver, err
32 | }
33 |
34 | // retrieve the package version from source in GOPATH using "git describe" command
35 | func GetPackageVersionFromSource(pkg string) string {
36 | gopath := GetGoPath()
37 |
38 | pkgParts := strings.Split(pkg, "/")
39 |
40 | return GetPackageVersionFromGit(filepath.Join(gopath, "src", filepath.Join(pkgParts...)))
41 | }
42 |
43 | // retrieve the package version from source in a directory using "git describe" command
44 | func GetPackageVersionFromGit(dir string) string {
45 | re := regexp.MustCompile("\\n")
46 |
47 | cmd := exec.Command("git", "describe", "--tags", "--dirty", "--always")
48 | cmd.Env = append(os.Environ())
49 |
50 | cmd.Dir = dir
51 |
52 | out, err := cmd.Output() // execute "git describe"
53 | if err != nil {
54 | log.Fatal(err)
55 | }
56 | fc := re.ReplaceAllString(string(out), "")
57 |
58 | if len(fc) > 1 {
59 | return fc[1:]
60 | }
61 |
62 | return fc
63 | }
64 |
65 | func CreateVersionFile(cmdPath, currentVersion string) error {
66 |
67 | f, err := os.Create(filepath.Join(cmdPath, "currentversion.go"))
68 | if err != nil {
69 | log.Fatal(err)
70 | return nil
71 | }
72 | defer f.Close()
73 |
74 | _ = packageTemplate.Execute(f, struct {
75 | Timestamp time.Time
76 | Version string
77 | }{
78 | Timestamp: time.Now(),
79 | Version: currentVersion,
80 | })
81 |
82 | return nil
83 | }
84 |
85 | var packageTemplate = template.Must(template.New("").Parse(`// Generated Code; DO NOT EDIT.
86 | // {{ .Timestamp }}
87 | package main
88 |
89 | func init() {
90 | Version = "{{ .Version }}"
91 | }
92 | `))
93 |
--------------------------------------------------------------------------------
/version.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import _ "embed"
4 |
5 | //go:embed VERSION
6 | var version string
7 |
8 | // Version will return the release version
9 | func Version() string {
10 | return version
11 | }
12 |
--------------------------------------------------------------------------------