gsm-sources-manager 1.0.0.alpha3
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 +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
|