learn-tool 0.0.16 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/learn-tool +31 -10
- data/lib/learn-tool.rb +22 -210
- data/lib/learn-tool/contributing-linter.rb +23 -0
- data/lib/learn-tool/learn-base.rb +112 -0
- data/lib/learn-tool/learn-create.rb +44 -0
- data/lib/learn-tool/learn-duplicate.rb +31 -0
- data/lib/learn-tool/learn-error.rb +69 -0
- data/lib/learn-tool/learn-lint.rb +55 -0
- data/lib/learn-tool/learn-repair.rb +25 -0
- data/lib/learn-tool/license-linter.rb +27 -0
- data/lib/learn-tool/readme-linter.rb +55 -0
- data/lib/{support_files → learn-tool/support_files}/CONTRIBUTING.md +0 -0
- data/lib/{support_files → learn-tool/support_files}/LICENSE.md +0 -0
- data/lib/learn-tool/yaml-linter.rb +44 -0
- metadata +13 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71fe36246ba3b898eff013d99569d72091bd0e575ee5efa2d52135e4425bed67
|
4
|
+
data.tar.gz: ac8b171728f7ff943d58d6bbe334d406b02e87a6236c781aa96ffac0954c693a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07af1813e35c0678cc0eee5645f79cd68a160b313e537c259e9e090767bf7e6619416df40e91841352d4d64ebc5e7e57d5d4c6960914b7233b07227404b6d7d6
|
7
|
+
data.tar.gz: c3b7f30f1e39ec94cad66695614d58de030a8895ccad61d9569a7905a268f71085db9add823aae700dd17d3c7399ff422d0aa553623335e0eb9335f17b5e193e
|
data/bin/learn-tool
CHANGED
@@ -12,9 +12,10 @@ OptionParser.new do |opts|
|
|
12
12
|
A tool for creating, cloning and repairing repositories. This tool has
|
13
13
|
three basic commands
|
14
14
|
|
15
|
-
learn-tool create
|
16
|
-
learn-tool duplicate
|
17
|
-
learn-tool
|
15
|
+
learn-tool --create
|
16
|
+
learn-tool --duplicate
|
17
|
+
learn-tool --lint <optional: filepath>
|
18
|
+
learn-tool --repair <optional: filepath>
|
18
19
|
|
19
20
|
When creating, you will need to provide a name of the repository you are
|
20
21
|
creating and choose from an existing template.
|
@@ -25,23 +26,43 @@ OptionParser.new do |opts|
|
|
25
26
|
|
26
27
|
EOBANNER
|
27
28
|
|
28
|
-
opts.on("
|
29
|
-
|
30
|
-
|
29
|
+
opts.on("-c", "--create",
|
30
|
+
"Create new repository") do |v|
|
31
|
+
options[:create] = true
|
32
|
+
end
|
33
|
+
opts.on("-d", "--duplicate",
|
34
|
+
"Clone from existing repository") do |v|
|
35
|
+
options[:duplicate] = true
|
36
|
+
end
|
37
|
+
opts.on("-l", "--lint [ABSOLUTE_FILEPATH]",
|
38
|
+
"Lint a directory given its absolute path. If blank, lints current directory") do |filepath|
|
39
|
+
filepath ? options[:lint] = filepath : options[:lint] = Dir.pwd
|
40
|
+
end
|
41
|
+
opts.on("-r", "--repair [ABSOLUTE_FILEPATH]",
|
42
|
+
"Add correct files to a directory given its absolute path. If blank, edits current directory") do |filepath|
|
43
|
+
filepath ? options[:repair] = filepath : options[:repair] = Dir.pwd
|
44
|
+
end
|
45
|
+
|
31
46
|
end.parse!
|
32
47
|
|
33
48
|
puts options
|
34
49
|
|
35
50
|
if options[:create]
|
36
|
-
|
51
|
+
LearnTool.new(mode: "create", filepath: Dir.pwd)
|
37
52
|
end
|
38
53
|
|
39
54
|
if options[:duplicate]
|
40
|
-
|
55
|
+
LearnTool.new(mode: "duplicate", filepath: Dir.pwd)
|
41
56
|
end
|
42
57
|
|
43
|
-
if options[:
|
44
|
-
|
58
|
+
if options[:lint]
|
59
|
+
LearnTool.new(mode: "lint", filepath: options[:lint])
|
45
60
|
end
|
46
61
|
|
62
|
+
if options[:repair]
|
63
|
+
LearnTool.new(mode: "repair", filepath: options[:repair])
|
64
|
+
end
|
47
65
|
|
66
|
+
if options == {}
|
67
|
+
LearnTool.new(mode: "lint", filepath: Dir.pwd)
|
68
|
+
end
|
data/lib/learn-tool.rb
CHANGED
@@ -1,238 +1,50 @@
|
|
1
1
|
require 'faraday'
|
2
2
|
require 'uri'
|
3
3
|
require 'open3'
|
4
|
+
require_relative './learn-tool/learn-base'
|
5
|
+
require_relative './learn-tool/learn-repair'
|
6
|
+
require_relative './learn-tool/learn-create'
|
7
|
+
require_relative './learn-tool/learn-duplicate'
|
8
|
+
require_relative './learn-tool/learn-lint'
|
9
|
+
require_relative './learn-tool/learn-error'
|
10
|
+
require_relative './learn-tool/license-linter'
|
11
|
+
require_relative './learn-tool/readme-linter'
|
12
|
+
require_relative './learn-tool/yaml-linter'
|
13
|
+
require_relative './learn-tool/contributing-linter'
|
4
14
|
|
5
15
|
class LearnTool
|
6
|
-
|
7
|
-
README_TEMPLATE = 'readme-template'
|
8
|
-
RUBY_LAB_TEMPLATE = 'ruby-lab-template'
|
9
|
-
JAVASCRIPT_LAB_TEMPLATE = 'js-lab-template'
|
10
|
-
REACT_LAB_TEMPLATE = 'react-lab-template'
|
16
|
+
|
11
17
|
|
12
|
-
def initialize(mode)
|
13
|
-
|
14
|
-
puts mode
|
18
|
+
def initialize(mode:, filepath:Dir.pwd)
|
19
|
+
puts filepath
|
15
20
|
if mode == 'create'
|
16
|
-
|
21
|
+
LearnCreate.new(filepath)
|
17
22
|
end
|
18
23
|
|
19
24
|
if mode == 'duplicate'
|
20
|
-
|
25
|
+
LearnDuplicate.new(filepath)
|
21
26
|
end
|
22
27
|
|
23
28
|
if mode == 'repair'
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def create
|
29
|
-
puts 'Note: You must have write access to the learn-co-curriculum org on GitHub to use this tool'
|
30
|
-
until name_new_repo do
|
31
|
-
puts 'Careful - rate limiting can occur'
|
29
|
+
LearnRepair.new(filepath)
|
32
30
|
end
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
end_message
|
37
|
-
end
|
38
|
-
|
39
|
-
def choose_repo_template
|
40
|
-
puts 'Is the lesson you are creating a Readme? (Y/n)'
|
41
|
-
readme_input = gets.chomp.downcase
|
42
|
-
if readme_input == "n" || readme_input == "no" || readme_input == "N" || readme_input == "No"
|
43
|
-
language = choose_language
|
44
|
-
case language
|
45
|
-
when /^ru/
|
46
|
-
@old_repo_name = RUBY_LAB_TEMPLATE
|
47
|
-
when /^j/
|
48
|
-
@old_repo_name = JAVASCRIPT_LAB_TEMPLATE
|
49
|
-
when /^re/
|
50
|
-
@old_repo_name = REACT_LAB_TEMPLATE
|
51
|
-
else
|
52
|
-
@old_repo_name = README_TEMPLATE
|
53
|
-
end
|
54
|
-
else
|
55
|
-
@old_repo_name = README_TEMPLATE
|
32
|
+
if mode == 'lint'
|
33
|
+
LearnLinter.new(filepath).lint_directory
|
56
34
|
end
|
57
|
-
@old_repo_name
|
58
35
|
end
|
59
36
|
|
60
|
-
|
61
|
-
language = ''
|
62
|
-
loop do
|
63
|
-
puts 'What lab template would you like to use? (Ruby/JavaScript/React)'
|
64
|
-
language = gets.chomp.downcase
|
65
|
-
break if language =~ /^(ru|j|re)/
|
66
|
-
puts 'Please enter Ruby, JavaScript or React, or at minimum, the first two letters:'
|
67
|
-
puts ''
|
68
|
-
end
|
69
|
-
language
|
70
|
-
end
|
71
|
-
|
72
|
-
def duplicate
|
73
|
-
puts 'Note: You must have write access to the learn-co-curriculum org on GitHub to use this tool'
|
74
|
-
loop do
|
75
|
-
puts 'What is the name of the repository you would like to copy? Paste exactly as is shown in the URL (i.e. advanced-hashes-hashketball)'
|
76
|
-
old_repo_name_input = gets.strip
|
77
|
-
if repo_exists(old_repo_name_input)
|
78
|
-
@old_repo_name = old_repo_name_input
|
79
|
-
puts ''
|
80
|
-
puts 'Old repository: ' + @old_repo_name
|
81
|
-
until name_new_repo do
|
82
|
-
puts 'Careful - rate limiting can occur'
|
83
|
-
end
|
84
|
-
|
85
|
-
|
86
|
-
create_new_repo
|
87
|
-
end_message
|
88
|
-
break
|
89
|
-
else
|
90
|
-
puts 'Provided repository name is not a valid learn-co-curriculum repository. Please try again. Careful - rate limiting can be triggered'
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
37
|
+
|
94
38
|
|
95
|
-
def name_new_repo
|
96
|
-
puts 'What is the name of the repository you would like to create?'
|
97
|
-
new_name = gets.strip.gsub(/\s+/, '-').downcase
|
98
|
-
|
99
|
-
if name_length_is_good(new_name)
|
100
|
-
if !repo_exists(new_name)
|
101
|
-
@new_repo_name = new_name
|
102
|
-
else
|
103
|
-
puts 'A repository with that name already exists. Please try again.'
|
104
|
-
return false
|
105
|
-
end
|
106
|
-
else
|
107
|
-
puts 'Repository names must be shorter than 100 characters'
|
108
|
-
return false
|
109
|
-
end
|
110
|
-
@new_repo_name
|
111
|
-
end
|
112
|
-
|
113
|
-
def create_new_repo
|
114
|
-
# 'cd' doesn't work the way it would in the shell, must be used before every command
|
115
|
-
puts 'Cloning old repository'
|
116
|
-
git_clone
|
117
|
-
# puts "Renaming old directory with new name: #{@new_repo_name}"
|
118
|
-
# rename_repo
|
119
|
-
puts ''
|
120
|
-
puts 'Creating new remote learn-co-curriculum repository'
|
121
|
-
git_create_and_set_new_origin
|
122
|
-
puts ''
|
123
|
-
puts 'Setting new git remote based on SSH settings'
|
124
|
-
git_set_remote
|
125
|
-
puts ''
|
126
|
-
puts 'Pushing all old-remote branches to new remote'
|
127
|
-
git_push
|
128
|
-
end
|
129
|
-
|
130
|
-
def end_message
|
131
|
-
puts ''
|
132
|
-
puts 'To access local folder, change directory into ' + @new_repo_name + '/'
|
133
|
-
puts "Repository available at #{GITHUB_ORG}" + @new_repo_name
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
def git_clone
|
139
|
-
cmd = "git clone https://github.com/learn-co-curriculum/#{@old_repo_name} #{@new_repo_name}"
|
140
|
-
puts cmd
|
141
|
-
`#{cmd}`
|
142
|
-
end
|
143
|
-
|
144
|
-
def git_create_and_set_new_origin
|
145
|
-
# Creates repo **and** assigns new remote to 'origin' shortname
|
146
|
-
cmd = cd_into_and("hub create learn-co-curriculum/#{@new_repo_name}")
|
147
|
-
puts cmd
|
148
|
-
`#{cmd}`
|
149
|
-
end
|
150
|
-
|
151
|
-
def git_set_remote
|
152
|
-
remote = check_ssh_config ? "git@github.com:learn-co-curriculum/#{@new_repo_name}.git" : "https://github.com/learn-co-curriculum/#{@new_repo_name}"
|
153
|
-
cmd = cd_into_and("git remote set-url origin #{remote}")
|
154
|
-
puts cmd
|
155
|
-
`#{cmd}`
|
156
|
-
end
|
157
|
-
|
158
|
-
def git_push
|
159
|
-
# Copy `master`, attempt to copy `solution`, but if it's not there, no complaints
|
160
|
-
cmds = [
|
161
|
-
%q|git push origin 'refs/remotes/origin/master:refs/heads/master' > /dev/null 2>&1|,
|
162
|
-
%q|git push origin 'refs/remotes/origin/solution:refs/heads/solution' > /dev/null 2>&1|
|
163
|
-
]
|
164
|
-
cmds.each { |cmd| `#{cd_into_and(cmd)}` }
|
165
|
-
end
|
166
|
-
|
167
|
-
def repo_exists(repo_name)
|
168
|
-
url = GITHUB_ORG + repo_name
|
169
|
-
encoded_url = URI.encode(url).slice(0, url.length)
|
170
|
-
check_existing = Faraday.get URI.parse(encoded_url)
|
171
|
-
!check_existing.body.include? '"Not Found"'
|
172
|
-
end
|
173
|
-
|
174
|
-
def cd_into_and(command)
|
175
|
-
"cd #{@new_repo_name} && #{command}"
|
176
|
-
end
|
177
|
-
|
178
|
-
def create_support_file(name_of_file)
|
179
|
-
# copies a template folder from the learn_create gem to a subfolder of the current directory
|
180
|
-
gem_template_location = File.dirname(__FILE__)
|
181
|
-
template_path = File.expand_path(gem_template_location) + "/support_files/#{name_of_file.upcase}"
|
182
|
-
cmd = "cp #{template_path} #{Dir.pwd}"
|
183
|
-
`#{cmd}`
|
184
|
-
end
|
185
|
-
|
186
|
-
def create_dot_learn_file
|
187
|
-
`
|
188
|
-
cat > .learn <<EOL
|
189
|
-
languages:
|
190
|
-
- none
|
191
|
-
`
|
192
|
-
end
|
193
39
|
|
194
|
-
# def create_dot_gitignore_file
|
195
|
-
# `
|
196
|
-
# cat > .gitignore <<EOL
|
197
|
-
# .DS_Store
|
198
|
-
# logs
|
199
|
-
# *.log
|
200
|
-
# npm-debug.log*
|
201
|
-
# pids
|
202
|
-
# *.pid
|
203
|
-
# *.seed
|
204
|
-
# lib-cov
|
205
|
-
# build/Release
|
206
|
-
# node_modules
|
207
|
-
# jspm_packages
|
208
|
-
# .npm
|
209
|
-
# .node_repl_history
|
210
|
-
# .results.json
|
211
|
-
# /.bundle
|
212
|
-
# /db/*.sqlite3
|
213
|
-
# /db/*.sqlite3-journal
|
214
|
-
# /log/*
|
215
|
-
# !/log/.keep
|
216
|
-
# /tmp
|
217
|
-
# `
|
218
|
-
# end
|
219
40
|
|
220
41
|
|
221
42
|
|
222
|
-
|
223
|
-
create_dot_learn_file
|
224
|
-
create_support_file("LICENSE.md")
|
225
|
-
create_support_file("CONTRIBUTING.md")
|
226
|
-
end
|
43
|
+
|
227
44
|
|
228
|
-
|
229
|
-
name.length < 100
|
230
|
-
end
|
45
|
+
|
231
46
|
|
232
|
-
|
233
|
-
result = Open3.capture2e('ssh -T git@github.com').first
|
234
|
-
result.include?("You've successfully authenticated")
|
235
|
-
end
|
47
|
+
|
236
48
|
|
237
49
|
|
238
50
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class ContributingLinter
|
2
|
+
|
3
|
+
VALID_FILE = File.open(File.expand_path(File.dirname(__FILE__)) + '/support_files/CONTRIBUTING.md')
|
4
|
+
|
5
|
+
def self.parse_file(file, learn_error)
|
6
|
+
directory_file = sanitize_whitespace(File.open(file).read)
|
7
|
+
valid_file_array = sanitize_whitespace(VALID_FILE.read)
|
8
|
+
diff = directory_file - valid_file_array
|
9
|
+
|
10
|
+
if diff.empty?
|
11
|
+
learn_error.contributing_error[:valid_contributing] = true
|
12
|
+
learn_error.valid_contributing = {message: "valid CONTRIBUTING.md", color: :green}
|
13
|
+
else
|
14
|
+
learn_error.contributing_error[:valid_contributing] = false
|
15
|
+
learn_error.valid_contributing = {message: "invalid CONTRIBUTING.md - Errors: #{diff}", color: :red}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.sanitize_whitespace(file)
|
20
|
+
file.split.delete_if {|char| char.empty? || char == "\n"}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'pry'
|
2
|
+
class LearnBase
|
3
|
+
|
4
|
+
GITHUB_ORG = 'https://api.github.com/repos/learn-co-curriculum/'
|
5
|
+
README_TEMPLATE = 'readme-template'
|
6
|
+
RUBY_LAB_TEMPLATE = 'ruby-lab-template'
|
7
|
+
JAVASCRIPT_LAB_TEMPLATE = 'js-lab-template'
|
8
|
+
REACT_LAB_TEMPLATE = 'react-lab-template'
|
9
|
+
|
10
|
+
def initialize(filepath)
|
11
|
+
@filepath = filepath
|
12
|
+
@old_repo_name = ''
|
13
|
+
@new_repo_name = ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def repo_is_available(repo_name)
|
17
|
+
url = GITHUB_ORG + repo_name
|
18
|
+
encoded_url = URI.encode(url).slice(0, url.length)
|
19
|
+
check_existing = Faraday.get URI.parse(encoded_url)
|
20
|
+
check_existing.body.include? '"Not Found"'
|
21
|
+
end
|
22
|
+
|
23
|
+
def cd_into_and(filepath, command)
|
24
|
+
puts filepath, command
|
25
|
+
cmd = "cd #{filepath} && #{command}"
|
26
|
+
`#{cmd}`
|
27
|
+
end
|
28
|
+
|
29
|
+
def name_new_repo
|
30
|
+
puts 'What is the name of the repository you would like to create?'
|
31
|
+
new_name = gets.strip.gsub(/\s+/, '-').downcase
|
32
|
+
|
33
|
+
if name_length_is_good(new_name)
|
34
|
+
if repo_is_available(new_name)
|
35
|
+
@new_repo_name = new_name
|
36
|
+
else
|
37
|
+
puts 'A repository with that name already exists. Please try again.'
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
else
|
41
|
+
puts 'Repository names must be shorter than 100 characters'
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
@new_repo_name
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_new_name
|
48
|
+
puts 'Note: You must have write access to the learn-co-curriculum org on GitHub to use this tool'
|
49
|
+
until name_new_repo do
|
50
|
+
puts 'Careful - rate limiting can occur'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_new_repo
|
55
|
+
# 'cd' doesn't work the way it would in the shell, must be used before every command
|
56
|
+
puts 'Cloning old repository'
|
57
|
+
git_clone
|
58
|
+
# puts "Renaming old directory with new name: #{new_repo_name}"
|
59
|
+
# rename_repo
|
60
|
+
puts ''
|
61
|
+
puts 'Creating new remote learn-co-curriculum repository'
|
62
|
+
git_create_and_set_new_origin
|
63
|
+
puts ''
|
64
|
+
puts 'Setting new git remote based on SSH settings'
|
65
|
+
git_set_remote
|
66
|
+
puts ''
|
67
|
+
puts 'Pushing all old-remote branches to new remote'
|
68
|
+
git_push
|
69
|
+
end
|
70
|
+
|
71
|
+
def end_message
|
72
|
+
puts ''
|
73
|
+
puts 'To access local folder, change directory into ' + @new_repo_name + '/'
|
74
|
+
puts "Repository available at #{GITHUB_ORG}" + @new_repo_name
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def git_clone
|
80
|
+
cmd = "git clone https://github.com/learn-co-curriculum/#{@old_repo_name} #{@new_repo_name}"
|
81
|
+
puts cmd
|
82
|
+
`#{cmd}`
|
83
|
+
end
|
84
|
+
|
85
|
+
def git_create_and_set_new_origin
|
86
|
+
# Creates repo **and** assigns new remote to 'origin' shortname
|
87
|
+
cd_into_and(@filepath + "/#{@new_repo_name}", "hub create learn-co-curriculum/#{@new_repo_name}")
|
88
|
+
end
|
89
|
+
|
90
|
+
def git_set_remote
|
91
|
+
remote = check_ssh_config ? "git@github.com:learn-co-curriculum/#{@new_repo_name}.git" : "https://github.com/learn-co-curriculum/#{new_repo_name}"
|
92
|
+
cd_into_and(@filepath + "/#{@new_repo_name}", "git remote set-url origin #{remote}")
|
93
|
+
end
|
94
|
+
|
95
|
+
def git_push
|
96
|
+
# Copy `master`, attempt to copy `solution`, but if it's not there, no complaints
|
97
|
+
cmds = [
|
98
|
+
%q|git push origin 'refs/remotes/origin/master:refs/heads/master' > /dev/null 2>&1|,
|
99
|
+
%q|git push origin 'refs/remotes/origin/solution:refs/heads/solution' > /dev/null 2>&1|
|
100
|
+
]
|
101
|
+
cmds.each { |cmd| cd_into_and(@filepath + "/#{@new_repo_name}", cmd) }
|
102
|
+
end
|
103
|
+
|
104
|
+
def name_length_is_good(name)
|
105
|
+
name.length < 100
|
106
|
+
end
|
107
|
+
|
108
|
+
def check_ssh_config
|
109
|
+
result = Open3.capture2e('ssh -T git@github.com').first
|
110
|
+
result.include?("You've successfully authenticated")
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class LearnCreate < LearnBase
|
2
|
+
|
3
|
+
def initialize(filepath)
|
4
|
+
super(filepath)
|
5
|
+
get_new_name
|
6
|
+
choose_repo_template
|
7
|
+
create_new_repo
|
8
|
+
end_message
|
9
|
+
end
|
10
|
+
|
11
|
+
def choose_repo_template
|
12
|
+
puts 'Is the lesson you are creating a Readme? (Y/n)'
|
13
|
+
readme_input = gets.chomp.downcase
|
14
|
+
if readme_input == "n" || readme_input == "no" || readme_input == "N" || readme_input == "No"
|
15
|
+
language = choose_language
|
16
|
+
case language
|
17
|
+
when /^ru/
|
18
|
+
@old_repo_name = RUBY_LAB_TEMPLATE
|
19
|
+
when /^j/
|
20
|
+
@old_repo_name = JAVASCRIPT_LAB_TEMPLATE
|
21
|
+
when /^re/
|
22
|
+
@old_repo_name = REACT_LAB_TEMPLATE
|
23
|
+
else
|
24
|
+
@old_repo_name = README_TEMPLATE
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@old_repo_name = README_TEMPLATE
|
28
|
+
end
|
29
|
+
@old_repo_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def choose_language
|
33
|
+
language = ''
|
34
|
+
loop do
|
35
|
+
puts 'What lab template would you like to use? (Ruby/JavaScript/React)'
|
36
|
+
language = gets.chomp.downcase
|
37
|
+
break if language =~ /^(ru|j|re)/
|
38
|
+
puts 'Please enter Ruby, JavaScript or React, or at minimum, the first two letters:'
|
39
|
+
puts ''
|
40
|
+
end
|
41
|
+
language
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class LearnDuplicate < LearnBase
|
2
|
+
|
3
|
+
def initialize(filepath)
|
4
|
+
super(filepath)
|
5
|
+
puts 'Note: You must have write access to the learn-co-curriculum org on GitHub to use this tool'
|
6
|
+
get_old_repo
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_old_repo
|
10
|
+
loop do
|
11
|
+
puts 'What is the name of the repository you would like to copy? Paste exactly as is shown in the URL (i.e. advanced-hashes-hashketball)'
|
12
|
+
old_repo_name_input = gets.strip
|
13
|
+
if !repo_is_available(old_repo_name_input)
|
14
|
+
@old_repo_name = old_repo_name_input
|
15
|
+
puts ''
|
16
|
+
puts 'Old repository: ' + @old_repo_name
|
17
|
+
until name_new_repo do
|
18
|
+
puts 'Careful - rate limiting can occur'
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
create_new_repo
|
23
|
+
end_message
|
24
|
+
break
|
25
|
+
else
|
26
|
+
puts 'Provided repository name is not a valid learn-co-curriculum repository. Please try again. Careful - rate limiting can be triggered'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class LearnError < StandardError
|
2
|
+
attr_accessor :filepath, :valid_yaml, :valid_license,
|
3
|
+
:present_learn, :present_license, :present_readme, :yaml_error,
|
4
|
+
:readme_error, :license_error, :valid_readme, :correct_yaml_content,
|
5
|
+
:valid_contributing, :present_contributing, :contributing_error
|
6
|
+
|
7
|
+
ESCAPES = { :green => "\033[32m",
|
8
|
+
:yellow => "\033[33m",
|
9
|
+
:red => "\033[31m",
|
10
|
+
:reset => "\033[0m" }
|
11
|
+
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@yaml_error = {present_dotlearn: false, valid_yaml: false, valid_whitespace: false, attributes: false}
|
15
|
+
@readme_error = {present_readme: false, valid_readme: false}
|
16
|
+
@license_error = {present_license: false, valid_license: false}
|
17
|
+
@contributing_error = {present_contributing: false, valid_contributing: false}
|
18
|
+
|
19
|
+
@correct_yaml_content = {message: ".learn file must have 'languages' key", color: :red}
|
20
|
+
|
21
|
+
@valid_yaml = {message: "invalid yaml", color: :red}
|
22
|
+
@valid_license = {message: "invalid or missing license content", color: :yellow}
|
23
|
+
@valid_readme = {message: [], color: :red}
|
24
|
+
@valid_contributing = {message: "invalid or missing contributing content", color: :yellow}
|
25
|
+
|
26
|
+
@present_learn = {message: "missing .learn file", color: :red}
|
27
|
+
@present_license = {message: "missing LICENSE.md", color: :red}
|
28
|
+
@present_readme = {message: "missing README.md", color: :red}
|
29
|
+
@present_contributing = {message: "missing CONTRIBUTING.md", color: :red}
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def emit(opts={})
|
34
|
+
color = opts[:color]
|
35
|
+
message = opts[:message]
|
36
|
+
print ESCAPES[color]
|
37
|
+
print message
|
38
|
+
print ESCAPES[:reset]
|
39
|
+
print "\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
def total_errors
|
43
|
+
{
|
44
|
+
dot_learn: yaml_error,
|
45
|
+
license: license_error,
|
46
|
+
readme: readme_error,
|
47
|
+
contributing: contributing_error
|
48
|
+
}
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def result_message
|
53
|
+
[present_learn, valid_yaml, correct_yaml_content, present_license, valid_license, present_readme, valid_readme, present_contributing, valid_contributing ]
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def result_output
|
58
|
+
result_message.each do |result|
|
59
|
+
if result[:message].is_a?(Array)
|
60
|
+
result[:message].each do |result_message|
|
61
|
+
emit({message: result_message, color: result[:color]})
|
62
|
+
end
|
63
|
+
else
|
64
|
+
emit(result)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class LearnLinter
|
2
|
+
def initialize(filepath)
|
3
|
+
@learn_error = LearnError.new
|
4
|
+
@filepath = filepath
|
5
|
+
end
|
6
|
+
|
7
|
+
def result_message
|
8
|
+
@learn_error.result_message
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_file?(file)
|
12
|
+
Dir.entries(@filepath).include?(file) || Dir.entries(@filepath).include?(file.upcase) || Dir.entries(@filepath).include?(file.downcase)
|
13
|
+
end
|
14
|
+
|
15
|
+
def lint_directory
|
16
|
+
self.yaml_lint
|
17
|
+
self.license_lint
|
18
|
+
self.readme_lint
|
19
|
+
self.contributing_lint
|
20
|
+
@learn_error.result_output
|
21
|
+
@learn_error.total_errors
|
22
|
+
end
|
23
|
+
|
24
|
+
def yaml_lint
|
25
|
+
if self.has_file?(".learn")
|
26
|
+
@learn_error.yaml_error[:present_dotlearn] = true
|
27
|
+
@learn_error.present_learn = {message: "present .learn file", color: :green}
|
28
|
+
YamlLint.parse_file("#{@filepath}/.learn", @learn_error)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def license_lint
|
33
|
+
if self.has_file?("LICENSE.md")
|
34
|
+
@learn_error.license_error[:present_license] = true
|
35
|
+
@learn_error.present_license = {message: "present LICENSE.md", color: :green}
|
36
|
+
LicenseLinter.parse_file("#{@filepath}/LICENSE.md", @learn_error)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def readme_lint
|
41
|
+
if self.has_file?("README.md")
|
42
|
+
@learn_error.readme_error[:present_readme] = true
|
43
|
+
@learn_error.present_readme = {message: "present README.md", color: :green}
|
44
|
+
ReadmeLinter.parse_file("#{@filepath}/README.md", @learn_error)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def contributing_lint
|
49
|
+
if self.has_file?("CONTRIBUTING.md")
|
50
|
+
@learn_error.contributing_error[:present_contributing] = true
|
51
|
+
@learn_error.present_contributing = {message: "present CONTRIBUTING.md", color: :green}
|
52
|
+
ContributingLinter.parse_file("#{@filepath}/CONTRIBUTING.md", @learn_error)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class LearnRepair < LearnBase
|
2
|
+
|
3
|
+
def initialize(filepath)
|
4
|
+
super(filepath)
|
5
|
+
cd_into_and(filepath, create_dot_learn_file)
|
6
|
+
cd_into_and(filepath, create_support_file("LICENSE.md"))
|
7
|
+
cd_into_and(filepath, create_support_file("CONTRIBUTING.md"))
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_dot_learn_file
|
11
|
+
`
|
12
|
+
cat > .learn <<- EOL
|
13
|
+
languages:
|
14
|
+
- none
|
15
|
+
`
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_support_file(name_of_file)
|
19
|
+
# copies a template folder from the learn_create gem to a subfolder of the current directory
|
20
|
+
gem_template_location = File.dirname(__FILE__)
|
21
|
+
template_path = File.expand_path(gem_template_location) + "/support_files/#{name_of_file}"
|
22
|
+
"cp #{template_path} #{@filepath}"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class LicenseLinter
|
2
|
+
|
3
|
+
VALID_FILE = File.open(File.expand_path(File.dirname(__FILE__)) + '/support_files/LICENSE.md')
|
4
|
+
VALID_YEARS = ["2015","2016","2017","2018","2019","2020","2021","2022","2023","2024","2025"]
|
5
|
+
|
6
|
+
def self.parse_file(file, learn_error)
|
7
|
+
directory_file_array = sanitize_whitespace(File.open(file).read)
|
8
|
+
valid_file_array = sanitize_whitespace(VALID_FILE.read)
|
9
|
+
diff = directory_file_array - valid_file_array
|
10
|
+
|
11
|
+
VALID_YEARS.each do |year|
|
12
|
+
diff.delete(year)
|
13
|
+
end
|
14
|
+
|
15
|
+
if diff.empty?
|
16
|
+
learn_error.license_error[:valid_license] = true
|
17
|
+
learn_error.valid_license = {message: "valid LICENSE.md", color: :green}
|
18
|
+
else
|
19
|
+
learn_error.license_error[:valid_license] = false
|
20
|
+
learn_error.valid_license = {message: "invalid LICENSE.md - Errors: #{diff}", color: :red}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.sanitize_whitespace(file)
|
25
|
+
file.split.delete_if {|char| char.empty? || char == "\n"}
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class ReadmeLinter
|
2
|
+
|
3
|
+
def self.parse_file(file, learn_error)
|
4
|
+
if has_code_snippets?(file)
|
5
|
+
lines = collect_lines(file)
|
6
|
+
validate_snippets(lines, learn_error)
|
7
|
+
else
|
8
|
+
green_light(learn_error)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.green_light(learn_error)
|
13
|
+
learn_error.readme_error[:valid_readme] = true
|
14
|
+
learn_error.valid_readme = {message: "valid readme", color: :green}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.has_code_snippets?(file)
|
18
|
+
file_content = File.open(file).read
|
19
|
+
file_content.match(/``/)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.collect_lines(file)
|
23
|
+
lines = {}
|
24
|
+
File.foreach(file) do |line_content|
|
25
|
+
lines["#{$.}"] = line_content
|
26
|
+
end
|
27
|
+
lines
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.validate_snippets(lines, learn_error)
|
31
|
+
lint_lines(lines, learn_error)
|
32
|
+
total_errors?(learn_error)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.lint_lines(lines, learn_error)
|
36
|
+
lines.each do |line_num, line_content|
|
37
|
+
if line_content.match(/``/)
|
38
|
+
if !(line_content.match(/^```(ruby|rb|bash|sh|swift|html|erb|js|javascript|objc|java|sql|css|text|python)?$/))
|
39
|
+
learn_error.valid_readme[:message] << "INVALID CODE SNIPPET - line #{line_num}: #{line_content}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.total_errors?(learn_error)
|
46
|
+
if error_free?(learn_error)
|
47
|
+
green_light(learn_error)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.error_free?(learn_error)
|
52
|
+
learn_error.valid_readme[:message].empty?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
File without changes
|
File without changes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class YamlLint
|
4
|
+
|
5
|
+
|
6
|
+
def self.parse_file(file, learn_error)
|
7
|
+
begin
|
8
|
+
YAML.load_file(file)
|
9
|
+
rescue Exception => err
|
10
|
+
learn_error.valid_yaml = {message: "#{err}", color: :red}
|
11
|
+
else
|
12
|
+
learn_error.yaml_error[:valid_yaml] = true
|
13
|
+
if check_attributes(file)
|
14
|
+
learn_error.yaml_error[:attributes] = true
|
15
|
+
learn_error.correct_yaml_content = {message: "valid attribute key names", color: :green}
|
16
|
+
end
|
17
|
+
if self.validate_whitespace_for_learn(file)
|
18
|
+
learn_error.yaml_error[:valid_whitespace] = true
|
19
|
+
learn_error.valid_yaml = {message: "valid yaml and valid whitespace.", color: :green}
|
20
|
+
else
|
21
|
+
learn_error.valid_yaml = {message: "valid yaml but invalid whitespace", color: :red}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.validate_whitespace_for_learn(file)
|
27
|
+
lines = file_lines(file).split("\n")
|
28
|
+
attributes = lines.select { |line| line.include?("-") }
|
29
|
+
attributes.each do |attribute|
|
30
|
+
return false unless attribute[0..3] == " - "
|
31
|
+
end
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.check_attributes(file)
|
36
|
+
file_string = file_lines(file)
|
37
|
+
file_string.match(/languages/)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.file_lines(file)
|
41
|
+
f = File.read(file)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: learn-tool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- flatironschool
|
@@ -38,8 +38,18 @@ files:
|
|
38
38
|
- Rakefile
|
39
39
|
- bin/learn-tool
|
40
40
|
- lib/learn-tool.rb
|
41
|
-
- lib/
|
42
|
-
- lib/
|
41
|
+
- lib/learn-tool/contributing-linter.rb
|
42
|
+
- lib/learn-tool/learn-base.rb
|
43
|
+
- lib/learn-tool/learn-create.rb
|
44
|
+
- lib/learn-tool/learn-duplicate.rb
|
45
|
+
- lib/learn-tool/learn-error.rb
|
46
|
+
- lib/learn-tool/learn-lint.rb
|
47
|
+
- lib/learn-tool/learn-repair.rb
|
48
|
+
- lib/learn-tool/license-linter.rb
|
49
|
+
- lib/learn-tool/readme-linter.rb
|
50
|
+
- lib/learn-tool/support_files/CONTRIBUTING.md
|
51
|
+
- lib/learn-tool/support_files/LICENSE.md
|
52
|
+
- lib/learn-tool/yaml-linter.rb
|
43
53
|
homepage: https://github.com/learn-co-curriculum/learn-tool
|
44
54
|
licenses:
|
45
55
|
- MIT
|