potassium 5.2.2 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.
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