gsm-sources-manager 1.0.0.alpha3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +17 -0
- data/.gitignore +52 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +21 -0
- data/README.md +69 -0
- data/Rakefile +24 -0
- data/docs/.gitignore +1 -0
- data/docs/_config.yml +1 -0
- data/docs/_data/changelog.yml +3 -0
- data/docs/_data/index.yml +87 -0
- data/docs/_layouts/default.html +43 -0
- data/docs/css/style.css +177 -0
- data/docs/image/gsm.png +0 -0
- data/docs/index.html +97 -0
- data/exe/gsm +139 -0
- data/gsm.gemspec +29 -0
- data/lib/gsm.rb +6 -0
- data/lib/gsm/bundle.rb +14 -0
- data/lib/gsm/gem.rb +178 -0
- data/lib/gsm/util.rb +4 -0
- data/lib/gsm/version.rb +3 -0
- data/test/conf/good.yml +6 -0
- data/test/conf/multiple.yml +6 -0
- data/test/helper.rb +18 -0
- data/test/test_bundle.rb +17 -0
- data/test/test_gem.rb +198 -0
- data/test/test_version.rb +9 -0
- metadata +137 -0
data/docs/image/gsm.png
ADDED
Binary file
|
data/docs/index.html
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
---
|
2
|
+
permalink: index.html
|
3
|
+
layout: default
|
4
|
+
---
|
5
|
+
<div id="intro">
|
6
|
+
<div id="intro-logo">
|
7
|
+
<img src="/gsm/image/gsm.png">
|
8
|
+
</div>
|
9
|
+
<div id="intro-text">
|
10
|
+
GSM is a simple sources manager for RubyGems.
|
11
|
+
</div>
|
12
|
+
<div id="intro-desc">
|
13
|
+
In Ruby development, we always need alternative RubyGems source in special networks. e.g. In China Mainland, you know. GSM helps get your gem sources managed.
|
14
|
+
</div>
|
15
|
+
<div id="intro-button">
|
16
|
+
<div class="install-item install-gsm">
|
17
|
+
<a href="#install">Install GSM</a>
|
18
|
+
</div>
|
19
|
+
<div class="install-item install-user-guide">
|
20
|
+
<a href="#guide">User Guide</a>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
<div id="intro-version">
|
24
|
+
<span>Version <a href="#changelog">{{ site.data.changelog[0].version }}</a> ·</span>
|
25
|
+
<span>Created by <a href="https://crispgm.com">David Zhang</a></span>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
<div id="social">
|
29
|
+
<a class="github-button" href="https://github.com/crispgm/gsm" data-icon="octicon-star" data-count-href="/crispgm/gsm/stargazers" data-count-api="/repos/crispgm/gsm#stargazers_count" data-count-aria-label="# stargazers on GitHub" aria-label="Star crispgm/gsm on GitHub">Star</a>
|
30
|
+
<a class="github-button" href="https://github.com/crispgm/gsm/fork" data-icon="octicon-repo-forked" data-count-href="/crispgm/gsm/network" data-count-api="/repos/crispgm/gsm#forks_count" data-count-aria-label="# forks on GitHub" aria-label="Fork crispgm/gsm on GitHub">Fork</a>
|
31
|
+
</div>
|
32
|
+
<div id="install" class="content">
|
33
|
+
<div class="content-title">
|
34
|
+
Installation
|
35
|
+
</div>
|
36
|
+
<div>
|
37
|
+
{{ site.data.index.install | markdownify }}
|
38
|
+
</div>
|
39
|
+
<div class="back-top">
|
40
|
+
<a href="#topbar">Back to top</a>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
<div id="guide" class="content">
|
44
|
+
<div class="content-title">
|
45
|
+
User Guide
|
46
|
+
</div>
|
47
|
+
{{ site.data.index.guide | markdownify }}
|
48
|
+
<div class="back-top">
|
49
|
+
<a href="#topbar">Back to top</a>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
<div id="changelog" class="content">
|
53
|
+
<div class="content-title">
|
54
|
+
Changelog
|
55
|
+
</div>
|
56
|
+
{% for change in site.data.changelog %}
|
57
|
+
<h3>Version {{ change.version }}</h3>
|
58
|
+
<ul>
|
59
|
+
{% for log in change.changes %}
|
60
|
+
<li>
|
61
|
+
{{ log.text }}
|
62
|
+
{% if log.issue != nil %}
|
63
|
+
<a href="https://github.com/crispgm/gsm/issues/{{ log.issue }}">#{{ log.issue }}</a></li>
|
64
|
+
{% endif %}
|
65
|
+
</li>
|
66
|
+
{% endfor %}
|
67
|
+
</ul>
|
68
|
+
{% endfor %}
|
69
|
+
<div class="back-top">
|
70
|
+
<a href="#topbar">Back to top</a>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
<div id="contributing" class="content">
|
74
|
+
<div class="content-title">
|
75
|
+
Contributing
|
76
|
+
</div>
|
77
|
+
{{ site.data.index.contrib | markdownify }}
|
78
|
+
<div class="back-top">
|
79
|
+
<a href="#topbar">Back to top</a>
|
80
|
+
</div>
|
81
|
+
</div>
|
82
|
+
<div id="disqus" class="content">
|
83
|
+
<div id="disqus_thread"></div>
|
84
|
+
<script>
|
85
|
+
var disqus_config = function () {
|
86
|
+
this.page.url = 'https://crispgm.github.io/gsm/';
|
87
|
+
this.page.identifier = 'Home | GSM Sources Manager';
|
88
|
+
};
|
89
|
+
(function() { // DON'T EDIT BELOW THIS LINE
|
90
|
+
var d = document, s = d.createElement('script');
|
91
|
+
s.src = '//crispgm-github-io.disqus.com/embed.js';
|
92
|
+
s.setAttribute('data-timestamp', +new Date());
|
93
|
+
(d.head || d.body).appendChild(s);
|
94
|
+
})();
|
95
|
+
</script>
|
96
|
+
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
|
97
|
+
</div>
|
data/exe/gsm
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w(.. lib)))
|
4
|
+
|
5
|
+
require "gsm"
|
6
|
+
require "mercenary"
|
7
|
+
|
8
|
+
Mercenary.program(:gsm) do |p|
|
9
|
+
p.version Gsm::VERSION
|
10
|
+
p.description "GSM Sources Manager"
|
11
|
+
p.syntax "gsm <subcommand> [options]"
|
12
|
+
|
13
|
+
conf_path = ""
|
14
|
+
user_path = Dir.home()
|
15
|
+
Dir.chdir(user_path) do
|
16
|
+
if !Dir.exist?(".gsm")
|
17
|
+
Dir.mkdir(".gsm")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
conf_path = "#{user_path}/.gsm/sources.yml"
|
22
|
+
|
23
|
+
gem = Gsm::Gem.new("#{user_path}/sources.yml")
|
24
|
+
|
25
|
+
p.command(:list) do |c|
|
26
|
+
c.syntax "list"
|
27
|
+
c.description "List all sources"
|
28
|
+
|
29
|
+
c.action do |_, _|
|
30
|
+
gem.sources.each do |name, url|
|
31
|
+
use_mark = ""
|
32
|
+
if gem.get == name
|
33
|
+
use_mark = "(*)"
|
34
|
+
end
|
35
|
+
puts "#{name}: #{url} #{use_mark}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
p.command(:use) do |c|
|
41
|
+
c.syntax "use [source]"
|
42
|
+
c.description "Use source"
|
43
|
+
|
44
|
+
c.action do |args, _|
|
45
|
+
name = args[0]
|
46
|
+
use = gem.use(name)
|
47
|
+
if use
|
48
|
+
puts "GSM: Source `#{use}` has been applied."
|
49
|
+
else
|
50
|
+
puts "GSM Error: Illegal source `#{name}`."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
p.command(:add) do |c|
|
56
|
+
c.syntax "add [name] [url]"
|
57
|
+
c.description "Add source"
|
58
|
+
|
59
|
+
c.option "use", "--use", "Add and use source"
|
60
|
+
|
61
|
+
c.action do |args, opts|
|
62
|
+
name = args[0]
|
63
|
+
url = args[1]
|
64
|
+
is_use = opts["use"]
|
65
|
+
|
66
|
+
if gem.add(name, url)
|
67
|
+
if is_use
|
68
|
+
if !gem.use(name)
|
69
|
+
puts "GSM Error: Illegal source `#{name}`."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
puts "GSM Error: Illegal name/url."
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
p.command(:del) do |c|
|
79
|
+
c.syntax "del [source]"
|
80
|
+
c.description "Delete source"
|
81
|
+
|
82
|
+
c.action do |args, _|
|
83
|
+
name = args[0]
|
84
|
+
ret = gem.del(name)
|
85
|
+
if ret == false
|
86
|
+
puts "GSM Error: Source is in use. Please switch to other source in advance."
|
87
|
+
return
|
88
|
+
elsif ret == nil
|
89
|
+
puts "GSM Error: No source name with `#{name}`."
|
90
|
+
return
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
p.command(:reset) do |c|
|
96
|
+
c.syntax "reset"
|
97
|
+
c.description "Reset all sources"
|
98
|
+
|
99
|
+
c.action do |_, _|
|
100
|
+
gem.reset
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
p.command(:mirror) do |c|
|
105
|
+
c.syntax "mirror [source]"
|
106
|
+
c.description "Mirror source for Bundler"
|
107
|
+
|
108
|
+
c.option "reset", "--reset", "Reset bundler mirror"
|
109
|
+
|
110
|
+
c.action do |args, opts|
|
111
|
+
if opts["reset"]
|
112
|
+
Gsm::Bundle.reset
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
if args.size == 0
|
117
|
+
name = gem.get
|
118
|
+
if name.empty?
|
119
|
+
puts "GSM Error: no default source for mirror."
|
120
|
+
return
|
121
|
+
end
|
122
|
+
else
|
123
|
+
name = args[0]
|
124
|
+
end
|
125
|
+
Gsm::Bundle.mirror(gem.sources[name])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
p.command(:help) do |c|
|
130
|
+
c.syntax "help"
|
131
|
+
c.description "Show usage"
|
132
|
+
|
133
|
+
c.action do |_, _|
|
134
|
+
puts p
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
p.default_command(:help)
|
139
|
+
end
|
data/gsm.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gsm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gsm-sources-manager"
|
8
|
+
spec.version = Gsm::VERSION
|
9
|
+
spec.authors = ["David Zhang"]
|
10
|
+
spec.email = ["crispgm@gmail.com"]
|
11
|
+
spec.summary = "GSM Sources Manager"
|
12
|
+
spec.description = "GSM is a simple sources manager for RubyGems."
|
13
|
+
spec.homepage = "https://github.com/crispgm/gsm"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.required_ruby_version = '>= 2.3.0'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
spec.add_development_dependency "shoulda-context"
|
26
|
+
spec.add_development_dependency "codeclimate-test-reporter"
|
27
|
+
|
28
|
+
spec.add_dependency "mercenary", "~> 0.3.6"
|
29
|
+
end
|
data/lib/gsm.rb
ADDED
data/lib/gsm/bundle.rb
ADDED
data/lib/gsm/gem.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
module Gsm
|
2
|
+
class Gem
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
attr_reader :conf_path, :name_pivot
|
6
|
+
attr_reader :sources
|
7
|
+
attr_reader :use_name
|
8
|
+
|
9
|
+
MAX_GENERATED_NAMES = 11
|
10
|
+
GEMSTONE_NAMES = [
|
11
|
+
"Amethyst",
|
12
|
+
"Emerald",
|
13
|
+
"Chrysocolla",
|
14
|
+
"Hematite",
|
15
|
+
"Jasper",
|
16
|
+
"Malachite",
|
17
|
+
"Quartz",
|
18
|
+
"Ruby",
|
19
|
+
"Sapphire",
|
20
|
+
"Sugilite",
|
21
|
+
"Turquoise"
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
def initialize(conf_path)
|
25
|
+
@sources = Hash.new
|
26
|
+
@use_name = ""
|
27
|
+
@name_pivot = 0
|
28
|
+
@conf_path = conf_path
|
29
|
+
|
30
|
+
if !load_from_yaml?
|
31
|
+
# load from `gem sources`
|
32
|
+
load
|
33
|
+
end
|
34
|
+
save
|
35
|
+
end
|
36
|
+
|
37
|
+
def load(mock = nil)
|
38
|
+
if mock != nil
|
39
|
+
outputs = mock
|
40
|
+
else
|
41
|
+
outputs = `gem sources -l`
|
42
|
+
end
|
43
|
+
outputs.split("\n").each do |line|
|
44
|
+
line.strip!
|
45
|
+
if validate_url?(line)
|
46
|
+
if @sources.length >= MAX_GENERATED_NAMES
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
name = generate_name
|
50
|
+
@sources[name] = line
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add(name, url)
|
56
|
+
return false if validate_name?(name)
|
57
|
+
|
58
|
+
# validate url
|
59
|
+
if !validate_url?(url)
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
|
63
|
+
@sources[name] = url
|
64
|
+
save
|
65
|
+
|
66
|
+
name
|
67
|
+
end
|
68
|
+
|
69
|
+
def del(name)
|
70
|
+
return false if @use_name.eql?(name)
|
71
|
+
return nil if !@sources.has_key?(name)
|
72
|
+
|
73
|
+
@sources.delete(name)
|
74
|
+
save
|
75
|
+
|
76
|
+
name
|
77
|
+
end
|
78
|
+
|
79
|
+
def get
|
80
|
+
@use_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset
|
84
|
+
@use_name.clear
|
85
|
+
@sources.clear
|
86
|
+
save
|
87
|
+
end
|
88
|
+
|
89
|
+
def use(name)
|
90
|
+
return false if !@sources.has_key?(name)
|
91
|
+
return @use_name if @use_name.eql?(name)
|
92
|
+
|
93
|
+
if !apply_source(name)
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
|
97
|
+
@use_name = name
|
98
|
+
save
|
99
|
+
|
100
|
+
@use_name
|
101
|
+
end
|
102
|
+
|
103
|
+
def save
|
104
|
+
data = {
|
105
|
+
:use => @use_name,
|
106
|
+
:sources => @sources
|
107
|
+
}
|
108
|
+
# write to file
|
109
|
+
f = File.new(@conf_path, "w")
|
110
|
+
f.syswrite(data.to_yaml)
|
111
|
+
f.close
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_s
|
115
|
+
return @sources[@use_name] if @sources.has_key?(@use_name)
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
def apply_source(name)
|
120
|
+
outputs = `gem sources -l`
|
121
|
+
outputs.split("\n").each do |line|
|
122
|
+
line.strip!
|
123
|
+
if validate_url?(line)
|
124
|
+
`gem sources --remove #{line}`
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
url = @sources[name]
|
129
|
+
output = `gem sources --add #{url}`
|
130
|
+
output.include?("#{url} added to sources")
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
def validate_name?(name)
|
135
|
+
return false if %r{[a-zA-Z]{1,32}*} =~ name
|
136
|
+
return false if @sources.has_key?(name)
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def validate_url?(url)
|
142
|
+
url.start_with?("http://") || url.start_with?("https://") ? true : false
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
def load_from_yaml?
|
147
|
+
# read yaml
|
148
|
+
if File.exist?(@conf_path)
|
149
|
+
data = YAML.load_file(@conf_path)
|
150
|
+
@use_name = data[:use] if data.has_key?(:use)
|
151
|
+
@sources = data[:sources] if data.has_key?(:sources)
|
152
|
+
true
|
153
|
+
else
|
154
|
+
f = File.new(@conf_path, "w")
|
155
|
+
f.close
|
156
|
+
false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
def generate_name
|
162
|
+
gen_name = ""
|
163
|
+
|
164
|
+
while @name_pivot < MAX_GENERATED_NAMES
|
165
|
+
gen_name = GEMSTONE_NAMES[@name_pivot].to_s
|
166
|
+
|
167
|
+
@name_pivot = @name_pivot + 1
|
168
|
+
|
169
|
+
if !@sources.has_key?(gen_name)
|
170
|
+
return gen_name
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|