omnitest-psychic 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +36 -0
- data/.travis.yml +10 -0
- data/CONTRIBUTING.md +61 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +234 -0
- data/Rakefile +12 -0
- data/appveyor.yml +9 -0
- data/bin/psychic +4 -0
- data/docs/code_samples.md +92 -0
- data/docs/index.md +128 -0
- data/lib/omnitest/output_helper.rb +84 -0
- data/lib/omnitest/psychic.rb +191 -0
- data/lib/omnitest/psychic/cli.rb +171 -0
- data/lib/omnitest/psychic/code2doc.rb +75 -0
- data/lib/omnitest/psychic/code2doc/code_helper.rb +122 -0
- data/lib/omnitest/psychic/code2doc/code_segmenter.rb +170 -0
- data/lib/omnitest/psychic/code2doc/comment_styles.rb +92 -0
- data/lib/omnitest/psychic/command_template.rb +35 -0
- data/lib/omnitest/psychic/error.rb +15 -0
- data/lib/omnitest/psychic/execution/default_strategy.rb +82 -0
- data/lib/omnitest/psychic/execution/env_strategy.rb +12 -0
- data/lib/omnitest/psychic/execution/flag_strategy.rb +14 -0
- data/lib/omnitest/psychic/execution/token_strategy.rb +68 -0
- data/lib/omnitest/psychic/factories/go_factories.rb +14 -0
- data/lib/omnitest/psychic/factories/hot_read_task_factory.rb +54 -0
- data/lib/omnitest/psychic/factories/java_factories.rb +81 -0
- data/lib/omnitest/psychic/factories/powershell_factories.rb +53 -0
- data/lib/omnitest/psychic/factories/ruby_factories.rb +56 -0
- data/lib/omnitest/psychic/factories/shell_factories.rb +89 -0
- data/lib/omnitest/psychic/factories/travis_factories.rb +33 -0
- data/lib/omnitest/psychic/factory_manager.rb +46 -0
- data/lib/omnitest/psychic/file_finder.rb +69 -0
- data/lib/omnitest/psychic/hints.rb +21 -0
- data/lib/omnitest/psychic/magic_task_factory.rb +98 -0
- data/lib/omnitest/psychic/script.rb +146 -0
- data/lib/omnitest/psychic/script_factory.rb +66 -0
- data/lib/omnitest/psychic/script_factory_manager.rb +24 -0
- data/lib/omnitest/psychic/script_runner.rb +45 -0
- data/lib/omnitest/psychic/task.rb +6 -0
- data/lib/omnitest/psychic/task_factory_manager.rb +19 -0
- data/lib/omnitest/psychic/task_runner.rb +30 -0
- data/lib/omnitest/psychic/tokens.rb +51 -0
- data/lib/omnitest/psychic/version.rb +5 -0
- data/lib/omnitest/psychic/workflow.rb +27 -0
- data/lib/omnitest/shell.rb +27 -0
- data/lib/omnitest/shell/buff_shellout_executor.rb +41 -0
- data/lib/omnitest/shell/execution_result.rb +61 -0
- data/lib/omnitest/shell/mixlib_shellout_executor.rb +66 -0
- data/mkdocs.yml +6 -0
- data/omnitest-psychic.gemspec +36 -0
- data/spec/fabricators/shell_fabricator.rb +9 -0
- data/spec/omnitest/psychic/code2doc/code_helper_spec.rb +123 -0
- data/spec/omnitest/psychic/execution/default_strategy_spec.rb +17 -0
- data/spec/omnitest/psychic/execution/env_strategy_spec.rb +17 -0
- data/spec/omnitest/psychic/execution/flag_strategy_spec.rb +26 -0
- data/spec/omnitest/psychic/execution/token_strategy_spec.rb +26 -0
- data/spec/omnitest/psychic/factories/java_factories_spec.rb +35 -0
- data/spec/omnitest/psychic/factories/powershell_factories_spec.rb +59 -0
- data/spec/omnitest/psychic/factories/ruby_factories_spec.rb +91 -0
- data/spec/omnitest/psychic/factories/shell_factories_spec.rb +79 -0
- data/spec/omnitest/psychic/factories/travis_factories_spec.rb +78 -0
- data/spec/omnitest/psychic/hot_read_task_factory_spec.rb +51 -0
- data/spec/omnitest/psychic/script_factory_manager_spec.rb +57 -0
- data/spec/omnitest/psychic/script_spec.rb +55 -0
- data/spec/omnitest/psychic/shell_spec.rb +68 -0
- data/spec/omnitest/psychic/workflow_spec.rb +46 -0
- data/spec/omnitest/psychic_spec.rb +170 -0
- data/spec/spec_helper.rb +52 -0
- metadata +352 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
# Running Code Samples
|
2
|
+
|
3
|
+
Psychic can be used to run code samples as well as tasks. It's designed so that if two (or more) projects have a similar set of samples than the same psychic commands should find and run the samples, even if the commands required to run them are drastically different. Psychic does this by:
|
4
|
+
- Searching for sample code by name rather than path
|
5
|
+
- Finding a task runner that is capable of running hte sample
|
6
|
+
- Adjusting how to pass input to the code sample, if necessary
|
7
|
+
|
8
|
+
## Showing Samples
|
9
|
+
|
10
|
+
You can check that psychic is finding the correct code sample before you attempt to run it. If you run:
|
11
|
+
|
12
|
+
```
|
13
|
+
$ psychic show sample "upload file"
|
14
|
+
```
|
15
|
+
|
16
|
+
This will show the information about the sample:
|
17
|
+
|
18
|
+
```
|
19
|
+
Sample Name: upload file
|
20
|
+
Tokens:
|
21
|
+
- authUrl
|
22
|
+
- username
|
23
|
+
- apiKey
|
24
|
+
- region
|
25
|
+
- containerName
|
26
|
+
- localFilePath
|
27
|
+
- remoteObjectName
|
28
|
+
Source File: ObjectStore/upload-object.php
|
29
|
+
```
|
30
|
+
|
31
|
+
If you pass the `--verbose` flag then Psychic will also display the code for the code sample, with syntax highlighting (if your terminal suports color).
|
32
|
+
|
33
|
+
The Tokens section shows what the tokens Psychic has detected that can be used for passing input to the code sample. See the documentation on [Tokens](tokens) for more details.
|
34
|
+
|
35
|
+
### Automatic detection
|
36
|
+
|
37
|
+
The default behavior is for psychic to search for with file with a name that is similar to the name of the sample. It will basically do a case-insensitive search for a file with a name that contains "upload file", ignoring whitespace. It will then display info about the sample it found:
|
38
|
+
|
39
|
+
```
|
40
|
+
$ bundle exec psychic show sample "upload file"
|
41
|
+
Sample Name: upload file
|
42
|
+
Tokens: (None)
|
43
|
+
Source File: storage/upload_file.rb
|
44
|
+
```
|
45
|
+
|
46
|
+
### Custom Map
|
47
|
+
|
48
|
+
You can tell Psychic exactly where to find a sample if it cannot be auto-detected. This is useful [omnitest](https://github.com/omnitest/omnitest) or any other situation where you want the same sample name to work across multiple projects even though the file names of the sample code have very different names.
|
49
|
+
|
50
|
+
You can do this by mapping sample names to files in your `psychic.yaml`. So you could add this:
|
51
|
+
|
52
|
+
```yaml
|
53
|
+
samples:
|
54
|
+
upload file: ObjectStore/upload-object.php
|
55
|
+
```
|
56
|
+
|
57
|
+
Now `psychic show sample "upload file"` will find the correct sample, even though it has "object" rather than "file" in the name.
|
58
|
+
|
59
|
+
### Listing Samples
|
60
|
+
|
61
|
+
There is also a command for listing all known samples. See the [skeptic](https://github.com/omnitest/psychic-skeptic) project if you want to make ensure a project has a required set of samples.
|
62
|
+
|
63
|
+
```
|
64
|
+
$ bundle exec psychic list samples
|
65
|
+
upload file ObjectStore/upload-object.php
|
66
|
+
change metadata ObjectStore/update-object-metadata.php
|
67
|
+
get file ObjectStore/get-object.php
|
68
|
+
create networked server Compute/create_server_with_network.php
|
69
|
+
create keypair Compute/create_new_keypair.php
|
70
|
+
create load balancer LoadBalancer/create-lb.php
|
71
|
+
secure load balancer LoadBalancer/blacklist-ip-range.php
|
72
|
+
setup ssl LoadBalancer/ssl-termination.php
|
73
|
+
delete load balancer LoadBalancer/delete-lb.php
|
74
|
+
```
|
75
|
+
|
76
|
+
### Running a sample
|
77
|
+
|
78
|
+
Once you've checked that Psychic is detecting the correct sample, you can easily tell Psychic to run it with hte `psychic sample` command.
|
79
|
+
|
80
|
+
If you want to see how psychic would run the sample first, you can use the `--print` flag:
|
81
|
+
|
82
|
+
```
|
83
|
+
psychic sample "upload object" --print
|
84
|
+
```
|
85
|
+
|
86
|
+
### Dealing with Input
|
87
|
+
|
88
|
+
Psychic is able to run some code samples that require input. See the [Input documentation](input) for more details.
|
89
|
+
|
90
|
+
### Testing code samples
|
91
|
+
|
92
|
+
You can use Psychic's companion project, [Skeptic](https://github.com/omnitest/psychic-skeptic) to test code samples.
|
data/docs/index.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Psychic::Runner
|
2
|
+
|
3
|
+
Psychic runs anything.
|
4
|
+
|
5
|
+
## What is Psychic
|
6
|
+
|
7
|
+
Psychic is a project to help developers that work on many different projects. It
|
8
|
+
provides a unified interface for running tasks so you don't need to remember a
|
9
|
+
bunch of project specific commands.
|
10
|
+
|
11
|
+
Psychic provides command aliases so that a command like `psychic bootstrap`
|
12
|
+
will invoke a similiar task in any project, but the actual command invoked will vary.
|
13
|
+
It might end up calling `bundle install`, `npm install` `./scripts/boostrap.sh` or some
|
14
|
+
other command.
|
15
|
+
|
16
|
+
Psychic provides a common set of alises for common tasks but also allows you to define
|
17
|
+
custom tasks across different projects, so you could have cross-project commands like
|
18
|
+
`metrics` or `documentation`.
|
19
|
+
|
20
|
+
## Why?
|
21
|
+
|
22
|
+
Psychic exists to provide a common interface for tasks to build, test, and analyze projects while still allowing (or even encouraging) projects to use idiomatic patterns for their particular language. This makes it easy for new contributors to join a project because it follows the "principal of least astonishment", while also providing a consistent interface for tools to build, test or analyze any project.
|
23
|
+
|
24
|
+
The [bootstrap consistency pattern](http://wynnnetherland.com/linked/2013012801/bootstrapping-consistency) is an example of that. It aims to provide "a consistent user experience to get from zero to productive on any new project". Similarly, Travis-CI gives you a consistent interface to test any software project. The command `travis run` will always build and test a project.
|
25
|
+
|
26
|
+
Omnitest is an attempt to make a more generic framework for creating consistent user experiences across projects. That experienec can be "getting from zero to productive" like the bootstrap consistency pattern, "testing a change" like Travis-CI, or even something like "generating end of sprint reports" or "generating and previewing documentation".
|
27
|
+
|
28
|
+
In fact, the reason the project is called "psychic" is because it's supposed to seem like it's reading your mind. When you ask it to do something, like "bootstrap" a project, it should seem like it just magically picks the correct command to run. The project is actually more of a fraud than a clairvoyant - it just uses "[hot](http://en.wikipedia.org/wiki/Hot_reading)" and "[cold](http://en.wikipedia.org/wiki/Cold_reading)" reading tricks to pick the correct command.
|
29
|
+
|
30
|
+
### Psychic vs scripts/*
|
31
|
+
|
32
|
+
ThoughtBot, GitHub and others use a [bootstrap consistency pattern](http://wynnnetherland.com/linked/2013012801/bootstrapping-consistency) to provide "a consistent user experience to get from zero to productive on any new project". The scripts used vary but common examples are:
|
33
|
+
- Bootstrapping via `bin/setup` or `script/bootstrap`
|
34
|
+
- Running tests via `script/test` and/or `script/cibuild`
|
35
|
+
|
36
|
+
Psychic's goals are similar but it's scope is broader, as explained above. Psychic will detect the scripts/* pattern and delegate task commands to it, so `psychic task bootstrap` will run `scripts/bootstrap.sh` if it exists. If you have both `bootstrap.sh` and `boostrap.ps1` (for Windows) psychic will choose the appropriate script for your platform.
|
37
|
+
|
38
|
+
The difference is that if you try to run a task for psychic that doesn't have a script Psychic will continue looking for other ways to run that task. For example, if you have a `scripts/bootstrap.sh` and `Rakefile` that defines `lint` task, then `psychic bootstrap` will run `scripts/bootstrap.sh`, while `psychic lint` will run `rake lint`.
|
39
|
+
|
40
|
+
### Psychic vs Travis-Build
|
41
|
+
|
42
|
+
The goals of psychic are also similar to travis-build, and psychic will delegate supported tasks to travis-build if it is installed. Even if it isn't installed, Psychic aims to be as compatible as possible with travis-build, so a command like `psychic task install` behave a lot like `travis run install`.
|
43
|
+
|
44
|
+
Note that travis-build is not installed as a normal gem or Psychic would just depend on it. If you want psychic to delegate to travis-build you need to [install it as a CLI extension](https://github.com/travis-ci/travis-build#use-as-addon-for-cli).
|
45
|
+
|
46
|
+
#### Scope
|
47
|
+
Psychic's scope is broader, so there are things you could do with psychic that wouldn't make sense to run on Travis-CI. For example, you could setup a command like `psychic wip`, that would show your work in progress for any given project. The behavior would be project specific, but could include things like:
|
48
|
+
- Listing your local branches that haven't been merged
|
49
|
+
- Listing pull requests that are assigned to you
|
50
|
+
- Issues that are assigned to you
|
51
|
+
- Display `what_im_working_on.txt`
|
52
|
+
|
53
|
+
The command could be setup so it would display a WIP report for any project, even if some projects are getting info from your local git branches, other from GitHub, and others from issue trackers like JIRA, Launchpad, or Mingle. This command obviously doesn't make sense on Travis-CI, especially if it's only showing the work in progress for a current developer, but could be very useful if you want to quickly review your WIP across several projects. That's what the [omnitest](https://github.com/omnitest/omnitest) tool's [crosstasking](https://github.com/omnitest/omnitest#crosstasking-via-psychic) feature is designed to do.
|
54
|
+
|
55
|
+
#### Features
|
56
|
+
|
57
|
+
This is one of the reasons why psychic was created as a new project rather than as enhancements to travis-build. In general, Psychic aims to provide a simpler API that is less coupled with Travis. The major differences between Psychic and travis-build are:
|
58
|
+
- Psychic is distributed as a gem that can be used as a library or a standalone CLI. Travis-build is a
|
59
|
+
travis CLI extension that is not distributed as a gem.
|
60
|
+
- Psychic as a simple API for running tasks by alias. The travis-build API is tightly coupled with a travis-configuration object.
|
61
|
+
- Psychic just provides task aliases and inference. It does not provide environment setup, like fetching projects from version control or driving environment managers like rvm or virtualenv. Those actions should be done before invoking psychic.
|
62
|
+
- Psychic does not contain travis-specific features like integration with travis's caching system.
|
63
|
+
|
64
|
+
## Psychic Commands
|
65
|
+
|
66
|
+
### Tasks
|
67
|
+
|
68
|
+
Psychic supports both built-in and custom tasks. You should be able to use built-in tasks in virtually any project, even projects that weren't setup for psychic, because these tasks are mapped to common actions of commonly used tools.
|
69
|
+
|
70
|
+
#### Built-in Tasks
|
71
|
+
|
72
|
+
The `bootstrap` task is an example of a built-in task. If you run `psychic bootstrap` it will setup your project. It detects tools or patterns that are commonly used to setup projects and will choose an appropriate command for that tool. So it may run something like `bundle install`, `npm install`, or `scripts/bootstrap.sh`, depending on your project.
|
73
|
+
|
74
|
+
The built-in tasks are all mapped to top-level commands, so you can see them by running `psychic help`.
|
75
|
+
|
76
|
+
#### Custom Tasks
|
77
|
+
|
78
|
+
Psychic can also run custom tasks. You can list all known tasks, including custom tasks, with the command `psychic list tasks`. If you run with the `--verbose` flag it will show the command that would be run.
|
79
|
+
|
80
|
+
This list will include any custom tasks mapped to a command in `psychic.yaml`. Psychic will also try to detect known tasks from any tool that can print out a list of documented tasks, like Rake (via `rake --tasks` ), Grunt (via `grunt --help`) or NPM (via `npm run`). This is only partially supported, because not all tools support listing tasks, and even the ones that do rarely have an option for machine-readable output or an option like git's `--porcelain` (to "give the output in an easy-to-parse format for scripts", which will "remain stable across versions regardless of user configuration").
|
81
|
+
|
82
|
+
You can run a custom task via `psychic task <task_name>`. So if you have defined a `lint` task in a tool that supports autodetection, or defined the task in `psychic.yaml`, then you can run it with `psychic task lint`.
|
83
|
+
|
84
|
+
```sh
|
85
|
+
bundle exec psychic task lint
|
86
|
+
I, [2015-01-15T13:01:09.752242 #59850] INFO -- : Executing bundle exec rubocop -D
|
87
|
+
I, [2015-01-15T13:01:10.595926 #59850] INFO -- : warning: parser/current is loading parser/ruby21, which recognizes
|
88
|
+
I, [2015-01-15T13:01:10.596019 #59850] INFO -- : warning: 2.1.5-compliant syntax, but you are running 2.1.4.
|
89
|
+
I, [2015-01-15T13:01:11.142419 #59850] INFO -- : Inspecting 2 files
|
90
|
+
I, [2015-01-15T13:01:11.142526 #59850] INFO -- : ..
|
91
|
+
I, [2015-01-15T13:01:11.142553 #59850] INFO -- :
|
92
|
+
I, [2015-01-15T13:01:11.142576 #59850] INFO -- : 2 files inspected, no offenses detected
|
93
|
+
```
|
94
|
+
|
95
|
+
You can also pass additional arguments to the command after the end of options delimiter (`--`). This is useful for passing flags like `--debug`, `--verbose` or `--help`, though there is no guarantee that any of these flags will work (even `--help`) will work for a task.
|
96
|
+
|
97
|
+
```sh
|
98
|
+
$ bundle exec psychic task lint -- --debug
|
99
|
+
I, [2015-01-15T13:06:11.456547 #60908] INFO -- : Executing bundle exec rubocop -D --debug
|
100
|
+
I, [2015-01-15T13:06:12.288424 #60908] INFO -- : warning: parser/current is loading parser/ruby21, which recognizes
|
101
|
+
I, [2015-01-15T13:06:12.289181 #60908] INFO -- : warning: 2.1.5-compliant syntax, but you are running 2.1.4.
|
102
|
+
I, [2015-01-15T13:06:12.689274 #60908] INFO -- : For /Users/Thoughtworker/repos/rackspace/polytrix/samples/sdks/ruby: configuration from /Users/Thoughtworker/repos/rackspace/polytrix/.rubocop.yml
|
103
|
+
I, [2015-01-15T13:06:12.689343 #60908] INFO -- : Inheriting configuration from /Users/Thoughtworker/repos/rackspace/polytrix/.rubocop_todo.yml
|
104
|
+
I, [2015-01-15T13:06:12.689372 #60908] INFO -- : Default configuration from /opt/boxen/rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rubocop-0.28.0/config/default.yml
|
105
|
+
I, [2015-01-15T13:06:12.689396 #60908] INFO -- : Inheriting configuration from /opt/boxen/rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rubocop-0.28.0/config/enabled.yml
|
106
|
+
I, [2015-01-15T13:06:12.689421 #60908] INFO -- : Inheriting configuration from /opt/boxen/rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rubocop-0.28.0/config/disabled.yml
|
107
|
+
I, [2015-01-15T13:06:12.689447 #60908] INFO -- : Inspecting 2 files
|
108
|
+
I, [2015-01-15T13:06:12.689487 #60908] INFO -- : Scanning /Users/Thoughtworker/repos/rackspace/polytrix/samples/sdks/ruby/Gemfile
|
109
|
+
I, [2015-01-15T13:06:12.689514 #60908] INFO -- : .Scanning /Users/Thoughtworker/repos/rackspace/polytrix/samples/sdks/ruby/katas/hello_world.rb
|
110
|
+
I, [2015-01-15T13:06:12.689534 #60908] INFO -- : .
|
111
|
+
I, [2015-01-15T13:06:12.689553 #60908] INFO -- :
|
112
|
+
I, [2015-01-15T13:06:12.689574 #60908] INFO -- : 2 files inspected, no offenses detected
|
113
|
+
I, [2015-01-15T13:06:12.689595 #60908] INFO -- : Finished in 0.04216 seconds
|
114
|
+
```
|
115
|
+
|
116
|
+
### Code Samples
|
117
|
+
|
118
|
+
Psychic also has features to detect and run a code sample by alias, just like it runs tasks by an alias. This gets more complicated and might be split into a separate gem in the future, so we'll leave that for a separate doc.
|
119
|
+
|
120
|
+
## Related projects
|
121
|
+
|
122
|
+
### Skeptic
|
123
|
+
|
124
|
+
The [Skeptic](https://github.com/omnitest/skeptic) project is a companion to Psychic that tests the results code samples via Psychic. It captures and validates the output the exit code and output of the process, but can also capture additional data through "spies" like looking for HTTP calls it expects to see or files that should be created. So it let's you write assertions and reports on the behavior of code that's executed via Psychic.
|
125
|
+
|
126
|
+
### Omnitest
|
127
|
+
|
128
|
+
The [omnitest](https://github.com/omnitest/omnitest) project is for running tasks or tests across multiple projects. It uses Psychic (and Skeptic) in order to run the task in teach project, and then consolidates all of the results and produces reports.
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Omnitest
|
2
|
+
module OutputHelper
|
3
|
+
class StringShell < Thor::Base.shell
|
4
|
+
attr_reader :io
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@io = StringIO.new
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method :stdout, :io
|
12
|
+
alias_method :stderr, :io
|
13
|
+
|
14
|
+
def string
|
15
|
+
@io.string
|
16
|
+
end
|
17
|
+
|
18
|
+
def can_display_colors?
|
19
|
+
# Still capture colors if they can eventually be displayed.
|
20
|
+
$stdout.tty?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli
|
25
|
+
@cli ||= Thor::Base.shell.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_string
|
29
|
+
old_cli = @cli
|
30
|
+
new_cli = @cli = StringShell.new
|
31
|
+
yield
|
32
|
+
@cli = old_cli
|
33
|
+
new_cli.string
|
34
|
+
end
|
35
|
+
|
36
|
+
def reformat(string)
|
37
|
+
return if string.nil? || string.empty?
|
38
|
+
|
39
|
+
indent do
|
40
|
+
string.gsub(/^/, indent)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def indent
|
45
|
+
@indent_level ||= 0
|
46
|
+
if block_given?
|
47
|
+
@indent_level += 2
|
48
|
+
result = yield
|
49
|
+
@indent_level -= 2
|
50
|
+
result
|
51
|
+
else
|
52
|
+
' ' * @indent_level
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def say(msg)
|
57
|
+
cli.say msg if msg
|
58
|
+
end
|
59
|
+
|
60
|
+
def status(status, msg = nil, color = :cyan, colwidth = 50)
|
61
|
+
msg = yield if block_given?
|
62
|
+
cli.say(indent) if indent.length > 0
|
63
|
+
status = cli.set_color("#{status}:", color, true)
|
64
|
+
# The built-in say_status is right-aligned, we want left-aligned
|
65
|
+
cli.say format("%-#{colwidth}s %s", status, msg).rstrip
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: Reporters for different formats
|
69
|
+
def print_table(*args)
|
70
|
+
# @reporter.print_table(*args)
|
71
|
+
cli.print_table(*args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def colorize(string, *args)
|
75
|
+
return string unless @reporter.respond_to? :set_color
|
76
|
+
# @reporter.set_color(string, *args)
|
77
|
+
cli.set_color(string, *args)
|
78
|
+
end
|
79
|
+
|
80
|
+
def color_pad(string)
|
81
|
+
string + colorize('', :white)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'omnitest/core'
|
2
|
+
require 'omnitest/psychic/version'
|
3
|
+
require 'omnitest/psychic/error'
|
4
|
+
require 'omnitest/psychic/code2doc'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
autoload :Thor, 'thor'
|
8
|
+
|
9
|
+
module Omnitest
|
10
|
+
autoload :Shell, 'omnitest/shell'
|
11
|
+
autoload :OutputHelper, 'omnitest/output_helper'
|
12
|
+
|
13
|
+
# The primary interface for using Psychic as an API.
|
14
|
+
#
|
15
|
+
# Detects scripts and tools that can run tasks in the instance's working directory,
|
16
|
+
# so that Psychic can act as a universal task/script selection and execution system.
|
17
|
+
class Psychic # rubocop:disable Metrics/ClassLength
|
18
|
+
autoload :Tokens, 'omnitest/psychic/tokens'
|
19
|
+
module Tokens
|
20
|
+
autoload :RegexpTokenHandler, 'omnitest/psychic/tokens'
|
21
|
+
autoload :MustacheTokenHandler, 'omnitest/psychic/tokens'
|
22
|
+
end
|
23
|
+
module Execution
|
24
|
+
autoload :DefaultStrategy, 'omnitest/psychic/execution/default_strategy'
|
25
|
+
autoload :TokenStrategy, 'omnitest/psychic/execution/token_strategy'
|
26
|
+
autoload :EnvStrategy, 'omnitest/psychic/execution/env_strategy'
|
27
|
+
autoload :FlagStrategy, 'omnitest/psychic/execution/flag_strategy'
|
28
|
+
end
|
29
|
+
autoload :Hints, 'omnitest/psychic/hints'
|
30
|
+
autoload :FileFinder, 'omnitest/psychic/file_finder'
|
31
|
+
autoload :FactoryManager, 'omnitest/psychic/factory_manager'
|
32
|
+
autoload :ScriptFactoryManager, 'omnitest/psychic/script_factory_manager'
|
33
|
+
autoload :ScriptFactoryManager, 'omnitest/psychic/script_factory_manager'
|
34
|
+
autoload :TaskFactoryManager, 'omnitest/psychic/task_factory_manager'
|
35
|
+
autoload :MagicTaskFactory, 'omnitest/psychic/magic_task_factory'
|
36
|
+
autoload :ScriptFactory, 'omnitest/psychic/script_factory'
|
37
|
+
autoload :CommandTemplate, 'omnitest/psychic/command_template'
|
38
|
+
autoload :Task, 'omnitest/psychic/task'
|
39
|
+
autoload :Script, 'omnitest/psychic/script'
|
40
|
+
autoload :Workflow, 'omnitest/psychic/workflow'
|
41
|
+
autoload :TaskRunner, 'omnitest/psychic/task_runner'
|
42
|
+
autoload :ScriptRunner, 'omnitest/psychic/script_runner'
|
43
|
+
|
44
|
+
FactoryManager.autoload_factories!
|
45
|
+
|
46
|
+
include Core::Logger
|
47
|
+
include Shell
|
48
|
+
include TaskRunner
|
49
|
+
include ScriptRunner
|
50
|
+
|
51
|
+
# @return [String] A name for logging and reporting.
|
52
|
+
# The default value is the name of the current working directory.
|
53
|
+
attr_reader :name
|
54
|
+
# @return [Dir] Current working directory for running commands.
|
55
|
+
attr_reader :cwd
|
56
|
+
alias_method :basedir, :cwd
|
57
|
+
# @return [Hash] Environment variables to use when executing commands.
|
58
|
+
# The default is to pass all environment variables to the command.
|
59
|
+
attr_reader :env
|
60
|
+
# @return [String] The Operating System to target. Autodetected if unset.
|
61
|
+
attr_reader :os
|
62
|
+
# @return [Hints] Psychic "hints" that are used to help Psychic locate tasks or scripts.
|
63
|
+
attr_reader :hints
|
64
|
+
# @return [Hash] Parameters to use as input for scripts.
|
65
|
+
attr_reader :parameters
|
66
|
+
# @return [Hash] Additional options
|
67
|
+
attr_reader :opts
|
68
|
+
|
69
|
+
DEFAULT_PARAMS_FILE = 'psychic-parameters.yaml'
|
70
|
+
|
71
|
+
# Creates a new Psychic instance that can be used to execute tasks and scripts.
|
72
|
+
# All options are
|
73
|
+
# @params [Hash] opts
|
74
|
+
# @option opts [Dir] :cwd sets the current working directory
|
75
|
+
# @option opts [Logger] :logger assigns a logger
|
76
|
+
# @option opts [Hash] :env sets environment variables
|
77
|
+
# @option opts [String] :name a name for logging and reporting
|
78
|
+
# @option opts [String] :os the target operating system
|
79
|
+
# @option opts [String] :interactive run psychic in interactive mode, where it will prompt for input
|
80
|
+
def initialize(opts = { cwd: Dir.pwd }) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
81
|
+
opts[:cwd] ||= Dir.pwd
|
82
|
+
# must be a string on windows...
|
83
|
+
@cwd = opts[:cwd] = Pathname(opts[:cwd]).to_s
|
84
|
+
@opts = opts
|
85
|
+
init_attr(:name) { File.basename cwd }
|
86
|
+
init_hints
|
87
|
+
init_attr(:logger) { new_logger }
|
88
|
+
init_attr(:env) { ENV.to_hash }
|
89
|
+
init_attr(:os) { RbConfig::CONFIG['host_os'] }
|
90
|
+
init_attrs :cli, :interactive, :parameter_mode, :restore_mode, :print
|
91
|
+
@shell_opts = select_shell_opts
|
92
|
+
@parameters = load_parameters(opts[:parameters])
|
93
|
+
end
|
94
|
+
|
95
|
+
# Executes a command using the options set on this Psychic instance.
|
96
|
+
# @param [String] command the command to execute
|
97
|
+
# @param [*args] *args additional arguments to join to the command
|
98
|
+
# @return [ExecutionResult] the result of running the command
|
99
|
+
# @raises [ExecutionError] if the command
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# psychic.execute('echo', 'hello', 'world')
|
103
|
+
# #<Omnitest::Shell::ExecutionResult:0x007fdfe15208f0 @command="echo hello world",
|
104
|
+
# @exitstatus=0, @stderr="", @stdout="hello world\n">
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# psychic.execute('foo')
|
108
|
+
# # Omnitest::Shell::ExecutionError: No such file or directory - foo
|
109
|
+
def execute(command, *args)
|
110
|
+
shell_opts = @shell_opts.dup
|
111
|
+
shell_opts.merge!(args.shift) if args.first.is_a? Hash
|
112
|
+
full_cmd = [command, *args].join(' ')
|
113
|
+
logger.banner("Executing: #{full_cmd}")
|
114
|
+
shell.execute(full_cmd, shell_opts)
|
115
|
+
end
|
116
|
+
|
117
|
+
def workflow(name = 'workflow', options = {}, &block)
|
118
|
+
Workflow.new(self, name, options, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Detects the Operating System family for the selected Operating System.
|
122
|
+
# @return [Symbol] The operating system family for {#os}.
|
123
|
+
def os_family
|
124
|
+
case os
|
125
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
126
|
+
:windows
|
127
|
+
when /darwin|mac os/
|
128
|
+
:macosx
|
129
|
+
when /linux/
|
130
|
+
:linux
|
131
|
+
when /solaris|bsd/
|
132
|
+
:unix
|
133
|
+
else
|
134
|
+
:unknown
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [Boolean] true if Psychic is in interactive mode and will prompt for decisions
|
139
|
+
def interactive?
|
140
|
+
@opts[:interactive]
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def init_attr(var)
|
146
|
+
var_name = "@#{var}"
|
147
|
+
var_value = @opts[var]
|
148
|
+
var_value = yield if var_value.nil? && block_given?
|
149
|
+
instance_variable_set(var_name, var_value)
|
150
|
+
end
|
151
|
+
|
152
|
+
def init_attrs(*vars)
|
153
|
+
vars.each do | var |
|
154
|
+
init_attr var
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def init_hints
|
159
|
+
hint_data = Omnitest::Core::Util.symbolized_hash(@opts[:hints] || load_hints || {})
|
160
|
+
@hints = Hints.new hint_data
|
161
|
+
@opts.merge! Omnitest::Core::Util.symbolized_hash(@hints.options)
|
162
|
+
end
|
163
|
+
|
164
|
+
def select_shell_opts
|
165
|
+
# Make sure to delete any option that isn't a MixLib::ShellOut option
|
166
|
+
@opts.select { |key, _| Omnitest::Shell::AVAILABLE_OPTIONS.include? key }
|
167
|
+
end
|
168
|
+
|
169
|
+
def load_hints
|
170
|
+
hints_file = Dir.glob("#{@cwd}/psychic.{yaml,yml}", File::FNM_CASEFOLD).first
|
171
|
+
YAML.load(File.read(hints_file)) unless hints_file.nil?
|
172
|
+
end
|
173
|
+
|
174
|
+
def load_parameters(parameters)
|
175
|
+
if parameters.nil? || parameters.is_a?(String)
|
176
|
+
load_parameters_file(parameters)
|
177
|
+
else
|
178
|
+
parameters
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def load_parameters_file(file = nil)
|
183
|
+
if file.nil?
|
184
|
+
file ||= File.expand_path(DEFAULT_PARAMS_FILE, cwd)
|
185
|
+
return {} unless File.exist? file
|
186
|
+
end
|
187
|
+
# Just return it as a template, not as YAML
|
188
|
+
File.read(file)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|