potassium 5.2.2 → 6.3.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.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +103 -28
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.gitignore +2 -1
  5. data/.node-version +1 -0
  6. data/.rubocop.yml +530 -0
  7. data/.ruby-version +1 -1
  8. data/CHANGELOG.md +92 -1
  9. data/README.md +51 -45
  10. data/docs/DSL.md +5 -5
  11. data/lib/potassium/assets/.circleci/config.yml.erb +151 -0
  12. data/lib/potassium/assets/.eslintrc.json +352 -0
  13. data/lib/potassium/assets/.github/pull_request_template.md +9 -0
  14. data/lib/potassium/assets/.rubocop.yml +528 -0
  15. data/lib/potassium/assets/.stylelintrc.json +46 -0
  16. data/lib/potassium/assets/Makefile.erb +21 -32
  17. data/lib/potassium/assets/README.yml +59 -15
  18. data/lib/potassium/assets/active_admin/admin-component.vue +35 -0
  19. data/lib/potassium/assets/active_admin/admin_application.js +14 -0
  20. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +10 -0
  21. data/lib/potassium/assets/app/graphql/graphql_controller.rb +55 -0
  22. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +23 -0
  23. data/lib/potassium/assets/app/graphql/queries/base_query.rb +4 -0
  24. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +4 -0
  25. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +4 -0
  26. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +5 -0
  27. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +5 -0
  28. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +7 -0
  29. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +5 -0
  30. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +4 -0
  31. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +4 -0
  32. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +10 -0
  33. data/lib/potassium/assets/app/graphql/types/query_type.rb +13 -0
  34. data/lib/potassium/assets/app/javascript/app.spec.js +14 -0
  35. data/lib/potassium/assets/app/uploaders/base_uploader.rb +11 -0
  36. data/lib/potassium/assets/app/uploaders/image_uploader.rb +5 -0
  37. data/lib/potassium/assets/app/views/shared/_gtm_body.html.erb +4 -0
  38. data/lib/potassium/assets/app/views/shared/_gtm_head.html.erb +7 -0
  39. data/lib/potassium/assets/bin/release +1 -1
  40. data/lib/potassium/assets/bin/setup.erb +1 -1
  41. data/lib/potassium/assets/config/database_mysql.yml.erb +2 -2
  42. data/lib/potassium/assets/config/database_postgresql.yml.erb +2 -2
  43. data/lib/potassium/assets/config/graphql_playground.rb +20 -0
  44. data/lib/potassium/assets/config/puma.rb +1 -1
  45. data/lib/potassium/assets/config/shrine.rb +36 -0
  46. data/lib/potassium/assets/lib/tasks/auto_annotate_models.rake +2 -1
  47. data/lib/potassium/assets/package.json +4 -1
  48. data/lib/potassium/assets/redis.yml +1 -2
  49. data/lib/potassium/assets/testing/rails_helper.rb +2 -0
  50. data/lib/potassium/cli/commands/create.rb +12 -19
  51. data/lib/potassium/cli_options.rb +77 -26
  52. data/lib/potassium/helpers/gem-helpers.rb +1 -1
  53. data/lib/potassium/helpers/template-helpers.rb +8 -0
  54. data/lib/potassium/newest_version_ensurer.rb +19 -36
  55. data/lib/potassium/node_version_ensurer.rb +30 -0
  56. data/lib/potassium/recipes/admin.rb +3 -3
  57. data/lib/potassium/recipes/annotate.rb +1 -1
  58. data/lib/potassium/recipes/api.rb +93 -21
  59. data/lib/potassium/recipes/background_processor.rb +66 -19
  60. data/lib/potassium/recipes/ci.rb +10 -38
  61. data/lib/potassium/recipes/data_migrate.rb +44 -0
  62. data/lib/potassium/recipes/database.rb +4 -0
  63. data/lib/potassium/recipes/database_container.rb +7 -5
  64. data/lib/potassium/recipes/draper.rb +1 -10
  65. data/lib/potassium/recipes/file_storage.rb +66 -0
  66. data/lib/potassium/recipes/front_end.rb +225 -9
  67. data/lib/potassium/recipes/github.rb +93 -15
  68. data/lib/potassium/recipes/google_tag_manager.rb +90 -0
  69. data/lib/potassium/recipes/heroku.rb +42 -29
  70. data/lib/potassium/recipes/mailer.rb +18 -2
  71. data/lib/potassium/recipes/monitoring.rb +5 -0
  72. data/lib/potassium/recipes/node.rb +21 -0
  73. data/lib/potassium/recipes/rack_cors.rb +18 -15
  74. data/lib/potassium/recipes/schedule.rb +17 -2
  75. data/lib/potassium/recipes/style.rb +21 -3
  76. data/lib/potassium/recipes/vue_admin.rb +124 -0
  77. data/lib/potassium/templates/application.rb +10 -7
  78. data/lib/potassium/version.rb +7 -4
  79. data/potassium.gemspec +11 -6
  80. data/spec/features/api_spec.rb +25 -0
  81. data/spec/features/background_processor_spec.rb +19 -6
  82. data/spec/features/ci_spec.rb +7 -4
  83. data/spec/features/data_migrate_spec.rb +14 -0
  84. data/spec/features/database_container_spec.rb +1 -5
  85. data/spec/features/draper_spec.rb +1 -6
  86. data/spec/features/file_storage_spec.rb +75 -0
  87. data/spec/features/front_end_spec.rb +102 -0
  88. data/spec/features/github_spec.rb +53 -8
  89. data/spec/features/google_tag_manager_spec.rb +59 -0
  90. data/spec/features/graphql_spec.rb +71 -0
  91. data/spec/features/heroku_spec.rb +1 -1
  92. data/spec/features/mailer_spec.rb +16 -0
  93. data/spec/features/new_project_spec.rb +6 -14
  94. data/spec/features/node_spec.rb +28 -0
  95. data/spec/features/power_types_spec.rb +5 -16
  96. data/spec/features/schedule_spec.rb +11 -4
  97. data/spec/features/vue_admin_spec.rb +47 -0
  98. data/spec/spec_helper.rb +5 -0
  99. data/spec/support/fake_octokit.rb +31 -0
  100. data/spec/support/potassium_test_helpers.rb +26 -9
  101. data/tmp/.keep +0 -0
  102. metadata +152 -46
  103. data/lib/potassium/assets/.circleci/config.yml +0 -20
  104. data/lib/potassium/assets/Dockerfile.ci +0 -6
  105. data/lib/potassium/assets/active_admin/active_admin.js.coffee +0 -4
  106. data/lib/potassium/assets/active_admin/init_activeadmin_angular.rb +0 -8
  107. data/lib/potassium/assets/api/api_error_concern.rb +0 -32
  108. data/lib/potassium/assets/api/base_controller.rb +0 -9
  109. data/lib/potassium/assets/api/draper_responder.rb +0 -62
  110. data/lib/potassium/assets/api/responder.rb +0 -41
  111. data/lib/potassium/assets/aws.rb +0 -1
  112. data/lib/potassium/assets/bin/cibuild.erb +0 -100
  113. data/lib/potassium/assets/docker-compose.ci.yml +0 -11
  114. data/lib/potassium/assets/sidekiq_scheduler.yml +0 -9
  115. data/lib/potassium/assets/testing/paperclip.rb +0 -59
  116. data/lib/potassium/recipes/active_storage.rb +0 -40
  117. data/lib/potassium/recipes/angular_admin.rb +0 -56
  118. data/lib/potassium/recipes/aws_sdk.rb +0 -7
  119. data/lib/potassium/recipes/paperclip.rb +0 -47
  120. data/spec/features/active_storage_spec.rb +0 -30
  121. data/spec/features/front_end.rb +0 -30
