shaman_cli 2.6 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dockerignore +4 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/pull_request_template.md +9 -0
- data/.github/workflows/build-app-image-and-run-checks.yml +39 -0
- data/.rspec +1 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +63 -0
- data/Dockerfile +15 -0
- data/Gemfile +9 -0
- data/README.md +9 -0
- data/Rakefile +5 -3
- data/bin/console +4 -10
- data/docker-compose.yml +17 -0
- data/exe/shaman +2 -40
- data/lib/shaman/cli.rb +59 -0
- data/lib/shaman/deploy.rb +74 -24
- data/lib/shaman/helpers.rb +5 -3
- data/lib/shaman/init.rb +46 -42
- data/lib/shaman/tryout_apps/client.rb +27 -0
- data/lib/shaman/tryout_apps/resource/project.rb +79 -0
- data/lib/shaman/tryout_apps/resource/release.rb +46 -0
- data/lib/shaman/tryout_apps/resource.rb +21 -0
- data/lib/shaman/tryout_apps.rb +9 -0
- data/lib/shaman/version.rb +3 -1
- data/lib/shaman.rb +33 -9
- data/shaman.gemspec +13 -13
- data/tmp/.keep +0 -0
- metadata +42 -83
- data/.travis.yml +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dcb33a085aa0c609c2f7962d581e8dfb3c64deefdb9ea54f2c8dcd9b05f9cad
|
4
|
+
data.tar.gz: 75bfe0f9edab951a8a2c7e0a24bc8c0a221336f91e6dec16317b205ed0b74fab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba4358814ae2c7641ecd97ab99edb3e6a6256c798db7d140b23c247d5409367579d92a81becc48bc181c509002dac8d9704d06704f62e5bf996779928fd2ee22
|
7
|
+
data.tar.gz: 6c343328fd16f1ea1adf857aa8f99be57f9027415910af36b1a2e3c939237fe27eea0f204a7b46339dd4da9bce1f8e40b8d970e16e8b2dfe9260dcdfae487933
|
data/.dockerignore
ADDED
data/.github/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @uncoverd @vr4b4c
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Task: [#__TASK_NUMBER__](__PRODUCTIVE_TASK_URL__) <!-- If applicable -->
|
2
|
+
|
3
|
+
## Description
|
4
|
+
<!-- Please describe what you have changed or added -->
|
5
|
+
|
6
|
+
## Checklist:
|
7
|
+
- [ ] Specs are added or updated.
|
8
|
+
- [ ] Changes follow the code style set by the shaman project.
|
9
|
+
- [ ] CHANGELOG.md is updated.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: Build app image and run checks
|
2
|
+
concurrency:
|
3
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
4
|
+
cancel-in-progress: true
|
5
|
+
|
6
|
+
on:
|
7
|
+
workflow_dispatch:
|
8
|
+
pull_request:
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
build_image:
|
12
|
+
name: Build app image and run checks
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v4
|
17
|
+
|
18
|
+
- name: Set up Docker Buildx
|
19
|
+
id: buildx
|
20
|
+
uses: docker/setup-buildx-action@v3
|
21
|
+
|
22
|
+
- name: Build app image
|
23
|
+
uses: docker/build-push-action@v5
|
24
|
+
with:
|
25
|
+
builder: ${{ steps.buildx.outputs.name }}
|
26
|
+
context: .
|
27
|
+
file: Dockerfile
|
28
|
+
target: ci
|
29
|
+
push: false
|
30
|
+
load: true
|
31
|
+
tags: app
|
32
|
+
cache-from: type=gha
|
33
|
+
cache-to: type=gha,mode=max
|
34
|
+
|
35
|
+
- name: Lint
|
36
|
+
run: docker run app rubocop
|
37
|
+
|
38
|
+
- name: Test
|
39
|
+
run: docker run app rspec
|
data/.rspec
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-infinum: rubocop.yml
|
3
|
+
|
4
|
+
require: rubocop-infinum
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 3.0
|
8
|
+
|
9
|
+
Gemspec/RequiredRubyVersion:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Rails:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
RSpec/ExampleLength:
|
16
|
+
Max: 20
|
17
|
+
|
18
|
+
RSpec/MultipleExpectations:
|
19
|
+
Max: 5
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.6
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [3.0.0] - 2024-04-19
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Specs
|
15
|
+
- RuboCop linter and formatter
|
16
|
+
- CI steps
|
17
|
+
- CHANGELOG.md
|
18
|
+
- codeowners file
|
19
|
+
- pull request template
|
20
|
+
- Require Ruby >= 3.0
|
21
|
+
- Dependency version constraints
|
22
|
+
- Custom exit code for processing errors
|
23
|
+
- Development section to the README
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
|
27
|
+
- Modularize CLI
|
28
|
+
- Fixed syntax to be compliant with Ruby 3.0
|
29
|
+
|
30
|
+
### Removed
|
31
|
+
|
32
|
+
- Travis CI config
|
33
|
+
- Removed unused proguard file deploy option
|
34
|
+
|
35
|
+
## [2.7.0] - 2024-04-19
|
36
|
+
|
37
|
+
## [2.6] - 2019-07-19
|
38
|
+
|
39
|
+
## [2.5.0] - 2019-01-28
|
40
|
+
|
41
|
+
## [2.4.0] - 2018-01-30
|
42
|
+
|
43
|
+
## [2.3.0] - 2017-09-29
|
44
|
+
|
45
|
+
## [2.2.0] - 2017-03-23
|
46
|
+
|
47
|
+
## [2.1.1] - 2017-01-11
|
48
|
+
|
49
|
+
## [2.1.0] - 2016-11-22
|
50
|
+
|
51
|
+
## [2.0.0] - 2016-02-23
|
52
|
+
|
53
|
+
[unreleased]: https://github.com/infinum/shaman/compare/v3.0.0...HEAD
|
54
|
+
[3.0.0]: https://github.com/infinum/shaman/compare/v2.7.0...v3.0.0
|
55
|
+
[2.7.0]: https://rubygems.org/gems/shaman_cli/versions/2.7.0
|
56
|
+
[2.6]: https://rubygems.org/gems/shaman_cli/versions/2.6
|
57
|
+
[2.5.0]: https://rubygems.org/gems/shaman_cli/versions/2.5.0
|
58
|
+
[2.4.0]: https://rubygems.org/gems/shaman_cli/versions/2.4.0
|
59
|
+
[2.3.0]: https://rubygems.org/gems/shaman_cli/versions/2.3.0
|
60
|
+
[2.2.0]: https://rubygems.org/gems/shaman_cli/versions/2.2.0
|
61
|
+
[2.1.1]: https://rubygems.org/gems/shaman_cli/versions/2.1.1
|
62
|
+
[2.1.0]: https://rubygems.org/gems/shaman_cli/versions/2.1.0
|
63
|
+
[2.0.0]: https://rubygems.org/gems/shaman_cli/versions/2.0.0
|
data/Dockerfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
FROM ruby:3.0 as base
|
2
|
+
|
3
|
+
RUN apt-get update -qq \
|
4
|
+
&& apt-get install -yq --no-install-recommends git build-essential less
|
5
|
+
|
6
|
+
WORKDIR /app
|
7
|
+
|
8
|
+
COPY Gemfile shaman.gemspec ./
|
9
|
+
COPY lib/shaman/version.rb lib/shaman/version.rb
|
10
|
+
|
11
|
+
RUN bundle install
|
12
|
+
|
13
|
+
FROM base as ci
|
14
|
+
RUN mkdir /app/tmp
|
15
|
+
COPY . /app
|
data/Gemfile
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in shaman.gemspec
|
4
6
|
gemspec
|
7
|
+
|
8
|
+
gem 'pry'
|
9
|
+
gem 'rake'
|
10
|
+
gem 'rspec', '~> 3.13'
|
11
|
+
gem 'rubocop-infinum'
|
12
|
+
gem 'solargraph'
|
13
|
+
gem 'webmock', '~> 3.23'
|
data/README.md
CHANGED
@@ -66,6 +66,15 @@ This will deploy your relese file to labs with with configs from .shaman.yml
|
|
66
66
|
-C, --commit COMMIT Which commit to use instead of HEAD
|
67
67
|
-n, --release_name RELEASE_NAME Release name (ZIP platform only)
|
68
68
|
|
69
|
+
## Development
|
70
|
+
|
71
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests.
|
72
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
73
|
+
|
74
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update
|
75
|
+
the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for
|
76
|
+
the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
77
|
+
|
69
78
|
## License
|
70
79
|
|
71
80
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'shaman'
|
5
6
|
|
6
|
-
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
require "pry"
|
7
|
+
require 'pry'
|
11
8
|
Pry.start
|
12
|
-
|
13
|
-
# require "irb"
|
14
|
-
# IRB.start
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.9'
|
2
|
+
name: infinum-shaman
|
3
|
+
|
4
|
+
services:
|
5
|
+
runner:
|
6
|
+
build:
|
7
|
+
context: .
|
8
|
+
target: base
|
9
|
+
image: shaman:1.0.0
|
10
|
+
environment:
|
11
|
+
HISTFILE: /app/tmp/bash_history
|
12
|
+
SHAMAN_TOKEN: change-me
|
13
|
+
volumes:
|
14
|
+
- .:/app:cached
|
15
|
+
command: ['bash']
|
16
|
+
stdin_open: true
|
17
|
+
tty: true
|
data/exe/shaman
CHANGED
@@ -1,45 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'rubygems'
|
4
|
-
require 'commander/import'
|
5
5
|
require 'shaman'
|
6
|
-
require 'pry' if ENV['SHAMAN_ENV'] == 'development'
|
7
6
|
|
8
|
-
|
9
|
-
program :description, 'Shaman cli for labs'
|
10
|
-
program :help_formatter, :compact
|
11
|
-
|
12
|
-
command :init do |c|
|
13
|
-
c.syntax = 'shaman init'
|
14
|
-
c.description = 'Initialize your project. EXAMPLE: shaman init -s abamobi'
|
15
|
-
c.option '-s', '--search STRING', String, 'Shrink project selection'
|
16
|
-
c.option '-f', '--favorites', 'Show only favorites'
|
17
|
-
c.option '-p', '--platform PLATFORM', 'Choose platform'
|
18
|
-
c.option '-i', '--project_id PROJECT', 'Choose project id'
|
19
|
-
c.action do |_args, options|
|
20
|
-
Shaman::Init.check
|
21
|
-
Shaman::Init.init(options)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
command :deploy do |c|
|
26
|
-
c.syntax = 'shaman deploy [environment]'
|
27
|
-
c.description = 'Deploy a release to specified environment'
|
28
|
-
c.option '-m', '--message MESSAGE', String, 'Changelog message'
|
29
|
-
c.option '-f', '--file FILE', String, 'Release path'
|
30
|
-
c.option '-p', '--proguard FILE', String, 'Add aditional proguard mapping'
|
31
|
-
c.option '-t', '--token TOKEN', String, 'Use different user token'
|
32
|
-
c.option '-c', '--config FILE', String, 'Use different config file'
|
33
|
-
c.option '-g', '--git', 'Use git for message (overrides any manual settings!)'
|
34
|
-
c.option '-C', '--commit COMMIT', String, 'Which commit to use instead of HEAD'
|
35
|
-
c.option '-M', '--minimum_version', 'Set release as minimum version'
|
36
|
-
c.option '-n', '--release_name RELEASE_NAME', String, 'Release name (ZIP platform only)'
|
37
|
-
c.action do |args, options|
|
38
|
-
begin
|
39
|
-
Shaman::Deploy.new(args, options).deploy
|
40
|
-
rescue => e
|
41
|
-
say_error e.message
|
42
|
-
exit(-1)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
7
|
+
Shaman::CLI.new.run
|
data/lib/shaman/cli.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'commander'
|
4
|
+
|
5
|
+
module Shaman
|
6
|
+
class CLI
|
7
|
+
module ExitCode
|
8
|
+
PROCESSING_ERROR = 99
|
9
|
+
end
|
10
|
+
|
11
|
+
include Commander::Methods
|
12
|
+
|
13
|
+
def run
|
14
|
+
program :name, 'shaman'
|
15
|
+
program :version, Shaman::VERSION
|
16
|
+
program :description, 'Shaman CLI for labs'
|
17
|
+
program :help_formatter, :compact
|
18
|
+
|
19
|
+
init
|
20
|
+
deploy
|
21
|
+
|
22
|
+
run!
|
23
|
+
end
|
24
|
+
|
25
|
+
def init # rubocop:disable Metrics/MethodLength
|
26
|
+
command :init do |c|
|
27
|
+
c.syntax = 'shaman init'
|
28
|
+
c.description = 'Initialize your project. EXAMPLE: shaman init -s abamobi'
|
29
|
+
c.option '-s', '--search SEARCH', String, 'Filter projects by a search term'
|
30
|
+
c.option '-f', '--favorites', 'Show only favorites'
|
31
|
+
c.option '-p', '--platform PLATFORM', 'Choose platform'
|
32
|
+
c.option '-i', '--project_id PROJECT', 'Choose project id'
|
33
|
+
c.action do |_args, options|
|
34
|
+
Shaman::Init.check!
|
35
|
+
Shaman::Init.init(options)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def deploy # rubocop:disable Metrics/MethodLength
|
41
|
+
command :deploy do |c|
|
42
|
+
c.syntax = 'shaman deploy [environment]'
|
43
|
+
c.description = 'Deploy a release to specified environment'
|
44
|
+
c.option '-m', '--message MESSAGE', String, 'Changelog message'
|
45
|
+
c.option '-f', '--file FILE', String, 'Release path'
|
46
|
+
c.option '-t', '--token TOKEN', String, 'Use different user token'
|
47
|
+
c.option '-c', '--config FILE', String, 'Use different config file'
|
48
|
+
c.option '-g', '--git', 'Use git for message (overrides any manual settings!)'
|
49
|
+
c.option '-C', '--commit COMMIT', String, 'Which commit to use instead of HEAD'
|
50
|
+
c.option '-M', '--minimum_version', 'Set release as minimum version'
|
51
|
+
c.option '-n', '--release_name RELEASE_NAME', String, 'Release name (ZIP platform only)'
|
52
|
+
c.option '-x', '--env_token ENV_TOKEN', String, 'Environment token'
|
53
|
+
c.action do |args, options|
|
54
|
+
Shaman::Deploy.new(args, options).deploy
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/shaman/deploy.rb
CHANGED
@@ -1,53 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
1
5
|
module Shaman
|
2
6
|
class Deploy
|
3
7
|
include Helpers
|
8
|
+
include Commander::Methods
|
9
|
+
|
10
|
+
DEFAULT_ENVIRONMENT = 'default'
|
11
|
+
DEFAULT_EDITOR = 'vi'
|
12
|
+
DEFAULT_MESSAGE = ''
|
13
|
+
DEFAULT_RELEASE_NAME = ''
|
14
|
+
GIT_DEFAULT_DIRECTORY = '.'
|
15
|
+
GIT_HEAD = 'HEAD'
|
4
16
|
|
5
17
|
def initialize(args, options)
|
6
|
-
|
7
|
-
@environment = args.first
|
18
|
+
@environment = args.first || DEFAULT_ENVIRONMENT
|
8
19
|
@options = options
|
9
|
-
|
20
|
+
verify_release!
|
10
21
|
end
|
11
22
|
|
12
23
|
def deploy
|
13
|
-
prompt.ok "Connecting to #{
|
14
|
-
|
15
|
-
response
|
24
|
+
prompt.ok "Connecting to #{Shaman.tryout_apps_base_uri}/api/v1/releases"
|
25
|
+
|
26
|
+
response = Shaman.tryout_apps_client.create_release(release)
|
27
|
+
|
28
|
+
if response.success? == 200
|
29
|
+
prompt.ok(response.data)
|
30
|
+
else
|
31
|
+
error!("Failed to create a release: #{response.data}")
|
32
|
+
end
|
16
33
|
end
|
17
34
|
|
18
35
|
private
|
19
36
|
|
20
|
-
attr_reader :environment
|
37
|
+
attr_reader :environment
|
38
|
+
attr_reader :options
|
39
|
+
|
40
|
+
def release
|
41
|
+
@release ||=
|
42
|
+
Shaman::TryoutApps::Resource::Release::CreateInput.new({ file: release_file,
|
43
|
+
environment_token: environment_token,
|
44
|
+
message: message,
|
45
|
+
token: token,
|
46
|
+
minimum_version: minimum_version,
|
47
|
+
name: name })
|
48
|
+
end
|
49
|
+
|
50
|
+
def environment_token
|
51
|
+
options.env_token || config[:token]
|
52
|
+
end
|
53
|
+
|
54
|
+
def release_file
|
55
|
+
options.file || config[:release_path]
|
56
|
+
end
|
57
|
+
|
58
|
+
def token
|
59
|
+
options.token || ENV.fetch('SHAMAN_TOKEN', nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
def minimum_version
|
63
|
+
options.minimum_version || false
|
64
|
+
end
|
21
65
|
|
22
|
-
def
|
23
|
-
|
24
|
-
@deploy_options ||= {
|
25
|
-
environment_token: config[:token],
|
26
|
-
release: HTTP::FormData::File.new(options.file || config[:release_path]),
|
27
|
-
message: message || '',
|
28
|
-
token: options.token || ENV['SHAMAN_TOKEN'],
|
29
|
-
minimum_version: options.minimum_version || false,
|
30
|
-
name: options.release_name || ''
|
31
|
-
}
|
66
|
+
def name
|
67
|
+
options.release_name || DEFAULT_RELEASE_NAME
|
32
68
|
end
|
33
69
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
70
|
+
def verify_release!
|
71
|
+
release.form.each do |attribute, value|
|
72
|
+
error!("Release attribute not specified: #{attribute}") if value.nil?
|
37
73
|
end
|
38
74
|
end
|
39
75
|
|
40
76
|
def config
|
41
|
-
@config ||=
|
42
|
-
YAML.load_file(options.config || PROJECT_CONFIG_PATH)[environment]
|
77
|
+
@config ||= load_config
|
43
78
|
end
|
44
79
|
|
45
80
|
def gcommit
|
46
|
-
@gcommit ||= Git.open(
|
81
|
+
@gcommit ||= Git.open(GIT_DEFAULT_DIRECTORY).gcommit(options.commit || GIT_HEAD)
|
47
82
|
end
|
48
83
|
|
49
84
|
def message
|
50
|
-
options.git
|
85
|
+
(options.git && gcommit.message) ||
|
86
|
+
options.message ||
|
87
|
+
ask_editor(nil, ENV.fetch('EDITOR', DEFAULT_EDITOR)) ||
|
88
|
+
DEFAULT_MESSAGE
|
89
|
+
end
|
90
|
+
|
91
|
+
def load_config
|
92
|
+
YAML.load_file(config_file).fetch(environment)
|
93
|
+
rescue Errno::ENOENT => _e
|
94
|
+
error!("Config file #{config_file} doesn't exist")
|
95
|
+
rescue KeyError => _e
|
96
|
+
error!("Envrionment #{environment} doesn't exist in #{config_file}")
|
97
|
+
end
|
98
|
+
|
99
|
+
def config_file
|
100
|
+
@config_file ||= options.config || PROJECT_CONFIG_PATH
|
51
101
|
end
|
52
102
|
end
|
53
103
|
end
|
data/lib/shaman/helpers.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Shaman
|
2
4
|
module Helpers
|
3
5
|
def prompt
|
4
|
-
|
6
|
+
Shaman.prompt
|
5
7
|
end
|
6
8
|
|
7
|
-
def error!(msg)
|
9
|
+
def error!(msg, exit_code = Shaman::CLI::ExitCode::PROCESSING_ERROR)
|
8
10
|
prompt.error(msg)
|
9
|
-
exit(
|
11
|
+
exit(exit_code)
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
data/lib/shaman/init.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
1
5
|
module Shaman
|
2
6
|
class Init
|
7
|
+
PLATFORMS = [:android, :ios, :zip, :air, :web].freeze
|
8
|
+
|
3
9
|
include Shaman::Helpers
|
4
10
|
extend Shaman::Helpers
|
5
11
|
|
6
|
-
def self.check
|
12
|
+
def self.check!
|
7
13
|
return unless ENV['SHAMAN_TOKEN'].nil?
|
8
|
-
|
14
|
+
|
15
|
+
error!("SHAMAN_TOKEN isn't defined. Find it at #{Shaman.tryout_apps_base_uri}/users/me")
|
9
16
|
end
|
10
17
|
|
11
18
|
def self.init(options)
|
@@ -17,9 +24,9 @@ module Shaman
|
|
17
24
|
end
|
18
25
|
|
19
26
|
def call
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
error!("Invalid project id: #{project_id}") if selected_project.nil?
|
28
|
+
|
29
|
+
File.write(PROJECT_CONFIG_PATH, YAML.dump(config))
|
23
30
|
prompt.ok '.shaman.yml created'
|
24
31
|
prompt.say(File.read(PROJECT_CONFIG_PATH), color: :cyan)
|
25
32
|
end
|
@@ -28,59 +35,56 @@ module Shaman
|
|
28
35
|
|
29
36
|
attr_reader :options
|
30
37
|
|
31
|
-
def
|
32
|
-
|
38
|
+
def selected_project
|
39
|
+
@selected_project = projects.find { |project| project.id == project_id }
|
33
40
|
end
|
34
41
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
projects.
|
39
|
-
|
40
|
-
end
|
42
|
+
def projects
|
43
|
+
@projects ||= begin
|
44
|
+
response = fetch_projects
|
45
|
+
error!("Failed to load projects: #{response.data}") unless response.success?
|
46
|
+
response.data
|
41
47
|
end
|
42
48
|
end
|
43
49
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
release_path = prompt.ask("Release path for #{environment['name']}", default: 'path/to/release')
|
51
|
-
config[environment['name']] = {
|
52
|
-
release_path: release_path,
|
53
|
-
token: environment['token']
|
54
|
-
}
|
55
|
-
end
|
56
|
-
File.open(PROJECT_CONFIG_PATH, 'w') { |f| f.write(YAML.dump(config)) }
|
50
|
+
def fetch_projects
|
51
|
+
params = { token: ENV.fetch('SHAMAN_TOKEN', nil), platform: platform }
|
52
|
+
params[:favorites] = options.favorites if options.favorites
|
53
|
+
params[:search] = options.search if options.search
|
54
|
+
|
55
|
+
Shaman.tryout_apps_client.list_projects(params)
|
57
56
|
end
|
58
57
|
|
59
|
-
def
|
60
|
-
|
61
|
-
response = HTTP.get(
|
62
|
-
"#{LABS_URL}/api/v1/projects",
|
63
|
-
params: {
|
64
|
-
token: ENV['SHAMAN_TOKEN'], favorites: favorites, search: search, platform: platform
|
65
|
-
}
|
66
|
-
)
|
67
|
-
@projects = JSON.parse(response.body)
|
58
|
+
def project_id
|
59
|
+
@project_id ||= options.project_id&.to_i || choose_project_id
|
68
60
|
end
|
69
61
|
|
70
|
-
def
|
71
|
-
|
62
|
+
def choose_project_id
|
63
|
+
prompt.enum_select('Choose project:') do |menu|
|
64
|
+
menu.enum '.'
|
65
|
+
projects.each do |project|
|
66
|
+
menu.choice "#{project.name} (#{project.id})", project.id
|
67
|
+
end
|
68
|
+
end
|
72
69
|
end
|
73
70
|
|
74
|
-
def
|
75
|
-
|
71
|
+
def choose_platform
|
72
|
+
prompt.select('Choose platform:', PLATFORMS)
|
76
73
|
end
|
77
74
|
|
78
|
-
def
|
79
|
-
|
75
|
+
def config
|
76
|
+
selected_project
|
77
|
+
.environments
|
78
|
+
.select { |environment| environment.platform == platform }
|
79
|
+
.reduce({}) do |memo, environment|
|
80
|
+
release_path = prompt.ask("Release path for #{environment.name}", default: 'path/to/release')
|
81
|
+
|
82
|
+
memo.merge(environment.name => { release_path: release_path, token: environment.token })
|
83
|
+
end
|
80
84
|
end
|
81
85
|
|
82
86
|
def platform
|
83
|
-
@platform
|
87
|
+
@platform ||= options.platform || choose_platform.to_s
|
84
88
|
end
|
85
89
|
end
|
86
90
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaman
|
4
|
+
module TryoutApps
|
5
|
+
class Client
|
6
|
+
def initialize(base_uri)
|
7
|
+
@base_uri = base_uri
|
8
|
+
end
|
9
|
+
|
10
|
+
def list_projects(params)
|
11
|
+
Shaman::TryoutApps::Resource::Project::ListResponse.new(
|
12
|
+
HTTP.get("#{base_uri}/api/v1/projects", params: params)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_release(release)
|
17
|
+
Shaman::TryoutApps::Resource::Release::CreateResponse.new(
|
18
|
+
HTTP.post("#{base_uri}/api/v1/releases", form: release.form)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :base_uri
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Shaman
|
6
|
+
module TryoutApps
|
7
|
+
module Resource
|
8
|
+
module Project
|
9
|
+
class ListResponse < Shaman::TryoutApps::Resource::Response
|
10
|
+
def data
|
11
|
+
return parse_projects if response.status.success?
|
12
|
+
|
13
|
+
error_message
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :response
|
19
|
+
|
20
|
+
def parse_projects
|
21
|
+
JSON.parse(response.body).map do |project|
|
22
|
+
Shaman::TryoutApps::Resource::Project::Project.new(project)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def error_message
|
27
|
+
response.body.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Project
|
32
|
+
def initialize(data)
|
33
|
+
@data = data
|
34
|
+
end
|
35
|
+
|
36
|
+
def name
|
37
|
+
data['name']
|
38
|
+
end
|
39
|
+
|
40
|
+
def id
|
41
|
+
data['id']
|
42
|
+
end
|
43
|
+
|
44
|
+
def environments
|
45
|
+
@environments ||= data['environments'].map do |environment|
|
46
|
+
Shaman::TryoutApps::Resource::Project::Environment.new(environment)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :data
|
53
|
+
end
|
54
|
+
|
55
|
+
class Environment
|
56
|
+
def initialize(data)
|
57
|
+
@data = data
|
58
|
+
end
|
59
|
+
|
60
|
+
def name
|
61
|
+
data['name']
|
62
|
+
end
|
63
|
+
|
64
|
+
def platform
|
65
|
+
data['platform']
|
66
|
+
end
|
67
|
+
|
68
|
+
def token
|
69
|
+
data['token']
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
attr_reader :data
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaman
|
4
|
+
module TryoutApps
|
5
|
+
module Resource
|
6
|
+
module Release
|
7
|
+
class CreateInput
|
8
|
+
def initialize(options)
|
9
|
+
@file = options.fetch(:file)
|
10
|
+
@environment_token = options.fetch(:environment_token)
|
11
|
+
@message = options.fetch(:message)
|
12
|
+
@token = options.fetch(:token)
|
13
|
+
@minimum_version = options.fetch(:minimum_version)
|
14
|
+
@name = options.fetch(:name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def form
|
18
|
+
{
|
19
|
+
release: HTTP::FormData::File.new(file),
|
20
|
+
environment_token: environment_token,
|
21
|
+
message: message,
|
22
|
+
token: token,
|
23
|
+
minimum_version: minimum_version,
|
24
|
+
name: name
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :file
|
31
|
+
attr_reader :environment_token
|
32
|
+
attr_reader :message
|
33
|
+
attr_reader :token
|
34
|
+
attr_reader :minimum_version
|
35
|
+
attr_reader :name
|
36
|
+
end
|
37
|
+
|
38
|
+
class CreateResponse < Shaman::TryoutApps::Resource::Response
|
39
|
+
def data
|
40
|
+
response.body.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaman
|
4
|
+
module TryoutApps
|
5
|
+
module Resource
|
6
|
+
class Response
|
7
|
+
def initialize(response)
|
8
|
+
@response = response
|
9
|
+
end
|
10
|
+
|
11
|
+
def success?
|
12
|
+
response.status.success?
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :response
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/shaman/version.rb
CHANGED
data/lib/shaman.rb
CHANGED
@@ -1,20 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'http'
|
2
4
|
require 'git'
|
3
|
-
require 'yaml'
|
4
5
|
require 'tty-prompt'
|
5
6
|
require 'shaman/helpers'
|
7
|
+
require 'shaman/cli'
|
6
8
|
require 'shaman/init'
|
7
9
|
require 'shaman/deploy'
|
10
|
+
require 'shaman/tryout_apps'
|
11
|
+
require 'shaman/tryout_apps/client'
|
12
|
+
require 'shaman/tryout_apps/resource'
|
13
|
+
require 'shaman/tryout_apps/resource/project'
|
14
|
+
require 'shaman/tryout_apps/resource/release'
|
8
15
|
require 'shaman/version'
|
9
16
|
|
10
17
|
module Shaman
|
11
|
-
PROJECT_CONFIG_PATH = '.shaman.yml'
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
PROJECT_CONFIG_PATH = '.shaman.yml'
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def tryout_apps_client
|
22
|
+
@tryout_apps_client ||= Shaman::TryoutApps::Client.new(tryout_apps_base_uri)
|
23
|
+
end
|
24
|
+
|
25
|
+
def tryout_apps_base_uri
|
26
|
+
ENV.fetch('SERVER_URL', tryout_apps_env_base_uri)
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_writer :prompt
|
30
|
+
|
31
|
+
def prompt
|
32
|
+
@prompt ||= TTY::Prompt.new
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def tryout_apps_env_base_uri
|
38
|
+
case ENV.fetch('SHAMAN_ENV', nil)
|
39
|
+
when 'development' then Shaman::TryoutApps::DEVELOPMENT_BASE_URI
|
40
|
+
when 'staging' then Shaman::TryoutApps::STAGING_BASE_URI
|
41
|
+
else Shaman::TryoutApps::PRODUCTION_BASE_URI end
|
18
42
|
end
|
19
|
-
|
43
|
+
end
|
20
44
|
end
|
data/shaman.gemspec
CHANGED
@@ -1,29 +1,29 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require 'shaman/version'
|
4
6
|
|
5
7
|
Gem::Specification.new do |spec|
|
6
8
|
spec.name = 'shaman_cli'
|
7
9
|
spec.version = Shaman::VERSION
|
8
|
-
spec.authors = ['
|
9
|
-
spec.email = ['
|
10
|
+
spec.authors = ['Rails team']
|
11
|
+
spec.email = ['team.rails@infinum.com']
|
10
12
|
|
11
|
-
spec.summary = '
|
12
|
-
spec.description = '
|
13
|
-
spec.homepage = '
|
13
|
+
spec.summary = 'Shaman CLI'
|
14
|
+
spec.description = 'CLI tool for deploying builds to Tryoutapps service'
|
15
|
+
spec.homepage = 'https://github.com/infinum/shaman'
|
14
16
|
spec.license = 'MIT'
|
15
17
|
|
18
|
+
spec.required_ruby_version = '>= 3.0'
|
16
19
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
20
|
spec.bindir = 'exe'
|
18
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
22
|
spec.require_paths = ['lib']
|
20
23
|
|
21
|
-
spec.
|
22
|
-
spec.
|
23
|
-
spec.
|
24
|
-
spec.add_development_dependency 'pry-byebug'
|
25
|
-
spec.add_dependency 'http'
|
26
|
-
spec.add_dependency 'git'
|
27
|
-
spec.add_dependency 'commander'
|
24
|
+
spec.add_dependency 'commander', '~> 5.0'
|
25
|
+
spec.add_dependency 'git', '~> 1.19'
|
26
|
+
spec.add_dependency 'http', '~> 5.2'
|
28
27
|
spec.add_dependency 'tty-prompt', '0.12.0'
|
28
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
29
29
|
end
|
data/tmp/.keep
ADDED
File without changes
|
metadata
CHANGED
@@ -1,113 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shaman_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- Rails team
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: commander
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: git
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
type: :
|
33
|
+
version: '1.19'
|
34
|
+
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry-byebug
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
40
|
+
version: '1.19'
|
69
41
|
- !ruby/object:Gem::Dependency
|
70
42
|
name: http
|
71
43
|
requirement: !ruby/object:Gem::Requirement
|
72
44
|
requirements:
|
73
|
-
- - "
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: git
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: commander
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
45
|
+
- - "~>"
|
102
46
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
47
|
+
version: '5.2'
|
104
48
|
type: :runtime
|
105
49
|
prerelease: false
|
106
50
|
version_requirements: !ruby/object:Gem::Requirement
|
107
51
|
requirements:
|
108
|
-
- - "
|
52
|
+
- - "~>"
|
109
53
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
54
|
+
version: '5.2'
|
111
55
|
- !ruby/object:Gem::Dependency
|
112
56
|
name: tty-prompt
|
113
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,36 +66,52 @@ dependencies:
|
|
122
66
|
- - '='
|
123
67
|
- !ruby/object:Gem::Version
|
124
68
|
version: 0.12.0
|
125
|
-
description:
|
69
|
+
description: CLI tool for deploying builds to Tryoutapps service
|
126
70
|
email:
|
127
|
-
-
|
71
|
+
- team.rails@infinum.com
|
128
72
|
executables:
|
129
73
|
- shaman
|
130
74
|
extensions: []
|
131
75
|
extra_rdoc_files: []
|
132
76
|
files:
|
77
|
+
- ".dockerignore"
|
78
|
+
- ".github/CODEOWNERS"
|
79
|
+
- ".github/pull_request_template.md"
|
80
|
+
- ".github/workflows/build-app-image-and-run-checks.yml"
|
133
81
|
- ".gitignore"
|
134
82
|
- ".rspec"
|
135
|
-
- ".
|
83
|
+
- ".rubocop.yml"
|
84
|
+
- ".ruby-version"
|
85
|
+
- CHANGELOG.md
|
136
86
|
- CODE_OF_CONDUCT.md
|
87
|
+
- Dockerfile
|
137
88
|
- Gemfile
|
138
89
|
- LICENSE.txt
|
139
90
|
- README.md
|
140
91
|
- Rakefile
|
141
92
|
- bin/console
|
142
93
|
- bin/setup
|
94
|
+
- docker-compose.yml
|
143
95
|
- exe/shaman
|
144
96
|
- lib/shaman.rb
|
97
|
+
- lib/shaman/cli.rb
|
145
98
|
- lib/shaman/deploy.rb
|
146
99
|
- lib/shaman/helpers.rb
|
147
100
|
- lib/shaman/init.rb
|
101
|
+
- lib/shaman/tryout_apps.rb
|
102
|
+
- lib/shaman/tryout_apps/client.rb
|
103
|
+
- lib/shaman/tryout_apps/resource.rb
|
104
|
+
- lib/shaman/tryout_apps/resource/project.rb
|
105
|
+
- lib/shaman/tryout_apps/resource/release.rb
|
148
106
|
- lib/shaman/version.rb
|
149
107
|
- shaman.gemspec
|
150
|
-
|
108
|
+
- tmp/.keep
|
109
|
+
homepage: https://github.com/infinum/shaman
|
151
110
|
licenses:
|
152
111
|
- MIT
|
153
|
-
metadata:
|
154
|
-
|
112
|
+
metadata:
|
113
|
+
rubygems_mfa_required: 'true'
|
114
|
+
post_install_message:
|
155
115
|
rdoc_options: []
|
156
116
|
require_paths:
|
157
117
|
- lib
|
@@ -159,16 +119,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
159
119
|
requirements:
|
160
120
|
- - ">="
|
161
121
|
- !ruby/object:Gem::Version
|
162
|
-
version: '0'
|
122
|
+
version: '3.0'
|
163
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
124
|
requirements:
|
165
125
|
- - ">="
|
166
126
|
- !ruby/object:Gem::Version
|
167
127
|
version: '0'
|
168
128
|
requirements: []
|
169
|
-
|
170
|
-
|
171
|
-
signing_key:
|
129
|
+
rubygems_version: 3.2.33
|
130
|
+
signing_key:
|
172
131
|
specification_version: 4
|
173
|
-
summary:
|
132
|
+
summary: Shaman CLI
|
174
133
|
test_files: []
|
data/.travis.yml
DELETED