osdn-cli 0.0.1
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/exe/osdn +5 -0
- data/lib/osdn/cli/command/frs_mkdirs.rb +70 -0
- data/lib/osdn/cli/command/frs_upload.rb +128 -0
- data/lib/osdn/cli/command/login.rb +63 -0
- data/lib/osdn/cli/command/package.rb +118 -0
- data/lib/osdn/cli/common.rb +6 -0
- data/lib/osdn/cli/runner.rb +115 -0
- data/lib/osdn/cli/version.rb +5 -0
- data/lib/osdn/cli.rb +219 -0
- data/lib/osdn-cli.rb +1 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3376886c75658394fda1f814ebdfe379058ae405
|
4
|
+
data.tar.gz: d7c42b6e4e49cf484bdf5f26e6d9a937e89dcda6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 294713c7170970f465d9cfffda467b81eee659666ad1dcecc418e139e61800e626cbdaa724e5e918c20a128fb0025ab85987470c71b06749701f497f35c7d630
|
7
|
+
data.tar.gz: 5ce41536e44564b935bb6a87dbb38d3558836fa2b915c935f98a78807fc5bc548a60f4499898948e89c84dc2457cfa9319e8c91dceb1923e8e6dd97c66a2bdf0
|
data/exe/osdn
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module OSDN; module CLI; module Command
|
5
|
+
class FrsMkdirs < Base
|
6
|
+
def help
|
7
|
+
puts "#{$0} frs_mkdirs [opts] [target_dir]"
|
8
|
+
puts "Options:"
|
9
|
+
puts " -p --project=<project> Target project (numeric id or name)"
|
10
|
+
#puts " --package=<package_id> Target package (numeric id)"
|
11
|
+
#puts " --release=<release_id> Target release (numeric id)"
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
update_token
|
16
|
+
opts = GetoptLong.new(
|
17
|
+
[ '--dry-run', '-n', GetoptLong::NO_ARGUMENT ],
|
18
|
+
[ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
|
19
|
+
)
|
20
|
+
opts.each do |opt, arg|
|
21
|
+
case opt
|
22
|
+
when '--project'
|
23
|
+
arg.empty? or
|
24
|
+
@target_proj = arg
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
proj_info = api.get_project target_proj # check project existance
|
29
|
+
|
30
|
+
target_dir = Pathname.new(ARGV.shift || '.')
|
31
|
+
FileUtils.mkdir_p target_dir
|
32
|
+
update_variables target_dir, project: target_proj
|
33
|
+
|
34
|
+
logger.debug "Getting Package list..."
|
35
|
+
packages = api.list_packages target_proj
|
36
|
+
logger.debug "Making each package directry"
|
37
|
+
|
38
|
+
packages.each do |package|
|
39
|
+
logger.info "Making directory for package #{package.name}"
|
40
|
+
pdir = target_dir + package.name
|
41
|
+
FileUtils.mkdir_p(pdir)
|
42
|
+
update_variables pdir, package_id: package.id
|
43
|
+
package.releases.each do |release|
|
44
|
+
logger.info "Making directory for release #{release.name}"
|
45
|
+
rdir = pdir + release.name
|
46
|
+
FileUtils.mkdir_p(rdir)
|
47
|
+
update_variables rdir, release_id: release.id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.description
|
53
|
+
"Make directory tree for current project release"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def target_proj
|
58
|
+
@target_proj and return @target_proj
|
59
|
+
vars = load_variables
|
60
|
+
vars.project && !vars.project.empty? and
|
61
|
+
return vars.project
|
62
|
+
logger.fatal "No target project is specified."
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
|
66
|
+
def api
|
67
|
+
OSDNClient::ProjectApi.new
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end; end; end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module OSDN; module CLI; module Command
|
2
|
+
class FrsUpload < Base
|
3
|
+
def help
|
4
|
+
puts "#{$0} frs_upload [opts] [target_dir]"
|
5
|
+
puts "Options:"
|
6
|
+
puts " -n --dry-run Do noting (use with global -v to inspect)"
|
7
|
+
puts " -p --project=<project> Target project (numeric id or name)"
|
8
|
+
#puts " --package=<project> Target package (numeric id)"
|
9
|
+
#puts " --release=<project> Target release (numeric id)"
|
10
|
+
puts " -v --visibility=<public|private|hidden>"
|
11
|
+
puts " Default visibility for newly created items"
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
update_token
|
16
|
+
opts = GetoptLong.new(
|
17
|
+
[ '--dry-run', '-n', GetoptLong::NO_ARGUMENT ],
|
18
|
+
[ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
|
19
|
+
[ '--release', '-r', GetoptLong::REQUIRED_ARGUMENT ],
|
20
|
+
[ '--visibility', '-v', GetoptLong::REQUIRED_ARGUMENT ],
|
21
|
+
)
|
22
|
+
opts.each do |opt, arg|
|
23
|
+
case opt
|
24
|
+
when '--project'
|
25
|
+
arg.empty? or
|
26
|
+
@target_proj = arg
|
27
|
+
#when '--release'
|
28
|
+
# arg.empty? or
|
29
|
+
# @target_release = arg
|
30
|
+
#when '--package'
|
31
|
+
# arg.empty? or
|
32
|
+
# @target_package = arg
|
33
|
+
when '--visibility'
|
34
|
+
unless %w(public private hidden).member?(arg)
|
35
|
+
logger.fatal "Invalid visibility status: #{arg}"
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
@visibility = arg
|
39
|
+
when '--dry-run'
|
40
|
+
@dry_run = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@target_dir = Pathname.new(ARGV.shift || '.')
|
45
|
+
proj_info = api.get_project target_proj # check project existance
|
46
|
+
|
47
|
+
Pathname.glob(@target_dir+'*').each do |pdir|
|
48
|
+
unless load_variables(pdir).package_id
|
49
|
+
logger.info "Createing new package '#{pdir.basename}'"
|
50
|
+
pinfo = api.create_package target_proj, pdir.basename, visibility: @visibility
|
51
|
+
update_variables pdir, package_id: pinfo.id
|
52
|
+
$stdout.puts "New package '#{pinfo.name}' has been created; #{pinfo.url}"
|
53
|
+
end
|
54
|
+
|
55
|
+
Pathname.glob(pdir + '*').each do |rdir|
|
56
|
+
vars = load_variables(rdir)
|
57
|
+
rinfo = nil
|
58
|
+
if vars.release_id
|
59
|
+
rinfo = api.get_release target_proj, target_package(rdir), target_release(rdir)
|
60
|
+
else vars.release_id
|
61
|
+
logger.info "Createing new release '#{rdir.basename}'"
|
62
|
+
rinfo = nil
|
63
|
+
if api.respond_to? :create_reelase # TODO: remove, just typo...
|
64
|
+
rinfo = api.create_reelase target_proj, target_package(rdir), rdir.basename, visibility: @visibility
|
65
|
+
else
|
66
|
+
rinfo = api.create_release target_proj, target_package(rdir), rdir.basename, visibility: @visibility
|
67
|
+
end
|
68
|
+
update_variables rdir, release_id: rinfo.id
|
69
|
+
$stdout.puts "New release '#{rinfo.name}' has been created; #{rinfo.url}"
|
70
|
+
end
|
71
|
+
|
72
|
+
Pathname.glob(rdir + '*').each do |file|
|
73
|
+
if file.directory?
|
74
|
+
logger.error "Skip direcotry #{file}"
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
if rinfo.files.find { |f| f.name == file.basename.to_s }
|
79
|
+
logger.warn "Skip already uploaded file '#{file}'"
|
80
|
+
else
|
81
|
+
logger.info "Uploading file #{file} (#{file.size} bytes)"
|
82
|
+
# TODO: show progress bar!
|
83
|
+
finfo = api.create_release_file target_proj, target_package(rdir), target_release(rdir), file.open, visibility: @visibility
|
84
|
+
logger.info "Upload completed."
|
85
|
+
$stdout.puts "New file '#{file}' has been uploaded; #{finfo.url}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.description
|
93
|
+
"Upload local file tree and create package/release implicitly."
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def target_proj
|
98
|
+
@target_proj and return @target_proj
|
99
|
+
vars = load_variables(@target_dir)
|
100
|
+
vars.project && !vars.project.empty? and
|
101
|
+
return vars.project
|
102
|
+
logger.fatal "No target project is specified."
|
103
|
+
exit
|
104
|
+
end
|
105
|
+
|
106
|
+
def target_package(dir)
|
107
|
+
@target_package and return @target_package
|
108
|
+
vars = load_variables(dir)
|
109
|
+
vars.package_id && !vars.package_id.to_s.empty? and
|
110
|
+
return vars.package_id
|
111
|
+
logger.fatal "No target package is specified."
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
|
115
|
+
def target_release(dir)
|
116
|
+
@target_release and return @target_release
|
117
|
+
vars = load_variables(dir)
|
118
|
+
vars.release_id && !vars.release_id.to_s.empty? and
|
119
|
+
return vars.release_id
|
120
|
+
logger.fatal "No target release is specified."
|
121
|
+
exit
|
122
|
+
end
|
123
|
+
|
124
|
+
def api
|
125
|
+
OSDNClient::ProjectApi.new
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end; end; end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module OSDN; module CLI; module Command
|
2
|
+
class Login < Base
|
3
|
+
def run
|
4
|
+
logger.debug "Trying login"
|
5
|
+
scope = %w(profile group group_write)
|
6
|
+
|
7
|
+
auth_url = "https://osdn.jp/account/oauth2ui/authorize?client_id=#{CLI.client_id}&state=cli#{Time.now.to_i}&response_type=code&scope=#{scope.join('%20')}"
|
8
|
+
|
9
|
+
launch_brwoser auth_url
|
10
|
+
puts
|
11
|
+
authcode = prompt("Type your auth code: ")
|
12
|
+
puts
|
13
|
+
if authcode.empty?
|
14
|
+
logger.error "Empty auth code, login has been canceled."
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
api = OSDNClient::DefaultApi.new
|
19
|
+
begin
|
20
|
+
set_credential api.token(CLI.client_id, CLI.client_secret, code: authcode)
|
21
|
+
rescue OSDNClient::ApiError => e
|
22
|
+
begin
|
23
|
+
err = JSON.parse(e.response_body)
|
24
|
+
logger.fatal err["error_description"]
|
25
|
+
rescue
|
26
|
+
logger.fatal "Failed to get access token"
|
27
|
+
end
|
28
|
+
return
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def launch_brwoser(url)
|
33
|
+
puts "Access follwoing URL to get auth code;\n#{url}"
|
34
|
+
%w(/usr/bin/xdg-open /usr/bin/X11/xdg-open /usr/local/bin/xdg-open
|
35
|
+
/usr/bin/x-www-browser /usr/bin/firefox /usr/local/bin/firefox
|
36
|
+
).each do |bin|
|
37
|
+
File.executable?(bin) or next
|
38
|
+
exec(bin, url) if fork.nil?
|
39
|
+
return
|
40
|
+
end
|
41
|
+
case RUBY_PLATFORM
|
42
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
43
|
+
exec("start #{url}") if fork.nil?
|
44
|
+
when /darwin|mac os/
|
45
|
+
exec("/usr/bin/open", url) if fork.nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def prompt(msg = "", newline = false)
|
50
|
+
require 'readline'
|
51
|
+
msg += "\n" if newline
|
52
|
+
Readline.readline(msg, true).to_s.squeeze(" ").strip
|
53
|
+
end
|
54
|
+
|
55
|
+
def help
|
56
|
+
puts "#{$0} login"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.description
|
60
|
+
"Login and save access token."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end; end; end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module OSDN; module CLI; module Command
|
2
|
+
class Package < Base
|
3
|
+
def self.help
|
4
|
+
puts "#{$0} package [opts] [list]"
|
5
|
+
puts "#{$0} package [opts] create <new-package-name>"
|
6
|
+
puts "#{$0} package [opts] update <numeric-package-id> [name]"
|
7
|
+
puts "#{$0} package [opts] delete <numeric-package-id>"
|
8
|
+
puts "Options:"
|
9
|
+
puts " -f --format=<pretty|json> Set output format"
|
10
|
+
puts " -p --project=<project> Target project (numeric id or name)"
|
11
|
+
puts " -v --visibility=<public|private|hidden>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.description
|
15
|
+
"Manipulate release package of project"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
update_token
|
20
|
+
opts = GetoptLong.new(
|
21
|
+
[ '--format', '-f', GetoptLong::REQUIRED_ARGUMENT ],
|
22
|
+
[ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
|
23
|
+
[ '--visibility', '-v', GetoptLong::REQUIRED_ARGUMENT ],
|
24
|
+
)
|
25
|
+
opts.each do |opt, arg|
|
26
|
+
case opt
|
27
|
+
when '--format'
|
28
|
+
arg == 'json' and
|
29
|
+
self.format = arg
|
30
|
+
when '--project'
|
31
|
+
arg.empty? or
|
32
|
+
@target_proj = arg
|
33
|
+
when '--visibility'
|
34
|
+
unless %w(public private hidden).member?(arg)
|
35
|
+
logger.fatal "Invalid visibility status: #{arg}"
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
@visibility = arg
|
39
|
+
end
|
40
|
+
end
|
41
|
+
command = ARGV.shift || 'list'
|
42
|
+
if !command || command.empty?
|
43
|
+
logger.fatal "subcommand is missing."
|
44
|
+
help
|
45
|
+
return
|
46
|
+
end
|
47
|
+
if self.respond_to? command
|
48
|
+
self.send command
|
49
|
+
else
|
50
|
+
logger.fatal "Invalid subcommand: #{command}"
|
51
|
+
help
|
52
|
+
return
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def list
|
57
|
+
list = api.list_packages target_proj
|
58
|
+
if format == 'json'
|
59
|
+
puts list.to_json
|
60
|
+
else
|
61
|
+
list.each do |p|
|
62
|
+
puts format_package(p)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def create
|
68
|
+
name = ARGV.shift
|
69
|
+
if !name
|
70
|
+
logger.fatal "Package name is missing."
|
71
|
+
help
|
72
|
+
return
|
73
|
+
end
|
74
|
+
p = api.create_package target_proj, name, visibility: @visibility
|
75
|
+
logger.info "New package has been created."
|
76
|
+
puts format_package(p)
|
77
|
+
end
|
78
|
+
|
79
|
+
def update
|
80
|
+
target_id = ARGV.shift
|
81
|
+
args = {name: ARGV.shift}
|
82
|
+
if @visibility
|
83
|
+
args[:visibility] = @visibility
|
84
|
+
end
|
85
|
+
p = api.update_package target_proj, target_id, args
|
86
|
+
logger.info "Package #{target_id} has been updated."
|
87
|
+
puts format_package(p)
|
88
|
+
end
|
89
|
+
|
90
|
+
def delete
|
91
|
+
target_id = ARGV.shift
|
92
|
+
p = api.delete_package target_proj, target_id
|
93
|
+
logger.info "Package #{target_id} has been deleted."
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def target_proj
|
98
|
+
@target_proj and return @target_proj
|
99
|
+
vars = load_variables
|
100
|
+
vars.project && !vars.project.empty? and
|
101
|
+
return vars.project
|
102
|
+
logger.fatal "No target project is specified."
|
103
|
+
exit
|
104
|
+
end
|
105
|
+
|
106
|
+
def api
|
107
|
+
OSDNClient::ProjectApi.new
|
108
|
+
end
|
109
|
+
|
110
|
+
def format_package(p)
|
111
|
+
if format == 'json'
|
112
|
+
p.to_json
|
113
|
+
else
|
114
|
+
"##{p.id} #{p.name} (#{p.visibility}, #{[*p.releases].count} releases)\n #{p.url}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end; end; end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'getoptlong'
|
2
|
+
require 'logger'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
module OSDN
|
6
|
+
module CLI
|
7
|
+
class Runner
|
8
|
+
def initialize
|
9
|
+
@logger = Logger.new(STDERR)
|
10
|
+
@logger.level = Logger::WARN
|
11
|
+
@logger.formatter = proc { |severity, time, progname, msg|
|
12
|
+
"[%s] %s\n" % [severity, msg]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
attr_reader :logger
|
16
|
+
|
17
|
+
def parse_opt
|
18
|
+
opts = GetoptLong.new(
|
19
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
20
|
+
[ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
|
21
|
+
[ '--quiet', '-q', GetoptLong::NO_ARGUMENT ],
|
22
|
+
)
|
23
|
+
opts.ordering = GetoptLong::REQUIRE_ORDER
|
24
|
+
opts.each do |opt, arg|
|
25
|
+
case opt
|
26
|
+
when '--help'
|
27
|
+
help
|
28
|
+
exit 0
|
29
|
+
when '--verbose'
|
30
|
+
if logger.level == Logger::DEBUG
|
31
|
+
OSDNClient.configure do |config|
|
32
|
+
config.debugging = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
logger.level > Logger::DEBUG and
|
36
|
+
logger.level -= 1
|
37
|
+
when '--quiet'
|
38
|
+
logger.level < Logger::UNKNOWN and
|
39
|
+
logger.level += 1
|
40
|
+
when '--help'
|
41
|
+
help
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
logger.debug "Loglevel is #{logger.level}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_command_class(command_name)
|
49
|
+
class_name = command_name.to_s.split('_').map(&:capitalize).join
|
50
|
+
begin
|
51
|
+
return self.class.const_get("OSDN::CLI::Command::#{class_name}")
|
52
|
+
rescue NameError => e
|
53
|
+
logger.fatal "Invalid command name '#{command_name}'. Use 'help' to list commands."
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def run
|
60
|
+
parse_opt
|
61
|
+
|
62
|
+
command_name = ARGV.shift
|
63
|
+
unless command_name
|
64
|
+
help
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
if command_name == 'help'
|
69
|
+
help
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
command = get_command_class(command_name).new(logger)
|
74
|
+
logger.debug "Run command #{command_name}"
|
75
|
+
begin
|
76
|
+
command.run
|
77
|
+
rescue OSDNClient::ApiError => e
|
78
|
+
begin
|
79
|
+
err = JSON.parse(e.response_body)
|
80
|
+
if err["message"]
|
81
|
+
logger.fatal "#{err["status"]}: #{err["message"]}"
|
82
|
+
elsif err["error_description"]
|
83
|
+
logger.fatal err["error_description"]
|
84
|
+
else
|
85
|
+
logger.fatal "Command failed: #{e.inspect}"
|
86
|
+
end
|
87
|
+
rescue
|
88
|
+
logger.fatal "Command failed: #{e.inspect}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def help
|
94
|
+
command_name = ARGV.shift
|
95
|
+
if command_name
|
96
|
+
get_command_class(command_name).new(logger).help
|
97
|
+
else
|
98
|
+
puts "#{$0} [global-options] <command> [command-options] [args]"
|
99
|
+
puts "#{$0} help <command>"
|
100
|
+
puts "Global Options:"
|
101
|
+
puts " -h --help Show help message. use 'help <command>' for specific command. "
|
102
|
+
puts " -v --verbose Increase log level (multiple)"
|
103
|
+
puts " -q --quiet Decrease log level (multiple)"
|
104
|
+
puts "Avaiable Commands:"
|
105
|
+
puts " help"
|
106
|
+
OSDN::CLI::Command.constants.each do |c|
|
107
|
+
c = c.to_s.split(/(?=[A-Z])/).join('_').downcase
|
108
|
+
c == 'base' and next
|
109
|
+
puts " %-14s %s" % [c, get_command_class(c).description]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/osdn/cli.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
require "osdn/cli/version"
|
2
|
+
require "osdn/cli/runner"
|
3
|
+
require 'getoptlong'
|
4
|
+
require 'logger'
|
5
|
+
require 'pathname'
|
6
|
+
require 'yaml'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'osdn-client'
|
9
|
+
require 'hashie'
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
module OSDN
|
13
|
+
module CLI
|
14
|
+
@@client_id = "osdn-cli"
|
15
|
+
@@client_secret = "not-secret"
|
16
|
+
def client_id
|
17
|
+
@@client_id
|
18
|
+
end
|
19
|
+
def client_secret
|
20
|
+
@@client_secret
|
21
|
+
end
|
22
|
+
module_function :client_id, :client_secret
|
23
|
+
|
24
|
+
module Command
|
25
|
+
autoload :Login, 'osdn/cli/command/login'
|
26
|
+
autoload :Package, 'osdn/cli/command/package'
|
27
|
+
#autoload :Release, 'osdn/cli/command/release'
|
28
|
+
#autoload :FrsFile, 'osdn/cli/command/frs_file'
|
29
|
+
autoload :FrsMkdirs, 'osdn/cli/command/frs_mkdirs'
|
30
|
+
autoload :FrsUpload, 'osdn/cli/command/frs_upload'
|
31
|
+
|
32
|
+
class Base
|
33
|
+
def initialize(logger)
|
34
|
+
@logger = logger
|
35
|
+
@credential = Hashie::Mash.new
|
36
|
+
@format = 'pretty'
|
37
|
+
end
|
38
|
+
attr_reader :logger
|
39
|
+
attr_accessor :credential, :format
|
40
|
+
|
41
|
+
def credential_path
|
42
|
+
Pathname.new(ENV['HOME']) + '.config/osdn/credential.yml'
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_credential
|
46
|
+
begin
|
47
|
+
stat = credential_path.stat()
|
48
|
+
unless credential_path.owned?
|
49
|
+
logger.error "Invalid ownership of credential file #{credential_path}, skip loading."
|
50
|
+
return
|
51
|
+
end
|
52
|
+
unless (stat.mode & 0777).to_s(8) == "600"
|
53
|
+
logger.error "Invalid permission #{(stat.mode & 0777).to_s(8)} of credential file #{credential_path}, skip loading."
|
54
|
+
return
|
55
|
+
end
|
56
|
+
rescue Errno::ENOENT
|
57
|
+
return
|
58
|
+
end
|
59
|
+
logger.debug "Loading credentials from #{credential_path}"
|
60
|
+
@credential = Hashie::Mash.new(YAML.load_file(credential_path))
|
61
|
+
set_client_token
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_credential
|
65
|
+
FileUtils.mkdir_p credential_path.dirname, verbose: false
|
66
|
+
cio = credential_path.open('w', 0600)
|
67
|
+
YAML.dump(credential.to_hash, cio)
|
68
|
+
cio.close
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_token
|
72
|
+
logger.debug "Checking token expires..."
|
73
|
+
load_credential
|
74
|
+
if credential.expires_at > Time.now + 30
|
75
|
+
logger.debug "You have valid access token, skip to refresh."
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
logger.debug "Access token has been expired. Refresh access token..."
|
80
|
+
api = OSDNClient::DefaultApi.new
|
81
|
+
begin
|
82
|
+
set_credential api.token(CLI.client_id, CLI.client_secret, grant_type: 'refresh_token', refresh_token: credential.refresh_token)
|
83
|
+
rescue OSDNClient::ApiError => e
|
84
|
+
begin
|
85
|
+
err = JSON.parse(e.response_body)
|
86
|
+
logger.fatal err["error_description"]
|
87
|
+
rescue
|
88
|
+
logger.fatal "Failed to refresh access token."
|
89
|
+
end
|
90
|
+
logger.fatal "Please login again."
|
91
|
+
return
|
92
|
+
end
|
93
|
+
logger.debug "Access token refreshed successfully."
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_credential(token, update_expires = true)
|
97
|
+
token = Hashie::Mash.new(token.to_hash)
|
98
|
+
if update_expires
|
99
|
+
token.expires_at = Time.now + token.expires_in.to_i
|
100
|
+
end
|
101
|
+
token.scope = [*token.scope].join(' ').split(' ')
|
102
|
+
|
103
|
+
credential.update token
|
104
|
+
write_credential
|
105
|
+
set_client_token
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_client_token
|
109
|
+
if credential.access_token && !credential.access_token.empty?
|
110
|
+
OSDNClient.configure do |config|
|
111
|
+
config.access_token = credential.access_token
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def load_variables(path = '.', recursive_merge = true)
|
117
|
+
vars = {}
|
118
|
+
path = Pathname.new(Dir.getwd) + path
|
119
|
+
cur_dir = Pathname.new('/')
|
120
|
+
if recursive_merge
|
121
|
+
path.each_filename do |d|
|
122
|
+
cur_dir = cur_dir + d
|
123
|
+
vf = cur_dir + '.osdn.vars'
|
124
|
+
vf.exist? or next
|
125
|
+
begin
|
126
|
+
logger.debug "Load and merge variables from #{vf}"
|
127
|
+
vars.update YAML.load_file(vf.to_s)
|
128
|
+
rescue => e
|
129
|
+
logger.warn "Failed to load variables from #{vf}; #{e.message}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
else
|
133
|
+
begin
|
134
|
+
path = path+'.osdn.vars'
|
135
|
+
if path.exist?
|
136
|
+
logger.debug "Load and merge variables from #{path}"
|
137
|
+
vars.update YAML.load_file(path)
|
138
|
+
end
|
139
|
+
rescue => e
|
140
|
+
logger.warn "Failed to load variables from #{path}; #{e.message}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
logger.debug "Variables: #{vars.inspect}"
|
144
|
+
Hashie::Mash.new(vars)
|
145
|
+
end
|
146
|
+
|
147
|
+
def write_variables(vars, dir = nil)
|
148
|
+
path = Pathname.new(dir || '.') + '.osdn.vars'
|
149
|
+
logger.info "Save variables to #{path}"
|
150
|
+
vio = path.open('w')
|
151
|
+
YAML.dump(vars.to_hash, vio)
|
152
|
+
vio.close
|
153
|
+
end
|
154
|
+
|
155
|
+
def update_variables(dir, vars)
|
156
|
+
write_variables(load_variables(dir, false).merge(vars), dir)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class Ping < Base
|
161
|
+
def run
|
162
|
+
update_token
|
163
|
+
api = OSDNClient::DefaultApi.new
|
164
|
+
pp api.ping
|
165
|
+
end
|
166
|
+
|
167
|
+
def help
|
168
|
+
puts "#{$0} ping"
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.description
|
172
|
+
"Test API request."
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class Vars < Base
|
177
|
+
def run
|
178
|
+
subcommand = ARGV.shift ||'show'
|
179
|
+
self.send subcommand
|
180
|
+
end
|
181
|
+
|
182
|
+
def show
|
183
|
+
name = ARGV.shift
|
184
|
+
if name
|
185
|
+
puts load_variables[name]
|
186
|
+
else
|
187
|
+
pp load_variables
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def set
|
192
|
+
name, value = ARGV.shift, ARGV.shift
|
193
|
+
if !name || name.empty?
|
194
|
+
logger.fatal "Missing variable name"
|
195
|
+
help
|
196
|
+
exit
|
197
|
+
end
|
198
|
+
if !value || value.empty?
|
199
|
+
logger.fatal "Missing variable value"
|
200
|
+
help
|
201
|
+
exit
|
202
|
+
end
|
203
|
+
vars = load_variables('.', false)
|
204
|
+
vars[name] = value
|
205
|
+
write_variables vars
|
206
|
+
end
|
207
|
+
|
208
|
+
def help
|
209
|
+
puts "#{$0} vars show [name] -- Show current variable"
|
210
|
+
puts "#{$0} vars set <name> <value> -- Save variable to .osdn.vars"
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.description
|
214
|
+
"Get/set request environment variable."
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
data/lib/osdn-cli.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'osdn/cli'
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: osdn-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- OSDN
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: osdn-client
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.0.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.0.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hashie
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Non-intaractive manipulation tool for OSDN
|
84
|
+
email:
|
85
|
+
- admin@osdn.jp
|
86
|
+
executables:
|
87
|
+
- osdn
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- exe/osdn
|
92
|
+
- lib/osdn-cli.rb
|
93
|
+
- lib/osdn/cli.rb
|
94
|
+
- lib/osdn/cli/command/frs_mkdirs.rb
|
95
|
+
- lib/osdn/cli/command/frs_upload.rb
|
96
|
+
- lib/osdn/cli/command/login.rb
|
97
|
+
- lib/osdn/cli/command/package.rb
|
98
|
+
- lib/osdn/cli/common.rb
|
99
|
+
- lib/osdn/cli/runner.rb
|
100
|
+
- lib/osdn/cli/version.rb
|
101
|
+
homepage: https://osdn.jp/projects/osdn-codes/wiki/CommandLineInterface
|
102
|
+
licenses: []
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.5.1
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: OSDN Command Line Interface
|
124
|
+
test_files: []
|