@@ -46,5 +46,9 @@ class Recipes::Database < Rails::AppBuilder
46
46
  else
47
47
  gather_gem db[:gem_name]
48
48
  end
49
+
50
+ after(:gem_install) do
51
+ generate 'strong_migrations:install'
52
+ end
49
53
  end
50
54
  end
@@ -24,20 +24,21 @@ class Recipes::DatabaseContainer < Rails::AppBuilder
24
24
  environment:
25
25
  MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
26
26
  volumes:
27
- - mysql_data:/var/lib/mysql
27
+ - mysql_data:/var/lib/mysql
28
28
  YAML
29
29
 
30
30
  def create
31
- copy_file '../assets/docker-compose.yml', 'docker-compose.yml'
31
+ db_type = get(:database)
32
+ return if [:None, :none].include? db_type.to_sym
32
33
 
34
+ copy_file '../assets/docker-compose.yml', 'docker-compose.yml'
33
35
  compose = DockerHelpers.new('docker-compose.yml')
34
36
 
35
- db_type = get(:database)
36
37
  compose.add_service(db_type.to_s, self.class.const_get("#{db_type}_service".upcase))
37
38
  compose.add_volume("#{db_type}_data")
38
39
  template '../assets/Makefile.erb', 'Makefile'
39
40
 
