checklister 0.1.1 → 0.9.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.
data/.travis.yml CHANGED
@@ -1,6 +1,8 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
+ - 2.0.0
4
5
  - 2.1.4
6
+ - 2.2.2
5
7
  before_install: gem install bundler -v 1.10.5
6
8
  script: 'bundle exec rake'
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/CHANGELOG.md CHANGED
@@ -3,12 +3,10 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  NOTE: the project follows [Semantic Versioning](http://semver.org/).
5
5
 
6
- ## 1.0.0 - YYYY-MM-DD
7
-
8
- ### Dependencies
9
-
10
- - [Gli](https://github.com/davetron5000/gli)
6
+ ## 0.0.9 - 2015-09-22
11
7
 
12
8
  ### Added
13
9
 
14
- - #4 - Add a CHANGELOG
10
+ - [feature] Create a new Issue based on a specific markdown file (local path)
11
+ - [feature] Connect to gitlab account
12
+ - [feature] Save your gitlab services settings to local file
data/README.md CHANGED
@@ -5,10 +5,12 @@
5
5
  [![Build Status](https://travis-ci.org/benichu/checklister.svg)](https://travis-ci.org/benichu/checklister)
6
6
  [![Dependency Status](https://gemnasium.com/benichu/checklister.svg)](https://gemnasium.com/benichu/checklister)
7
7
  [![Gem Version](https://badge.fury.io/rb/checklister.svg)](http://badge.fury.io/rb/checklister)
8
+ [![Code Climate](https://codeclimate.com/github/benichu/checklister/badges/gpa.svg)](https://codeclimate.com/github/benichu/checklister)
9
+ [![Coverage Status](https://coveralls.io/repos/benichu/checklister/badge.svg?branch=master&service=github)](https://coveralls.io/github/benichu/checklister?branch=master)
8
10
 
9
11
  ## Description
10
12
 
11
- Checklister is a CLI packaged as a Ruby gem giving you the power to transform any markdown file or url checklist into an actionable gitlab (and soon github) issue.
13
+ Checklister is a CLI packaged as a Ruby gem giving you the power to transform any markdown file or url checklist into an actionable gitlab or github issue.
12
14
 
13
15
  ### Why using checklists
14
16
 
@@ -33,33 +35,50 @@ Source: http://atulgawande.com/book/the-checklist-manifesto/
33
35
 
34
36
  ### Install/Update gem
35
37
 
36
- TO RELEASE
38
+ ```bash
39
+ $ gem install checklister
40
+ ```
37
41
 
38
42
  ### Setup Gitlab Authentication
39
43
 
40
44
  ```bash
41
- $ checklister setup gitlab
45
+ $ checklister setup
42
46
  ```
43
47
 
44
- ### Setup Github Authentication
48
+ Answer the questions and the information will be saved for later use in a configuration json file.
49
+
50
+ ### Check your saved configuration
45
51
 
46
52
  ```bash
47
- $ checklister setup github
53
+ $ checklister settings
48
54
  ```
49
55
 
50
56
  ### CLI
51
57
 
52
58
  ```bash
53
- $ checklister --file https://raw.githubusercontent.com/benichu/checklister/master/examples/simple-checklist.md \
54
- --new_issue_url https://github.com/benichu/checklister/issues
59
+ $ checklister new --checklist /path/to/simple-checklist.md
60
+ ```
61
+
62
+ or for a remote path (NOT IMPLEMENTED YET)
63
+
64
+ ```bash
65
+ $ checklister new --checklist https://raw.githubusercontent.com/benichu/checklister/master/examples/simple-checklist.md
55
66
  ```
56
67
 
68
+ In this example, the initial markdown file at `https://raw.githubusercontent.com/benichu/checklister/master/examples/simple-checklist.md` is the following :
69
+
70
+ ![Simple Checklist](http://i.imgur.com/KUXThqu.png)
71
+
72
+ Once the file is parsed, a Github issue will be created with its content. In this case, the issue would be :
73
+
74
+ ![Github Issue](http://i.imgur.com/1IwGKaS.png)
75
+
57
76
  ## Development Setup
58
77
 
59
78
  ### Dependencies
60
79
 
61
80
  * rbenv or rvm
62
- * Ruby 1.9+
81
+ * Ruby 2.0+
63
82
  * RubyGems 1.9+ (`gem update --system `)
64
83
  * Bundler 1.10+
65
84
 
@@ -74,18 +93,55 @@ To install or update your development environment, run `script/bootstrap`.
74
93
  3. Run `script/test` to ensure your development setup is sane.
75
94
  5. Read `CONTRIBUTING.md` for contribution guidelines.
76
95
  6. Run `bundle exec guard`
77
- 7. You can run a REPL: `bin/console`
96
+ 7. You can run a REPL: `script/console`
78
97
 
79
- ## Testing
98
+ ### Testing
80
99
 
81
100
  You can write tests using `rspec v3+` syntax in the `spec` folder. To run the tests, run `script/test`.
82
101
 
83
- ## Release
102
+ ### Documentation
103
+
104
+ [Documenting your code from rubygems.org](http://guides.rubygems.org/make-your-own-gem/#documenting-your-code)
105
+
106
+ This project is documented with the [YARD syntax](http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md),
107
+ every time the gem is published a documentation will automatically be generated at [rubydoc.info](http://www.rubydoc.info/gems/checklister).
84
108
 
85
- Make sure you are all setup first: http://guides.rubygems.org/publishing/#publishing-to-rubygemsorg
109
+ You should always check how your documentation looks like from time to time.
110
+ Here is a convenient way for you to run a yard server:
111
+
112
+ ```bash
113
+ $ script/doc
114
+ ```
115
+
116
+ You can now open your browser at [http://localhost:8808/](http://localhost:8808/)
117
+
118
+ ### Release
119
+
120
+ Make sure you are all set up first: http://guides.rubygems.org/publishing/#publishing-to-rubygemsorg
86
121
 
87
122
  To prepare a release, run `script/release`. This will package a new version of the `checklister` gem and release it to [https://rubygems.org/gems/checklister](https://rubygems.org/gems/checklister).
88
123
 
124
+ ## Roadmap
125
+
126
+ ### Version 0.9
127
+
128
+ - [x] [feature] Create a new Issue based on a specific markdown file (local path)
129
+ - [x] [feature] Connect to gitlab account
130
+
131
+ ### Version 1.0
132
+
133
+ - [ ] [improvements] CLI improvements based on first test run with real users
134
+ - [ ] [documentation] Yard documentation updated
135
+ - [ ] [feature] Create a new Issue based on a specific markdown file (remote path)
136
+ - [ ] [feature] Connect to github account
137
+
138
+ ### Wishlist
139
+
140
+ - [ ] [dev] Better step by step DSL for CLI interactions with user
141
+ - [ ] [feature] Select a milestone from a list populated based on github/gitlab API
142
+ - [ ] [feature] Select a checklist from an history list
143
+ - [ ] [feature] Connect to a bitbucket account
144
+
89
145
  ## Authors
90
146
 
91
147
  Checklister is written and maintained by [Benjamin Thouret](https://github.com/benichu) and [Manon Deloupy](https://github.com/mdeloupy).
data/bin/checklister ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+ require "gli"
3
+ require "checklister"
4
+
5
+ include GLI::App
6
+
7
+ ISSUE_SERVICES = { "1" => "gitlab", "2" => "github" }.freeze
8
+
9
+ program_desc "gives you the power to transform any markdown file or url checklist into an actionable gitlab and github issue."
10
+
11
+ version Checklister::VERSION
12
+ subcommand_option_handling :normal
13
+ arguments :strict
14
+ sort_help :manually
15
+
16
+ # Flags
17
+ flag [:c,:config], default_value: File.join(ENV["HOME"],".checklister.yml"),
18
+ desc: "Set the saved configuration path"
19
+
20
+ flag [:s,:service], must_match: ISSUE_SERVICES.values
21
+ flag [:e,:endpoint], desc: "Set the issue service API endpoint"
22
+ flag [:p,:private_token], desc: "Set your private token to access the issue API service"
23
+
24
+ pre do |global_options,command,options,args|
25
+ unless command.name.to_s == "setup"
26
+ config_file = Checklister::ConfigurationFile.new(global_options["config"])
27
+ if global_options[:endpoint]
28
+ config_options = global_options
29
+ elsif config_file.exist?
30
+ saved_services = config_file.services
31
+ if saved_services.size > 1
32
+ puts "* Which Issue Service would you like to use?"
33
+ saved_services.each_with_index do |service, index|
34
+ line = []
35
+ line << "[#{index+1}] #{service[:endpoint]}"
36
+ line << "(#{service[:label]})" if service[:label]
37
+ puts line.join(" ")
38
+ end
39
+ choice = STDIN.gets.to_i
40
+ raise("You need to select a service.") if choice < 1
41
+ else
42
+ choice = 1
43
+ end
44
+ service_selected = saved_services[choice-1]
45
+ config_options = service_selected
46
+ else
47
+ config_options = {}
48
+ end
49
+ Checklister.configure(config_options)
50
+ else
51
+ true
52
+ end
53
+ end
54
+
55
+ desc "Show current configuration settings"
56
+ command :settings do |c|
57
+ c.action do |global_options,options,args|
58
+ puts "---------------------"
59
+ Checklister.config.to_stdout
60
+ puts "---------------------"
61
+ end
62
+ end
63
+
64
+ desc "Save your gitlab or github settings for later"
65
+ command :setup do |c|
66
+ c.action do |global_options,options,args|
67
+ data = {}
68
+ config_file = Checklister::ConfigurationFile.new(global_options["config"])
69
+
70
+ if global_options[:service]
71
+ service_selected = global_options[:service]
72
+ else
73
+ puts "* Which Issue Service would you like to setup?"
74
+ ISSUE_SERVICES.each do |index, service|
75
+ puts "[#{index}] #{service}"
76
+ end
77
+ choice = STDIN.gets.to_i
78
+ raise("You need to select a service.") if choice < 1
79
+ service_selected = ISSUE_SERVICES[choice.to_s]
80
+ end
81
+ data["kind"] = service_selected
82
+
83
+ puts ""
84
+ puts "* We are going to help you set up the #{service_selected} service"
85
+
86
+ if service_selected == "gitlab"
87
+ puts ""
88
+ puts "** What is your gitlab endpoint? (Ex: https://gitlab.com/api/v3)"
89
+ choice = STDIN.gets.chomp
90
+ raise("You need to type a endpoint.") if choice == ""
91
+ data["endpoint"] = choice
92
+ elsif service_selected == "github"
93
+ data["endpoint"] = "https://api.github.com"
94
+ end
95
+
96
+ puts ""
97
+ puts "** What is your private token with the #{service_selected} service?"
98
+ choice = STDIN.gets.chomp
99
+ raise("You need to type a private token.") if choice == ""
100
+ data["private_token"] = choice
101
+
102
+ puts ""
103
+ puts "** You can give a label to #{data["endpoint"]} (you can leave it blank too)"
104
+ choice = STDIN.gets.chomp
105
+ if choice != ""
106
+ data["label"] = choice
107
+ end
108
+
109
+ config_file.add_service data
110
+ config_file.persist
111
+ puts ""
112
+ puts "* Your configuration data has been saved at #{global_options[:config]}"
113
+ end
114
+ end
115
+
116
+ desc "Transform a markdown file or url checklist into an actionable issue"
117
+ command :new do |c|
118
+ c.flag [:c,:checklist], desc: "Set the markdown checklist file path", required: true
119
+ c.action do |global_options,options,args|
120
+ client = Checklister::Client.new(Checklister.config.to_hash).get_api_client
121
+ gitlab_project = Checklister::Gitlab::Project.new(client)
122
+ checklist_path = options[:checklist]
123
+
124
+ puts "* Type some letters of your project's name..."
125
+ project_like = STDIN.gets.chomp
126
+ projects = gitlab_project.filtered_by_name(project_like)
127
+ default_project_id = projects.first[:id]
128
+ projects.each do |project|
129
+ puts "%-5s %-100s" % ["[#{project[:id]}]", project[:name]]
130
+ end
131
+ puts "* Pick your project Id, defaults to [#{default_project_id}]"
132
+ choice = STDIN.gets.chomp
133
+ project_id = choice == "" ? default_project_id : choice
134
+ project = gitlab_project.get(project_id)
135
+ puts "* Creating a checklist issue from #{checklist_path}"
136
+ puts " to the project: #{project[:name]}"
137
+ parsed_checklist = Checklister::Parser.new(checklist_path)
138
+ issue = Checklister::Gitlab::Issue.new(client).create(project_id, parsed_checklist.to_params)
139
+ puts "------------->"
140
+ puts "| Check it out: #{Checklister.config.endpoint.gsub("/api/v3", "")}/#{project[:path_with_namespace]}/issues/#{issue.id}"
141
+ puts "------------->"
142
+ end
143
+ end
144
+
145
+ on_error do |exception|
146
+ # Error logic here
147
+ # return false to skip default error handling
148
+ true
149
+ end
150
+
151
+ exit run(ARGV)
data/checklister.gemspec CHANGED
@@ -15,18 +15,23 @@ Gem::Specification.new do |spec|
15
15
  spec.homepage = "https://github.com/benichu/checklister"
16
16
  spec.license = "MIT"
17
17
 
18
- spec.required_ruby_version = '>= 2.0'
18
+ spec.required_ruby_version = '>= 2.0'
19
+ spec.required_rubygems_version = '>= 1.9'
19
20
 
20
21
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
22
  spec.bindir = "bin"
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
24
25
 
25
- spec.add_dependency "gli", "~> 2.13"
26
+ spec.add_dependency "gitlab" , "~> 3.4.0"
27
+ spec.add_dependency "gli" , "~> 2.13"
26
28
 
27
29
  spec.add_development_dependency "bundler" , "~> 1.10"
30
+ spec.add_development_dependency "coveralls" , "~> 0.8.2"
28
31
  spec.add_development_dependency "guard" , "~> 2.13"
29
32
  spec.add_development_dependency "guard-rspec" , "~> 4.6"
30
33
  spec.add_development_dependency "rake" , "~> 10.4"
31
34
  spec.add_development_dependency "rspec" , "~> 3.3"
35
+ spec.add_development_dependency "yard" , "~> 0.8"
36
+ spec.add_development_dependency "webmock" , "~> 1.21"
32
37
  end
@@ -0,0 +1,9 @@
1
+ services:
2
+ - endpoint: "https://www.gitlab.com/api"
3
+ private_token: "supersecret"
4
+ label: "Gitlab"
5
+ kind: "gitlab"
6
+ - endpoint: "https://api.github.com"
7
+ private_token: "supersecret"
8
+ label: "Github"
9
+ kind: "github"
@@ -0,0 +1,5 @@
1
+ ## Checklist
2
+
3
+ - [ ] Step 1
4
+ - [ ] Step 2
5
+ - [ ] Step 3
@@ -9,3 +9,7 @@ When you need to accomplish stuff, follow those steps.
9
9
  - [ ] Step 1
10
10
  - [ ] Step 2
11
11
  - [ ] Step 3
12
+
13
+ ## Checklist
14
+
15
+ - [ ] Step 4
data/lib/checklister.rb CHANGED
@@ -1,5 +1,31 @@
1
+ require "checklister/configuration"
2
+ require "checklister/configuration_file"
3
+ require "checklister/issue"
4
+ require "checklister/parser"
5
+ require "checklister/sanitizer"
1
6
  require "checklister/version"
2
7
 
8
+ # API Clients
9
+ require "checklister/client"
10
+ require "checklister/gitlab/project"
11
+ require "checklister/gitlab/issue"
12
+
3
13
  module Checklister
4
- # Your code goes here...
14
+ class << self
15
+ # Keep track of the configuration values set after a configuration
16
+ # has been applied
17
+ #
18
+ # @example Return a configuration value
19
+ # Checklister.config.foo #=> "bar"
20
+ #
21
+ # @return [Object] the configuration object
22
+ #
23
+ def config
24
+ @config ||= Checklister::Configuration.new
25
+ end
26
+
27
+ def configure(attributes = {})
28
+ config.apply attributes
29
+ end
30
+ end
5
31
  end
@@ -0,0 +1,23 @@
1
+ require "gitlab"
2
+
3
+ module Checklister
4
+ class Client
5
+ IMPLEMENTED_BACKENDS = %w(gitlab)
6
+
7
+ def initialize(options = {})
8
+ @kind = options.fetch(:kind) { raise ArgumentError, "No API client can be initialized" }
9
+ raise(ArgumentError, "No #{@kind} API client has been implemented") unless IMPLEMENTED_BACKENDS.include?(@kind)
10
+ @options = options.reject { |k| [:kind].include? k }
11
+ default_options = { user_agent: "Checklister for #{@kind} #{Checklister::VERSION}" }
12
+ default_options.merge!(httparty: { verify: false }) # FIXME
13
+ @options.merge! default_options
14
+ end
15
+
16
+ def get_api_client
17
+ case @kind
18
+ when "gitlab"
19
+ ::Gitlab.client(@options)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,155 @@
1
+ require "uri"
2
+
3
+ module Checklister
4
+ # This class maintains all system-wide configuration for *checklister*. It properly
5
+ # applies the correct source of configuration values based on the different contexts and
6
+ # also makes sure that compulsory data are not missing.
7
+ #
8
+ # Some values are dependent on a context, for example: `host` can have different values
9
+ # based on what service the user wants to connect to.
10
+ # Other values are shared by all those contexts, for example `log_file` is defined once and
11
+ # for all and the service selected by our user does not matter.
12
+ #
13
+ # *checklister* can work with several publishing destinations, for example:
14
+ #
15
+ # - [gitlab.com](https://gitlab.com/) (Official Hosted Service)
16
+ # - privately hosted _gitlab_ server
17
+ # - [github.com](https://github.com/)
18
+ #
19
+ # ## Setting/Selecting the configuration values
20
+ #
21
+ # When using the *checklister* binary, you can set one of the many publishing destinations
22
+ # you have access to, via two possible ways:
23
+ #
24
+ # ### 1. CLI Configuration Values
25
+ #
26
+ # No configuration file required, you can pass your service credentials directly inline as options.
27
+ #
28
+ # For example, you might do:
29
+ #
30
+ # ```bash
31
+ # $ checklister --endpoint=https://api.github.com --private_token==supersecret create ...
32
+ # ```
33
+ # NOTE: Every time you pass credentials via command line options, they will override any configuration
34
+ # file you have previously set .
35
+ #
36
+ # ### 2. Configuration File
37
+ #
38
+ # You can use the command line to add publishing destinations credentials and answer the questions:
39
+ #
40
+ # ```bash
41
+ # $ checklister setup
42
+ # ```
43
+ #
44
+ # By default, that configuration file will be saved at `/path/to/home/.checklister.json`.
45
+ # But you can easily use another one by using that option before any *checklister* commands:
46
+ #
47
+ # ```bash
48
+ # $ checklister --config=/another/path/to/my_checklister.json setup
49
+ # ```
50
+ # As soon as you have set up one or many publishing destinations, every time you will be using
51
+ # a *checklister* command you will be prompted to select which service to use, for example:
52
+ #
53
+ # ```bash
54
+ # $ checklister create ...
55
+ #
56
+ # Select which service to use:
57
+ # [1] https://github.com/benichu
58
+ # [2] https://github.com/mdeloupy
59
+ # [3] https://gitlab.intello.com
60
+ # ```
61
+ #
62
+ # ## DEVELOPMENT: Using the configuration values
63
+ #
64
+ # Any time you need to access the credentials, you just need to confidently query the value without
65
+ # worrying about the context selected by the user.
66
+ #
67
+ # For example:
68
+ #
69
+ # - The user selected the `https://github.com/benichu` service, querying `Checklister.config.username` will return the appropriate value `github_user`
70
+ # - But, if the user selects next the `https://gitlab.intello.com` service, querying `Checklister.config.username` will return the appropriate value `gitlab_user`
71
+ #
72
+ class Configuration
73
+ # List of all the configuration attributes stored for use within the gem
74
+ ATTRIBUTES = [:endpoint, :private_token, :label, :kind]
75
+
76
+ # List of accessor attributes
77
+ attr_accessor :endpoint, :private_token, :kind
78
+ # List of writer attributes (with a reader defined in the class definition)
79
+ attr_writer :label
80
+
81
+ # Apply a configuration hash to a configuration instance
82
+ #
83
+ # @example Override one of the configuration attributes
84
+ # config = Checklister::Configuration.new
85
+ # config.apply(private_token: 'supersecret')
86
+ # config.private_token #=> "supersecret"
87
+ #
88
+ # @param attributes [Hash] list of key/values to apply to the configuration
89
+ # @return [Object] the configuration object
90
+ #
91
+ def apply(attributes = {})
92
+ prepared_attributes = prepare_attributes attributes
93
+ prepared_attributes.each_pair do |attribute, value|
94
+ send("#{attribute}=", value)
95
+ end
96
+ self
97
+ end
98
+
99
+ # The label value, if not specifically set, we infer it from the given
100
+ # endpoint url (we use the host)
101
+ #
102
+ # @return [String] the label string
103
+ #
104
+ def label
105
+ if instance_variable_get "@label"
106
+ @label
107
+ elsif instance_variable_get "@endpoint"
108
+ URI.parse(@endpoint).host
109
+ end
110
+ end
111
+
112
+ # The configuration instance formatted as a stringified hash
113
+ #
114
+ # @example Override one of the configuration attributes
115
+ # config = Checklister::Configuration.new
116
+ # config.to_hash #=> { "endpoint" => "https://gitlab.example.com/api/v3", ..., "private_token" => "supersecret" }
117
+ #
118
+ # @return [Hash] the configuration object as a Hash
119
+ #
120
+ def to_hash
121
+ config_hash = ATTRIBUTES.inject({}) do |hash, attr|
122
+ hash["#{attr}"] = instance_variable_get("@#{attr}")
123
+ hash
124
+ end
125
+ Checklister::Sanitizer.symbolize config_hash
126
+ end
127
+
128
+ # Write a configuration summary to STDOUT, useful for output in the CLI
129
+ #
130
+ def to_stdout
131
+ to_hash.each_pair do |attribute, value|
132
+ puts "%-20s %-50s" % ["#{attribute}:", value]
133
+ end
134
+ nil
135
+ end
136
+
137
+ private
138
+
139
+ # Symbolize keys and remove nil or duplicate attributes
140
+ # The attributes usually passed to our configuration class by the CLI
141
+ # are usually full of duplicates and unconsistant keys, we make sure
142
+ # to clean up that input, before doing any configuration work.
143
+ #
144
+ # @param attributes [Hash] list of key/values
145
+ # @return [Hash] a clean list of key/values
146
+ #
147
+ def prepare_attributes(attributes)
148
+ # Convert string keys to symbols
149
+ symboled_attributes = Checklister::Sanitizer.symbolize attributes
150
+ # Clean up user_attributes from unwanted, nil and duplicate options
151
+ symboled_attributes.select { |key, _| ATTRIBUTES.include? key }
152
+ .delete_if { |_, v| v.nil? }
153
+ end
154
+ end
155
+ end