learn-tool 0.0.16 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|