40
- run 'make services-up'
41
+ run "docker-compose up -d"
41
42
 
42
43
  set_env(db_type, CONTAINER_VARS[db_type][:port], CONTAINER_VARS[db_type][:user])
43
44
  set_dot_env(db_type, CONTAINER_VARS[db_type][:port], CONTAINER_VARS[db_type][:user])
@@ -53,11 +54,12 @@ class Recipes::DatabaseContainer < Rails::AppBuilder
53
54
  setup_text = # setup file is templated on project creation, manual install is needed
54
55
  <<~TEXT
55
56
  # Set up required services
56
- make services-up
57
+ docker-compose up -d
57
58
 
58
59
  TEXT
59
60
 
60
61
  insert_into_file 'bin/setup', setup_text, before: "# Set up database"
62
+ create
61
63
  run 'bin/setup'
62
64
  info "A new container with a #{get(:database)} database has been created."
63
65
  end
@@ -7,7 +7,6 @@ class Recipes::Draper < Rails::AppBuilder
7
7
  def create
8
8
  return unless selected?(:draper)
9
9
  add_draper
10
- add_api_responder if selected?(:api_support)
11
10
  end
12
11
 
13
12
  def installed?
@@ -16,19 +15,11 @@ class Recipes::Draper < Rails::AppBuilder
16
15
 
17
16
  def install
18
17
  add_draper
19
- api_recipe = load_recipe(:api)
20
- add_api_responder if api_recipe.installed?
21
18
  end
22
19
 
23
20
  def add_draper
24
- gather_gem 'draper', '3.0.1'
21
+ gather_gem 'draper', '~> 3.1'
25
22
  add_readme_section :internal_dependencies, :draper
26
23
  create_file 'app/decorators/.keep'
27
24
  end
28
-
29
- def add_api_responder
30
- after(:gem_install) do
31
- copy_file '../assets/api/draper_responder.rb', 'app/responders/api_responder.rb', force: true
32
- end
33
- end
34
25
  end
@@ -0,0 +1,66 @@
1
+ class Recipes::FileStorage < Rails::AppBuilder
2
+ def ask
3
+ storages = {
4
+ active_storage: 'ActiveStorage',
5
+ shrine: 'Shrine',
6
+ none: 'None, thanks'
7
+ }
8
+
9
+ storage = answer(:storage) do
10
+ storages.keys[Ask.list('Which storage are you going to use?', storages.values)]
11
+ end
12
+
13
+ set(:storage, storage.to_sym)
14
+ end
15
+
16
+ def create
17
+ add_chosen_storage
18
+ end
19
+
20
+ def install
21
+ ask
22
+ add_chosen_storage
23
+ end
24
+
25
+ def installed?
26
+ file_exist?('config/storage.yml')
27
+ end
28
+
29
+ private
30
+
31
+ def add_active_storage
32
+ after(:gem_install) { run('bundle exec rails active_storage:install') }
33
+ copy_file('../assets/config/storage.yml', 'config/storage.yml', force: true)
34
+ active_storage_service_regexp = /config.active_storage.service = :local\n/
35
+ gsub_file 'config/environments/production.rb', active_storage_service_regexp do
36
+ 'config.active_storage.service = :amazon'
37
+ end
38
+ end
39
+
40
+ def add_shrine
41
+ gather_gem('shrine', '~> 3.0')
42
+ gather_gem('marcel', '~> 1.0')
43
+ copy_file('../assets/config/shrine.rb', 'config/initializers/shrine.rb', force: true)
44
+ copy_file('../assets/app/uploaders/image_uploader.rb', 'app/uploaders/image_uploader.rb')
45
+ copy_file('../assets/app/uploaders/base_uploader.rb', 'app/uploaders/base_uploader.rb')
46
+ append_to_file('.gitignore', "/public/uploads\n")
47
+ end
48
+
49
+ def common_setup
50
+ gather_gem 'aws-sdk-s3', '~> 1.0'
51
+ add_readme_section :internal_dependencies, get(:storage)
52
+ append_to_file '.env.development', "S3_BUCKET=\n"
53
+ end
54
+
55
+ def add_chosen_storage
56
+ return if [:none, :None].include? get(:storage).to_sym
57
+
58
+ common_setup
59
+ case get(:storage)
60
+ when :active_storage
61
+ add_active_storage
62
+ when :shrine
63
+ add_shrine
64
+ end
65
+ end
66
+ end
@@ -17,22 +17,22 @@ class Recipes::FrontEnd < Rails::AppBuilder
17
17
  def create
18
18
  return if [:none, :None].include? get(:front_end).to_sym
19
19
 
20
- gather_gem 'webpacker', github: 'rails/webpacker'
21
-
20
+ recipe = self
22
21
  after(:gem_install) do
23
22
  value = get(:front_end)
24
23
  run "rails webpacker:install"
25
24
  run "rails webpacker:install:#{value}" if value
26
25
 
27
26
  if value == :vue
28
- application_js_file = "app/javascript/packs/application.js"
29
- FileUtils.move "app/javascript/packs/hello_vue.js", application_js_file
30
- gsub_file application_js_file, %r{\/\/.*\n}, ""
31
-
32
- js_pack_tag = "\n <%= javascript_pack_tag 'application' %>\n"
33
- layout_file = "app/views/layouts/application.html.erb"
34
- insert_into_file layout_file, js_pack_tag, after: "<%= csrf_meta_tags %>"
27
+ recipe.setup_vue_with_compiler_build
28
+ recipe.setup_jest
29
+ if get(:api) == :graphql
30
+ recipe.setup_apollo
31
+ end
35
32
  end
33
+ recipe.add_responsive_meta_tag
34
+ recipe.setup_tailwind
35
+ add_readme_header :webpack
36
36
  end
37
37
  end
38
38
 
@@ -48,6 +48,66 @@ class Recipes::FrontEnd < Rails::AppBuilder
48
48
  package_content.include?("\"@angular/core\"") || package_content.include?("\"vue\"")
49
49
  end
50
50
 
51
+ def setup_vue_with_compiler_build
52
+ application_js = 'app/javascript/packs/application.js'
53
+ remove_file "app/javascript/packs/hello_vue.js"
54
+ create_file application_js, application_js_content, force: true
55
+
56
+ layout_file = "app/views/layouts/application.html.erb"
57
+ insert_into_file(
58
+ layout_file,
59
+ "<div id=\"vue-app\">\n <app></app>\n ",
60
+ before: "<%= yield %>"
61
+ )
62
+ insert_into_file layout_file, "\n </div>", after: "<%= yield %>"
63
+ end
64
+
65
+ def add_responsive_meta_tag
66
+ tag = "\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
67
+ layout_file = "app/views/layouts/application.html.erb"
68
+ insert_into_file layout_file, tag, after: "<%= csrf_meta_tags %>"
69
+ end
70
+
71
+ def setup_tailwind
72
+ run "bin/yarn add tailwindcss@#{Potassium::TAILWINDCSS}"
73
+ specify_autoprefixer_postcss_compatibility_versions
74
+ setup_client_css
75
+ remove_server_css_requires
76
+ setup_tailwind_requirements
77
+ end
78
+
79
+ def setup_jest
80
+ run 'bin/yarn add jest vue-jest babel-jest @vue/test-utils jest-serializer-vue babel-core@^7.0.0-bridge.0 --dev'
81
+ json_file = File.read(Pathname.new("package.json"))
82
+ js_package = JSON.parse(json_file)
83
+ js_package = js_package.merge(jest_config)
84
+ json_string = JSON.pretty_generate(js_package)
85
+ create_file 'package.json', json_string, force: true
86
+
87
+ copy_file '../assets/app/javascript/app.spec.js', 'app/javascript/app.spec.js'
88
+ end
89
+
90
+ def setup_apollo
91
+ run 'bin/yarn add vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag'
92
+
93
+ inject_into_file(
94
+ 'app/javascript/packs/application.js',
95
+ apollo_imports,
96
+ after: "import App from '../app.vue';"
97
+ )
98
+
99
+ inject_into_file(
100
+ 'app/javascript/packs/application.js',
101
+ apollo_loading,
102
+ after: "import VueApollo from 'vue-apollo';"
103
+ )
104
+ inject_into_file(
105
+ 'app/javascript/packs/application.js',
106
+ "\n apolloProvider,",
107
+ after: "components: { App },"
108
+ )
109
+ end
110
+
51
111
  private
52
112
 
53
113
  def frameworks(framework)
@@ -58,4 +118,160 @@ class Recipes::FrontEnd < Rails::AppBuilder
58
118
  }
59
119
  frameworks[framework]
60
120
  end
121
+
122
+ def apollo_imports
123
+ <<~JS
124
+ \n
125
+ import { ApolloClient } from 'apollo-client';
126
+ import { createHttpLink } from 'apollo-link-http';
127
+ import { InMemoryCache } from 'apollo-cache-inmemory';
128
+ import VueApollo from 'vue-apollo';
129
+ JS
130
+ end
131
+
132
+ def apollo_loading
133
+ <<~JS
134
+ \n
135
+ const httpLink = createHttpLink({
136
+ uri: `${window.location.origin}/graphql`,
137
+ })
138
+ const cache = new InMemoryCache()
139
+ const apolloClient = new ApolloClient({
140
+ link: httpLink,
141
+ cache,
142
+ })
143
+
144
+ Vue.use(VueApollo)
145
+ const apolloProvider = new VueApollo({
146
+ defaultClient: apolloClient,
147
+ })
148
+ JS
149
+ end
150
+
151
+ def specify_autoprefixer_postcss_compatibility_versions
152
+ run 'bin/yarn -D add postcss@^7 autoprefixer@^9'
153
+ end
154
+
155
+ def setup_client_css
156
+ application_css = 'app/javascript/css/application.css'
157
+ create_file application_css, "", force: true
158
+
159
+ stylesheet_pack_tag = "\n <%= stylesheet_pack_tag 'application' %>\n "
160
+ layout_file = "app/views/layouts/application.html.erb"
161
+ insert_into_file layout_file, stylesheet_pack_tag, before: "</head>"
162
+
163
+ application_js = 'app/javascript/packs/application.js'
164
+ if get(:front_end) != :vue
165
+ create_file application_js, "import '../css/application.css';\n", force: true
166
+ else
167
+ insert_into_file(
168
+ application_js,
169
+ "\nimport '../css/application.css';",
170
+ after: "import App from '../app.vue';"
171
+ )
172
+ end
173
+ end
174
+
175
+ def setup_tailwind_requirements
176
+ application_css = 'app/javascript/css/application.css'
177
+ insert_into_file application_css, tailwind_client_css
178
+
179
+ tailwind_config = 'tailwind.config.js'
180
+ create_file tailwind_config, tailwind_config_content, force: true
181
+
182
+ postcss_file = 'postcss.config.js'
183
+ insert_into_file postcss_file, postcss_require_tailwind, after: "plugins: [\n"
184
+ end
185
+
186
+ def remove_server_css_requires
187
+ assets_css_file = 'app/assets/stylesheets/application.css'
188
+ gsub_file(assets_css_file, " *= require_tree .\n *= require_self\n", "")
189
+ end
190
+
191
+ def application_js_content
192
+ <<~JS
193
+ import Vue from 'vue/dist/vue.esm';
194
+ import App from '../app.vue';
195
+
196
+ document.addEventListener('DOMContentLoaded', () => {
197
+ const app = new Vue({
198
+ el: '#vue-app',
199
+ components: { App },
200
+ });
201
+
202
+ return app;
203
+ });
204
+ JS
205
+ end
206
+
207
+ def tailwind_client_css
208
+ <<~CSS
209
+ @import 'tailwindcss/base';
210
+ @import 'tailwindcss/components';
211
+ @import 'tailwindcss/utilities';
212
+ CSS
213
+ end
214
+
215
+ def tailwind_config_content
216
+ <<~JS
217
+ /* eslint-disable no-undef */
218
+ module.exports = {
219
+ theme: {
220
+ extend: {},
221
+ },
222
+ variants: {},
223
+ plugins: [],
224
+ purge: {
225
+ enabled: process.env.NODE_ENV === 'production',
226
+ content: [
227
+ './app/**/*.html',
228
+ './app/**/*.vue',
229
+ './app/**/*.js',
230
+ './app/**/*.erb',
231
+ ],
232
+ }
233
+ };
234
+ JS
235
+ end
236
+
237
+ def postcss_require_tailwind
238
+ <<-JS.gsub(/^ {4}/, ' ')
239
+ require('tailwindcss'),
240
+ require('autoprefixer'),
241
+ JS
242
+ end
243
+
244
+ def jest_config
245
+ {
246
+ "scripts": {
247
+ "test": "jest",
248
+ "test:watch": "jest --watch"
249
+ },
250
+ "jest": {
251
+ "roots": [
252
+ "app/javascript"
253
+ ],
254
+ "moduleDirectories": [
255
+ "node_modules",
256
+ "app/javascript"
257
+ ],
258
+ "moduleNameMapper": {
259
+ "^@/(.*)$": "app/javascript/$1"
260
+ },
261
+ "moduleFileExtensions": [
262
+ "js",
263
+ "json",
264
+ "vue"
265
+ ],
266
+ "transform": {
267
+ "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
268
+ ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
269
+ },
270
+ "snapshotSerializers": [
271
+ "<rootDir>/node_modules/jest-serializer-vue"
272
+ ],
273
+ "testEnvironment": "jsdom"
274
+ }
275
+ }
276
+ end
61
277
  end
@@ -1,29 +1,107 @@
1
+ require 'octokit'
2
+
1
3
  class Recipes::Github < Rails::AppBuilder
2
4
  def ask
3
- repo_name = "platanus/#{get(:dasherized_app_name)}"
4
5
  github_repo_create = answer(:github) do
5
- q = "Do you want to create the Github repository (https://github.com/#{repo_name}) " +
6
- "for this project?"
7
- Ask.confirm(q)
8
- end
9
- if github_repo_create
10
- github_repo_private = answer(:"github-private") do
11
- Ask.confirm("Should the repository be private?")
12
- end
6
+ Ask.confirm('Do you want to create a Github repository?')
13
7
  end
14
- set(:github_repo_name, repo_name)
15
8
  set(:github_repo, github_repo_create)
16
- set(:github_repo_private, github_repo_private)
9
+ setup_repo if github_repo_create
10
+ end
11
+
12
+ def setup_repo
13
+ setup_repo_private
14
+ setup_repo_org
15
+ setup_repo_name
16
+ set(:github_access_token, get_access_token)
17
17
  end
18
18
 
19
19
  def create
20
- github_repo_create(get(:github_repo_name), get(:github_repo_private)) if selected?(:github_repo)
20
+ return unless selected?(:github_repo)
21
+
22
+ create_github_repo
23
+ copy_file '../assets/.github/pull_request_template.md', '.github/pull_request_template.md'
21
24
  end
22
25
 
23
26
  private
24
27
 
25
- def github_repo_create(repo_name, private_repo = false)
26
- flag = private_repo ? "-p" : ""
27
- run "hub create #{flag} #{repo_name}"
28
+ def setup_repo_private
29
+ repo_private = answer(:github_private) do
30
+ Ask.confirm('Should the repository be private?')
31
+ end
32
+ set(:github_repo_private, repo_private)
33
+ end
34
+
35
+ def setup_repo_org
36
+ has_organization = answer(:github_has_org) do
37
+ Ask.confirm('Is this repo for a Github organization?')
38
+ end
39
+ set(:github_has_org, has_organization)
40
+ if has_organization
41
+ repo_organization = answer(:github_org) do
42
+ Ask.input('What is the organization for this repository?', default: 'platanus')
43
+ end
44
+ set(:github_org, repo_organization)
45
+ end
46
+ end
47
+
48
+ def setup_repo_name
49
+ repo_name = answer(:github_name) do
50
+ Ask.input('What is the name for this repository?', default: get(:dasherized_app_name))
51
+ end
52
+ set(:github_repo_name, repo_name)
53
+ end
54
+
55
+ def create_github_repo
56
+ options = { private: get(:github_repo_private) }
57
+ options[:organization] = get(:github_org) if get(:github_has_org)
58
+ repo_name = get(:github_repo_name)
59
+
60
+ is_retry = false
61
+ begin
62
+ github_client(is_retry).create_repository(repo_name, options)
63
+ rescue Octokit::Unauthorized
64
+ is_retry = true
65
+ retry if retry_create_repo
66
+ end
67
+ end
68
+
69
+ def retry_create_repo
70
+ puts "Bad credentials, information on Personal Access Tokens here:"
71
+ puts "https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token"
72
+ puts "Make sure to give repo access to the personal access token"
73
+ Ask.confirm("Do you want to retry?")
74
+ end
75
+
76
+ def github_client(is_retry = false)
77
+ access_token = is_retry ? set_access_token : get(:github_access_token)
78
+ octokit_client.new(access_token: access_token)
79
+ end
80
+
81
+ def octokit_client
82
+ if answer(:test)
83
+ require_relative '../../../spec/support/fake_octokit'
84
+ FakeOctokit
85
+ else
86
+ Octokit::Client
87
+ end
88
+ end
89
+
90
+ def get_access_token
91
+ return File.open(config_filename, 'r').read if File.exists?(config_filename)
92
+
93
+ set_access_token
94
+ end
95
+
96
+ def set_access_token
97
+ access_token = answer(:github_access_token) do
98
+ Ask.input('Enter a GitHub personal access token', password: true)
99
+ end
100
+ File.open(config_filename, 'w') { |f| f.write(access_token) }
101
+ access_token
102
+ end
103
+
104
+ def config_filename
105
+ @config_filename ||= File.expand_path('~/.potassium')
28
106
  end
29
107
  end