fir-cli 0.0.7 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 57e26467b901310219bb57a2b199da8f27aff22e
4
- data.tar.gz: 9acdc07d57db5c388e9cc7b7aaff1123d4ce25e2
3
+ metadata.gz: 3954cf53014a8df835ab179b86ab8d2db19f88ac
4
+ data.tar.gz: eb30457b5e3eae90ad3ec155aedebe7b5735c9f6
5
5
  SHA512:
6
- metadata.gz: d70acc52ac8ef678d5f8ed17d4760b5a5b201fa721796412cbc0ada45fdd45cfb85ad0e886c61051d6328fc7ddbc3b6f66ab1554b6ec9ad583b5df58b602f25e
7
- data.tar.gz: a8ed609c3a197976fb6c4d102e1908bc2cc2711febfaacd12e90d1eedab4092670db2b31afa3106964cdd6170a76aeb1a36ea5bad9de433c54c8dcb96f8d061b
6
+ metadata.gz: e21a7166998730abcd305a041c379a0f9749756b8d8f4dae21a7b7ac01628bbe39576f6712c3eb008b7a6fa78657bdc34c3be0e217e1e55831db1d0ef3133a0a
7
+ data.tar.gz: 4120ab1cecace7ed4f098399302589ff7cb1938dcf4a9430a89cee112ba66321d7935f477fe23d73f376b5153969c796661161225c15dd9d0f8c7f9cd1a5ae66
data/.gitignore ADDED
@@ -0,0 +1,85 @@
1
+ # Created by https://www.gitignore.io
2
+
3
+ ### Ruby ###
4
+ *.gem
5
+ *.rbc
6
+ /.config
7
+ /coverage/
8
+ /InstalledFiles
9
+ /pkg/
10
+ /spec/reports/
11
+ /test/tmp/
12
+ /test/version_tmp/
13
+ /tmp/
14
+
15
+ ## Specific to RubyMotion:
16
+ .dat*
17
+ .repl_history
18
+ build/
19
+
20
+ ## Documentation cache and generated files:
21
+ /.yardoc/
22
+ /_yardoc/
23
+ /doc/
24
+ /rdoc/
25
+
26
+ ## Environment normalisation:
27
+ /.bundle/
28
+ /lib/bundler/man/
29
+
30
+ # for a library or gem, you might want to ignore these files since the code is
31
+ # intended to run in multiple environments; otherwise, check them in:
32
+ # Gemfile.lock
33
+ # .ruby-version
34
+ # .ruby-gemset
35
+
36
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
37
+ .rvmrc
38
+
39
+
40
+ ### Linux ###
41
+ *~
42
+
43
+ # KDE directory preferences
44
+ .directory
45
+
46
+
47
+ ### OSX ###
48
+ .DS_Store
49
+ .AppleDouble
50
+ .LSOverride
51
+
52
+ # Icon must end with two \r
53
+ Icon
54
+
55
+ # Thumbnails
56
+ ._*
57
+
58
+ # Files that might appear on external disk
59
+ .Spotlight-V100
60
+ .Trashes
61
+
62
+ # Directories potentially created on remote AFP share
63
+ .AppleDB
64
+ .AppleDesktop
65
+ Network Trash Folder
66
+ Temporary Items
67
+ .apdisk
68
+
69
+
70
+ ### SublimeText ###
71
+ # cache files for sublime text
72
+ *.tmlanguage.cache
73
+ *.tmPreferences.cache
74
+ *.stTheme.cache
75
+
76
+ # workspace files are user-specific
77
+ *.sublime-workspace
78
+
79
+ # project files should be checked into the repository, unless a significant
80
+ # proportion of contributors will probably not be using SublimeText
81
+ # *.sublime-project
82
+
83
+ # sftp configuration file
84
+ sftp-config.json
85
+
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://ruby.taobao.org/'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fir-cli (0.1.0)
5
+ lagunitas (= 0.0.1)
6
+ paint (~> 0.9.0)
7
+ pngdefry (= 0.1.1)
8
+ rest_client (~> 1.8.2)
9
+ ruby_apk (~> 0.7.1)
10
+ thor (~> 0.19.1)
11
+ user_config (= 0.0.4)
12
+
13
+ GEM
14
+ remote: https://ruby.taobao.org/
15
+ specs:
16
+ CFPropertyList (2.2.8)
17
+ lagunitas (0.0.1)
18
+ CFPropertyList
19
+ pngdefry (>= 0.1.0)
20
+ zip
21
+ netrc (0.7.9)
22
+ paint (0.9.0)
23
+ pngdefry (0.1.1)
24
+ rest_client (1.8.2)
25
+ netrc (~> 0.7.7)
26
+ ruby_apk (0.7.1)
27
+ rubyzip (< 1.0.0)
28
+ rubyzip (0.9.9)
29
+ thor (0.19.1)
30
+ user_config (0.0.4)
31
+ zip (2.0.2)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ fir-cli!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 idy0013@gmail.com
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ FIR.im 命令行工具
2
+ ---
3
+ ###安装
4
+ ```shell
5
+ gem install fir-cli
6
+ ```
7
+ ###指令文档
8
+ ####帮助
9
+ > 以下指令可以获取帮助
10
+
11
+ ```shell
12
+ fir help
13
+ ```
14
+
15
+ > 以下指令获取具体指令的帮助
16
+
17
+ - `COMMAND`:具体的一个指令,如`publish`,`update`
18
+ ```shell
19
+ fir help COMMAND
20
+ ```
21
+
22
+ ####登录
23
+ > 以下指令通过用户`USER_TOKEN`登录,已登录用户可以在[这里](http://fir.im/user/info)上可以查看自己的令牌。
24
+
25
+ ```shell
26
+ fir login USER_TOKEN
27
+ ```
28
+
29
+ ####获取应用文件的信息
30
+ > 以下指令可以显示应用文件的信息,支持 ipa 和 apk 文件。
31
+
32
+ - `-v`:可选,显示更多信息
33
+ - `-f`:可选,显示托管在 [FIR.im](http://fir.im) 的信息
34
+ ```shell
35
+ fir info APP_FILE_PATH [-f] [-v]
36
+ ```
37
+
38
+ ####设置全局信息
39
+ > 以下指令可以设置全局信息.
40
+
41
+ - `-r`:可选,默认发布都会使用企业证书签名
42
+ - `-t TOKEN`:可选,设置登录用户的令牌(作用和`fir loging USER_TOKEN`一样)
43
+ - `-e EMAIL`:可选,设置用户使用企业签名服务的默认邮件地址
44
+ ```shell
45
+ fir config [-r] [-t TOKEN] [-e EMAIL]
46
+ ```
47
+
48
+ ####企业证书签名
49
+ > 以下指令使用 [resign.tapbeta.com](http://resign.tapbeta.com) 进行企业证书签名
50
+
51
+ - `INPUT_IPA_FILE`:待签名的 ipa 文件
52
+ - `OUTPUT_IPA_FILE`:签名后的输出文件
53
+ - `-e EMAIL`:可选,设置使用签名服务的邮件地址,不填则使用全局设置
54
+ ```shell
55
+ fir resign INTPUT_IPA_FILE OUTPUT_IPA_FILE [-e EMAIL]
56
+ ```
57
+
58
+ ####发布应用至 [FIR.im](http://fir.im)
59
+ > 以下指令将指定应用文件发布到 [FIR.im](http://fir.im),支持 ipa 和 apk 文件。
60
+
61
+ - `-r`:可选,设置此开关将首先企业签名,之后发布到 [FIR.im](http://fir.im)
62
+ - `-s SHORT`:可选,指定发布应用的短地址
63
+ - `-t USER_TOKEN`:可选,设定发布应用的帐号,未设置则使用全局设置
64
+ - `-c CHANGE_LOG`:可选,设置发布的发布日志
65
+ ```shell
66
+ fir publish APP_FILE_PATH [-r] [-s SHORT] [-t USER_TOKEN] [-c CHANGE_LOG]
67
+ ```
68
+
69
+
data/bin/fir CHANGED
@@ -1,3 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'fir-cli'
3
- FirCli.start ARGV
3
+
4
+ Fir::Cli.load_extends
5
+
6
+ # begin
7
+ Fir::Cli.start ARGV
8
+ # rescue Exception => e
9
+ # puts "! 错误[#{ e.errno }]: #{ Paint[e.to_s, :red] }"
10
+ # end
11
+
data/fir-cli.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'fir-cli'
7
+ s.version = '0.1.0'
8
+ s.date = '2014-11-20'
9
+ s.summary = 'FIR.im 命令行工具'
10
+ s.description = 'FIR.im 命令行工具,支持 ios 和 android'
11
+ s.homepage = 'http://blog.fir.im/2014/fir-cli'
12
+ s.author = 'FIR.im'
13
+ s.email = 'fir-cli@fir.im'
14
+ s.license = 'GPL'
15
+ s.executables << 'fir'
16
+ s.files = `git ls-files -z`.split("\x0")
17
+ s.require_paths = ['./']
18
+
19
+ s.add_dependency 'lagunitas', '0.0.1'
20
+ s.add_dependency 'user_config', '0.0.4'
21
+ s.add_dependency 'pngdefry', '0.1.1'
22
+ s.add_dependency 'rest_client', '~> 1.8.2'
23
+ s.add_dependency 'paint', '~> 0.9.0'
24
+ s.add_dependency 'thor', '~> 0.19.1'
25
+ s.add_dependency 'ruby_apk', '~> 0.7.1'
26
+ end
data/fir-cli.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'thor'
2
+ module Fir
3
+ class Cli < Thor
4
+ end
5
+ end
6
+
7
+ Dir[File.dirname(__FILE__) + '/lib/*.rb'].each {|f| require f }
8
+ Dir[File.dirname(__FILE__) + '/lib/fir-cli-commands/*.rb'].each {|f| require f }
@@ -0,0 +1,21 @@
1
+ module Fir
2
+ class Cli < Thor
3
+ desc 'config', '配置全局设置'
4
+ option :token, :aliases => '-t', :desc => '用户令牌'
5
+ option :email, :aliases => '-e', :desc => '邮件地址'
6
+ option :resign, :aliases => '-r', :desc => '是否以企业签名发布 ios 应用', :type => :boolean
7
+ option :verbose, :aliases => '-v', :desc => '输出级别项 v, vv, vvv', :type => :string, :enum => ['v', 'vv', 'vvv']
8
+ option :quiet, :aliases => '-q', :desc => '安静模式', :type => :boolean
9
+ def config
10
+ if options.length > 0
11
+ options.each do |conf|
12
+ puts "> #{ Paint[conf[0].to_s.rjust(10), :blue] } : #{ @config[conf[0].to_s] } => #{ conf[1] }"
13
+ @config[conf[0]] = conf[1]
14
+ end
15
+ @config.save
16
+ end
17
+ puts '> 当前设置:'
18
+ @config.each {|conf| puts "> #{ Paint[conf[0].to_s.rjust(10), :blue] } => #{ conf[1] }"}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Fir
2
+ class Cli < Thor
3
+ desc 'info APP_FILE_PATH', '获取应用文件的信息(支持 ipa 文件和 apk 文件)'
4
+ option :all, :aliases => '-a', :desc => '显示全部应用信息', :type => :boolean
5
+ option :fir, :aliases => '-f', :desc => '显示托管在 fir.im 的应用信息', :type => :boolean
6
+ option :verbose, :aliases => '-v', :desc => '设置输出级别 v, vv, vvv'
7
+ option :quiet, :aliases => '-q', :desc => '安静模式,不输出任何选项'
8
+ def info(path)
9
+ app = _info path, options[:all]
10
+ app.each { |i| puts "#{ Paint[i[0].to_s.rjust(18), :blue] } #{ i[1] }" }
11
+ if options[:fir]
12
+ fir_app = _fir_info app[:identifier], app[:type]
13
+ fir_app.each { |i| puts "#{ Paint[i[0].to_s.rjust(18), :blue] } #{ i[1] }" }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Fir
2
+ class Cli < Thor
3
+ desc 'login USER_TOKEN', '以 USER_TOKEN 身份登陆'
4
+ option :verbose, :aliases => '-v', :desc => '设置输出级别 v, vv, vvv'
5
+ option :quite, :aliases => '-q', :desc => '安静模式,不输出任何选项'
6
+ def login(token)
7
+ if _opt_token && _opt_token != token
8
+ _puts "> 已登陆用户: #{ _opt_token }"
9
+ _puts "> 替换为用户: #{ token }"
10
+ end
11
+ @config['token'] = token
12
+ @config.save
13
+ _puts "> 当前登陆用户为:#{ token }"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,66 @@
1
+ module Fir
2
+ class Cli < Thor
3
+ desc 'publish APP_FILE_PATH', '将应用文件发布至 FIR.im(支持 ipa 文件和 apk 文件)'
4
+ option :resign, :aliases => '-r', :desc => '进行企业签名', :type => :boolean
5
+ option :short, :aliases => '-s', :desc => '自定义短地址'
6
+ option :token, :aliases => '-t', :desc => '用户令牌,不写则使用已经登陆信息'
7
+ option :changelog, :aliases => '-c', :desc => '修改纪录,默认为空字符串', :default => ''
8
+ option :verbose, :aliases => '-v', :desc => '设置输出级别 v, vv, vvv'
9
+ option :quiet, :aliases => '-q', :desc => '安静模式,不输出任何选项'
10
+ def publish(path)
11
+ if _opt_resign && _is_ipa(path)
12
+ tfile = Tempfile.new ["resign-#{ SecureRandom.hex }", '.ipa']
13
+ resign path, tfile.path
14
+ path = tfile.path
15
+ end
16
+ app = _info path, true
17
+ fir_app = _fir_info app[:identifier], app[:type]
18
+
19
+ id = fir_app[:id]
20
+ short = fir_app[:short]
21
+ bundle_app = fir_app[:bundle][:pkg]
22
+ bundle_icon = fir_app[:bundle][:icon]
23
+
24
+ if app[:icons] != nil && app[:icons].length > 0
25
+ icon_path = app[:icons][0][:path]
26
+
27
+ if _is_ipa path
28
+ _puts '> 转换图标...'
29
+ Pngdefry.defry icon_path, icon_path
30
+ end
31
+
32
+ _puts "> 上传图标..."
33
+ RestClient.post bundle_icon[:url],
34
+ :key => bundle_icon[:key],
35
+ :token => bundle_icon[:token],
36
+ :file => File.new(icon_path, 'rb')
37
+
38
+ _puts '> 上传图标成功'
39
+ end
40
+
41
+ _puts '> 上传应用...'
42
+ res = RestClient.post bundle_app[:url],
43
+ :key => bundle_app[:key],
44
+ :token => bundle_app[:token],
45
+ :file => File.new(path, 'rb')
46
+ _puts '> 上传应用成功'
47
+ upload_res = JSON.parse res.body, :symbolize_names => true
48
+
49
+ _fir_put fir_app[:id],
50
+ :name => app[:display_name] || app[:name],
51
+ :short => options[:short] || fir_app[:short],
52
+ :version => app[:version],
53
+ :versionShort => app[:short_version]
54
+
55
+ _fir_vput upload_res[:versionOid],
56
+ :version => app[:version],
57
+ :versionShort => app[:short_version],
58
+ :devices => app[:devices],
59
+ :release_type => app[:release_type],
60
+ :changelog => options[:changelog]
61
+
62
+
63
+ _puts "> http://fir.im/#{short}"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,67 @@
1
+ module Fir
2
+ class Cli < Thor
3
+ desc 'resign IPA_FILE_PATH OUTPUT_PATH', '使用 resign.tapbeta.com 进行企业签名'
4
+ option :email, :aliases => '-e', :desc => '邮件地址'
5
+ option :verbose, :aliases => '-v', :desc => '设置输出级别 v, vv, vvv'
6
+ option :quiet, :aliases => '-q', :desc => '安静模式,不输出任何选项'
7
+ def resign(ipath, opath)
8
+ _puts "! #{ Paint['resign.tapbeta.com 签名服务风险提示', :red] }"
9
+ _puts '! 无法保证签名证书的长期有效性,当某种条件满足后'
10
+ _puts '! 苹果会禁止该企业账号,所有由该企业账号所签发的'
11
+ _puts '! 证书都会失效。您如果使用该网站提供的服务进行应'
12
+ _puts "! 用分发,请注意:#{ Paint['当证书失效后,所有安装了已失效', :red ] }"
13
+ _puts "! #{ Paint['证书签名的用户都会无法正常运行应用;同时托管在', :red ] }"
14
+ _puts "! #{ Paint['fir.im 的应用将无法正常安装。', :red ] }"
15
+ if _opt_email == nil
16
+ _puts "! #{ Paint['您需要提供邮件地址才能使用 resign.tapbeta.com', :red] }"
17
+ _puts "! #{ Paint['的签名服务, 请输入:', :red] } fir config --email=EMAIL"
18
+ _puts "! #{ Paint['进行设置', :red] }"
19
+ exit 1
20
+ end
21
+ if !/\.ipa$/.match ipath
22
+ _puts Paint['! 只能给以 ipa 为扩展名的文件签名', :red]
23
+ exit 1
24
+ end
25
+ _puts '> 正在申请上传令牌...'
26
+ upload_res = RestClient.post 'http://api.resign.tapbeta.com/public/upload',
27
+ :email => @email,
28
+ :file => File.basename(ipath)
29
+ form = JSON.parse upload_res.body, :symbolize_names => true
30
+ tapbeta = {}
31
+ form.each do |f|
32
+ if /^tb_/.match f[0]
33
+ tapbeta[f[0]] = f[1]
34
+ form.delete f[0]
35
+ end
36
+ end
37
+ form[:file] = File.new Pathname.new(Dir.pwd).join(ipath).cleanpath, 'rb'
38
+ _puts '> 正在上传...'
39
+ res = RestClient.post tapbeta[:tb_upload_url], form
40
+
41
+ # Upyun's notify is fucking, handle it specific
42
+ # if tapbeta[:tb_upload_url].include? 'upyun'
43
+ # RestClient.get "#{ form[:'notify-url'] }?#{ URI.encode_www_form JSON.parse res.body }"
44
+ # end
45
+
46
+
47
+ _puts '> 正在排队...'
48
+ nped = true
49
+ info = {}
50
+ loop do
51
+ res = RestClient.get "http://api.resign.tapbeta.com/public/#{ tapbeta[:tb_upload_key] }",
52
+ :params => { :__mr => 'eyJ1cmwiOiIkKHVybCkiLCAicmVzaWduU3RhdHVzIjogIiQocmVzaWduU3RhdHVzKSJ9' }
53
+ info = JSON.parse res.body, :symbolize_names => true
54
+ if nped && info[:resignStatus] == 'doing'
55
+ _puts '> 正在签名...'
56
+ nped = false
57
+ end
58
+ break if info[:url] != ''
59
+ sleep 5
60
+ end
61
+ opath = Pathname.new(Dir.pwd).join(opath).cleanpath
62
+ _puts "> 正在下载到 #{ opath }..."
63
+ `curl #{ info[:url] } -o #{ opath } -s`
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,28 @@
1
+ module Fir
2
+ class Cli < Thor
3
+ desc 'update APP_FILE_PATH|BUNDLE_ID', '更新 FIR.im 的应用信息'
4
+ option :short, :aliases => '-s', :desc => '自定义短地址'
5
+ option :token, :aliases => '-t', :desc => '用户令牌,不写则使用已经登陆信息'
6
+ option :verbose, :aliases => '-v', :desc => '设置输出级别 v, vv, vvv'
7
+ option :quiet, :aliases => '-q', :desc => '安静模式,不输出任何选项'
8
+ def update(path)
9
+ identifier = path
10
+ identifier = _info(path)[:identifier] if File.exist? _path path
11
+ fir_app = _fir_info identifier
12
+ opt_short = options[:short]
13
+ post = {}
14
+ post[:short] = opt_short if opt_short
15
+
16
+ post.each { |i| post.delete i[0] if i[1] == fir_app[i[0]] }
17
+
18
+ if post.length == 0 then _puts '> 没有什么可以更新的'; return end
19
+ _fir_put fir_app[:id], post
20
+
21
+ fir_app = _fir_info identifier
22
+ if opt_short && opt_short != fir_app[:short]
23
+ _puts "> 短地址 #{ opt_short } 被占用,FIR.im 自动更新短地址为 #{ fir_app[:short] }"
24
+ end
25
+ _puts "> http://fir.im/#{ fir_app[:short] }"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,172 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'pathname'
4
+ require 'tempfile'
5
+ require 'securerandom'
6
+
7
+ require 'thor'
8
+ require 'paint'
9
+ require 'pngdefry'
10
+ require 'user_config'
11
+ require 'rest_client'
12
+ require 'lagunitas'
13
+ require 'ruby_apk'
14
+
15
+ module Fir
16
+ class Cli < Thor
17
+ def initialize(*args)
18
+ super
19
+ _init_config
20
+ _puts_welcome
21
+ end
22
+ def self.load_extends
23
+ `gem list --local`.each_line("\n") do |gem|
24
+ gem_name = /^[^\s]+/.match(gem)[0]
25
+ require gem_name if gem_name.start_with? 'fir-cli-'
26
+ end
27
+ end
28
+ private
29
+ def _init_config
30
+ @uconfig = UserConfig.new '.fir'
31
+ @config = @uconfig['default.yaml']
32
+ @tmpfiles = []
33
+ end
34
+ def _path(path)
35
+ path = Pathname.new(Dir.pwd).join(path).cleanpath
36
+ end
37
+ def _opt_token
38
+ @token ||= options[:token] || @config['token']
39
+ end
40
+ def _opt_email
41
+ @email ||= options[:email] || @config['email']
42
+ end
43
+ def _opt_verbose
44
+ @verbose ||= options[:verbose] || @config['verbose']
45
+ end
46
+ def _opt_resign
47
+ return options[:resign] if defined? options[:resign]
48
+ @resign ||= options[:resign] || @config['resign']
49
+ end
50
+ def _opt_quiet
51
+ return options[:quiet] if defined? options[:quiet]
52
+ @quiet ||= options[:quiet] || @config['quiet']
53
+ end
54
+ def _is_ipa(path)
55
+ path.end_with? '.ipa'
56
+ end
57
+ def _is_apk(path)
58
+ path.end_with? '.apk'
59
+ end
60
+ def _is_identifier(str)
61
+ /^(?:(?:[a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/.match str
62
+ end
63
+ def _chk_login
64
+ if !_opt_token
65
+ _puts "! #{ Paint['您还没有登录, 请输入:', :red] } fir login USER_TOKEN #{ Paint['进行登录', :red] }"
66
+ exit 1
67
+ end
68
+ end
69
+ def _puts_welcome
70
+ _puts "> #{ Paint['欢迎使用 FIR.im 命令行工具,如需帮助请输入:', :green] } fir help"
71
+ end
72
+ def _puts(text)
73
+ return if _opt_quiet
74
+ case _opt_verbose || 'vv' # If nothing about log is set, use the default option - vv
75
+ when 'v'
76
+ puts text if text.start_with?('!')
77
+ when 'vv'
78
+ puts text if text.start_with?('!') || text.start_with?('>')
79
+ when 'vvv'
80
+ puts text if text.start_with?('!') || text.start_with?('>') || text.start_with?('-')
81
+ end
82
+ end
83
+ def _info(path, more = false)
84
+ if _is_ipa path
85
+ _ipa_info path, more
86
+ elsif _is_apk path
87
+ _apk_info path, more
88
+ else
89
+ _puts "! #{ Paint['只能支持后缀为 ipa 和 apk 地文件', :red] }"
90
+ end
91
+ end
92
+ def _apk_info(path, more = false)
93
+ path = _path path
94
+ apk = Android::Apk.new path
95
+ info = {
96
+ :type => 'android',
97
+ :identifier => apk.manifest.package_name,
98
+ :name => apk.label,
99
+ :version => apk.manifest.version_code,
100
+ :short_version => apk.manifest.version_name
101
+ }
102
+ if more
103
+ info[:icons] = apk.icon.map do |name, data|
104
+ tfile = Tempfile.new ["icon-#{ SecureRandom.hex }", '.png']
105
+ tfile.write data
106
+ @tmpfiles.push tfile
107
+ {
108
+ :name => File.basename(name),
109
+ :path => tfile.path
110
+ }
111
+ end
112
+ end
113
+ info
114
+ end
115
+ def _ipa_info(path, more = false)
116
+ path = _path path
117
+ ipa = Lagunitas::IPA.new path
118
+ _puts '> 正在解析 ipa 文件...'
119
+ app = ipa.app
120
+ info = {
121
+ :type => 'ios',
122
+ :identifier => app.identifier,
123
+ :name => app.name,
124
+ :display_name => app.display_name,
125
+ :version => app.version,
126
+ :short_version => app.short_version,
127
+ :devices => app.devices,
128
+ :release_type => app.release_type,
129
+ :distribution_name => app.distribution_name
130
+ }
131
+ if more
132
+ if app.icons
133
+ info[:icons] = app.icons.map do |icon|
134
+ tfile = Tempfile.new ["icon-#{ SecureRandom.hex }", '.png']
135
+ @tmpfiles.push tfile
136
+ FileUtils.cp icon[:path], tfile.path
137
+ {
138
+ :name => File.basename(icon[:path]),
139
+ :path => tfile.path
140
+ }
141
+ end
142
+ end
143
+ info[:plist] = app.info
144
+ app.mobileprovision.delete 'DeveloperCertificates' if app.mobileprovision
145
+ info[:mobileprovision] = app.mobileprovision
146
+ end
147
+ ipa.cleanup
148
+ info
149
+ end
150
+ def _fir_info(identifier, type = 'ios')
151
+ _puts "> 正在获取 #{ identifier }@FIR.im 的应用信息..."
152
+ _chk_login
153
+ body = { :token => @token, :type => type }
154
+ res = RestClient.get "http://fir.im/api/v2/app/info/#{ identifier }?#{ URI.encode_www_form body }"
155
+ JSON.parse res.body, :symbolize_names => true
156
+ end
157
+ def _fir_put(id, body)
158
+ _chk_login
159
+ body[:token] = @token
160
+ _puts '> 正在更新 fir 的应用信息...'
161
+ RestClient.put "http://fir.im/api/v2/app/#{ id }?#{ URI.encode_www_form body }", body
162
+ _puts '> 更新成功'
163
+ end
164
+ def _fir_vput(id, body)
165
+ _chk_login
166
+ body[:token] = @token
167
+ _puts '> 正在更新 fir 的应用版本信息...'
168
+ RestClient.put "http://fir.im/api/v2/appVersion/#{ id }/complete?#{ URI.encode_www_form body }", body
169
+ _puts '> 更新成功'
170
+ end
171
+ end
172
+ end
data/lib/lagunitas.ext.rb CHANGED
@@ -21,7 +21,8 @@ module Lagunitas
21
21
  mobileprovision['ProvisionedDevices'] if mobileprovision
22
22
  end
23
23
  def distribution_name
24
- mobileprovision['DeveloperCertificates'] if mobileprovision
24
+ # mobileprovision['DeveloperCertificates'] if mobileprovision
25
+ "#{ mobileprovision['Name'] }: #{ mobileprovision['TeamName'] }" if mobileprovision
25
26
  end
26
27
  def metadata
27
28
  return if !metadata?
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fir-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Yan
7
+ - FIR.im
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
@@ -109,24 +109,37 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: 0.7.1
111
111
  description: FIR.im 命令行工具,支持 ios 和 android
112
- email: idy0013@gmail.com
112
+ email: fir-cli@fir.im
113
113
  executables:
114
114
  - fir
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
- - lib/fir-cli.rb
118
+ - .gitignore
119
+ - Gemfile
120
+ - Gemfile.lock
121
+ - LICENSE.txt
122
+ - README.md
123
+ - bin/fir
124
+ - fir-cli.gemspec
125
+ - fir-cli.rb
126
+ - lib/fir-cli-commands/config.rb
127
+ - lib/fir-cli-commands/info.rb
128
+ - lib/fir-cli-commands/login.rb
129
+ - lib/fir-cli-commands/publish.rb
130
+ - lib/fir-cli-commands/resign.rb
131
+ - lib/fir-cli-commands/update.rb
132
+ - lib/fir-cli.ext.rb
119
133
  - lib/lagunitas.ext.rb
120
134
  - lib/lagunitas.patch.rb
121
- - bin/fir
122
- homepage: https://github.com/idy/fir-cli
135
+ homepage: http://blog.fir.im/2014/fir-cli
123
136
  licenses:
124
- - MIT
137
+ - GPL
125
138
  metadata: {}
126
139
  post_install_message:
127
140
  rdoc_options: []
128
141
  require_paths:
129
- - lib
142
+ - ./
130
143
  required_ruby_version: !ruby/object:Gem::Requirement
131
144
  requirements:
132
145
  - - '>='
data/lib/fir-cli.rb DELETED
@@ -1,321 +0,0 @@
1
- require 'uri'
2
- require 'json'
3
- require 'pathname'
4
- require 'tempfile'
5
- require 'securerandom'
6
-
7
- require 'thor'
8
- require 'paint'
9
- require 'pngdefry'
10
- require 'user_config'
11
- require 'rest_client'
12
- require 'lagunitas'
13
- require 'lagunitas.patch'
14
- require 'lagunitas.ext'
15
- require 'ruby_apk'
16
-
17
- # p Lagunitas::App.new('').respond_to? :mobileprovision
18
-
19
- class FirCli < Thor
20
- def initialize(*args)
21
- super
22
- @uconfig = UserConfig.new '.fir'
23
- @ipa_config = @uconfig['ipa.yaml']
24
- @ipa_config['resign'] = false if @ipa_config['resign'] == nil
25
- @tmpfiles = []
26
- end
27
-
28
- desc 'login USER_TOKEN', '以 USER_TOKEN 身份登陆'
29
- def login(token)
30
- ot = @ipa_config['token']
31
- if ot && ot != token
32
- puts "> 替换用户 #{ot} => #{token}"
33
- end
34
- @ipa_config['token'] = token
35
- @ipa_config.save
36
- puts "> 当前用户:#{token}"
37
- end
38
-
39
- desc 'config', '配置全局设置'
40
- option :resign, :aliases => '-r', :desc => '是否以企业签名发布', :type => :boolean
41
- option :token, :aliases => '-t', :desc => '用户令牌'
42
- option :email, :aliases => '-e', :desc => '邮件地址'
43
- option :verbose, :aliases => '-v', :desc => '输出配置项', :type => :boolean
44
- def config
45
- if !options[:verbose]
46
- options.each do |conf|
47
- puts "> #{ Paint[conf[0].to_s.rjust(10), :blue] } : #{ @ipa_config[conf[0].to_s] } => #{ conf[1] }"
48
- @ipa_config[conf[0]] = conf[1]
49
- end
50
- @ipa_config.save
51
- end
52
- puts "> 当前设置:"
53
- @ipa_config.each {|conf| puts "> #{ Paint[conf[0].to_s.rjust(10), :blue] } => #{conf[1]}"}
54
- end
55
-
56
- desc 'update IPA_FILE_PATH', '更新 fir.im 的应用信息'
57
- option :short, :aliases => '-s', :desc => '自定义短地址'
58
- option :token, :aliases => '-t', :desc => '用户令牌,不写则使用已经登陆信息'
59
- def update(path)
60
- app = _info path
61
- fir_app = _fir_info app[:identifier], app[:type]
62
- opt_short = options[:short]
63
- post = {}
64
- post[:short] = opt_short if opt_short
65
-
66
- post.each { |i| post.delete i[0] if i[1] == fir_app[i[0]] }
67
-
68
- if post.length == 0 then puts '> 没有可以更新的项目'; return end
69
- _fir_put fir_app[:id], post
70
-
71
- fir_app = _fir_info app[:identifier], app[:type]
72
- if opt_short && opt_short != fir_app[:short]
73
- puts "> 短地址 #{ opt_short } 被占用,FIR.im 自动更新短地址为 #{ fir_app[:short] }"
74
- end
75
- puts "> http://fir.im/#{fir_app[:short]}"
76
- end
77
-
78
- desc 'publish IPA_FILE_PATH', '将 .ipa 文件发布至 FIR.im'
79
- option :resign, :aliases => '-r', :desc => '进行企业签名', :type => :boolean
80
- option :short, :aliases => '-s', :desc => '自定义短地址'
81
- option :token, :aliases => '-t', :desc => '用户令牌,不写则使用已经登陆信息'
82
- option :changelog, :aliases => '-c', :desc => '修改纪录,默认为空字符串', :default => ''
83
- def publish(path)
84
- if _opt_resign && _is_ipa(path)
85
- tfile = Tempfile.new SecureRandom.hex
86
- resign path, tfile.path
87
- path = tfile.path
88
- end
89
- app = _info path, false, true
90
- fir_app = _fir_info app[:identifier], app[:type]
91
-
92
- id = fir_app[:id]
93
- short = fir_app[:short]
94
- bundle_app = fir_app[:bundle][:pkg]
95
- bundle_icon = fir_app[:bundle][:icon]
96
-
97
- puts '> 上传应用...'
98
- res = RestClient.post bundle_app[:url],
99
- :key => bundle_app[:key],
100
- :token => bundle_app[:token],
101
- :file => File.new(path, 'rb')
102
- puts '> 上传应用成功'
103
- upload_res = JSON.parse res.body, :symbolize_names => true
104
-
105
- if app[:icons] != nil
106
- icon_path = app[:icons][0][:path]
107
-
108
- if _is_ipa path
109
- puts '> 转换图标...'
110
- Pngdefry.defry icon_path, icon_path
111
- end
112
-
113
- puts "> 上传图标..."
114
- RestClient.post bundle_icon[:url],
115
- :key => bundle_icon[:key],
116
- :token => bundle_icon[:token],
117
- :file => File.new(icon_path, 'rb')
118
-
119
- puts '> 上传图标成功'
120
- end
121
-
122
- _fir_put fir_app[:id],
123
- :name => app[:display_name] || app[:name],
124
- :short => options[:short] || fir_app[:short],
125
- :version => app[:version],
126
- :versionShort => app[:short_version]
127
-
128
- _fir_vput upload_res[:versionOid],
129
- :version => app[:version],
130
- :versionShort => app[:short_version],
131
- :devices => app[:devices],
132
- :release_type => app[:release_type],
133
- :changelog => options[:changelog]
134
-
135
-
136
- puts "> http://fir.im/#{short}"
137
- end
138
-
139
- desc 'info IPA_FILE_PATH', '获取 .ipa 文件的信息'
140
- option 'verbose', :aliases => '-v', :desc => '显示更多信息', :type => :boolean
141
- option 'fir', :aliases => '-f', :desc => '显示托管在 fir.im 的信息', :type => :boolean
142
- def info(path)
143
- app = _info path, false, options['verbose']
144
- app.each { |i| puts "#{ Paint[i[0].to_s.rjust(15), :blue] } #{ i[1] }" }
145
- if options[:fir]
146
- fir_app = _fir_info app[:identifier], app[:type]
147
- fir_app.each { |i| puts "#{ Paint[i[0].to_s.rjust(15), :blue] } #{ i[1] }" }
148
- end
149
- end
150
-
151
- desc 'resign IPA_FILE_PATH OUTPUT_PATH', '使用 resign.tapbeta.com 进行企业签名'
152
- option 'email', :aliases => '-e', :desc => '邮件地址'
153
- def resign(ipath, opath)
154
- puts '! resign.tapbeta.com 签名服务风险提示'
155
- puts '! 无法保证签名证书的长期有效性,当某种条件满足后'
156
- puts '! 苹果会禁止该企业账号,所有由该企业账号所签发的'
157
- puts '! 证书都会失效。您如果使用该网站提供的服务进行应'
158
- puts "! 用分发,请注意:#{ Paint['当证书失效后,所有安装以失效证', :red ] }"
159
- puts "! #{ Paint['书签名的的用户都会无法正常运行应用;同时托管在', :red ] }"
160
- puts "! #{ Paint['fir.im 的应用将无法正常安装。', :red ] }"
161
- if _opt_email == nil
162
- puts Paint['! 您需要提供邮件地址才能使用 resign.tapbeta.com 的签名服务', :red]
163
- puts Paint['! 使用 fir config --email=EMAIL 进行设置', :green]
164
- exit
165
- end
166
- if !/\.ipa$/.match ipath
167
- puts Paint['! 只能给以 ipa 为扩展名的文件签名', :red]
168
- exit
169
- end
170
- puts '> 正在申请上传令牌...'
171
- upload_res = RestClient.post 'http://api.resign.tapbeta.com/public/upload',
172
- :email => @email,
173
- :file => File.basename(ipath)
174
- form = JSON.parse upload_res.body, :symbolize_names => true
175
- tapbeta = {}
176
- form.each do |f|
177
- if /^tb_/.match f[0]
178
- tapbeta[f[0]] = f[1]
179
- form.delete f[0]
180
- end
181
- end
182
- form[:file] = File.new Pathname.new(Dir.pwd).join(ipath).cleanpath, 'rb'
183
- puts '> 正在上传...'
184
- RestClient.post tapbeta[:tb_upload_url], form
185
- puts '> 正在排队...'
186
- nped = true
187
- info = {}
188
- loop do
189
- res = RestClient.get "http://api.resign.tapbeta.com/public/#{ tapbeta[:tb_upload_key] }",
190
- :params => { :__mr => 'eyJ1cmwiOiIkKHVybCkiLCAicmVzaWduU3RhdHVzIjogIiQocmVzaWduU3RhdHVzKSJ9' }
191
- info = JSON.parse res.body, :symbolize_names => true
192
- if nped && info[:resignStatus] == 'doing'
193
- puts '> 正在签名...'
194
- nped = false
195
- end
196
- break if info[:url] != ''
197
- sleep 5
198
- end
199
- opath = Pathname.new(Dir.pwd).join(opath).cleanpath
200
- puts "> 正在下载到 #{ opath }..."
201
- `curl #{info[:url]} -o #{opath} -s`
202
- end
203
-
204
- private
205
- def _path(path)
206
- path = Pathname.new(Dir.pwd).join(path).cleanpath
207
- end
208
- def _info(path, quiet = false, more = false, bin = false)
209
- if _is_ipa path
210
- _ipa_info path, quiet, more
211
- elsif _is_apk path
212
- _apk_info path, quiet, more
213
- else
214
- puts Paint['! 只能支持后缀为 ipa 和 apk 地文件', :red]
215
- end
216
- end
217
- def _apk_info(path, quiet = false, more = false)
218
- path = _path path
219
- apk = Android::Apk.new path
220
- info = {
221
- :type => 'android',
222
- :identifier => apk.manifest.package_name,
223
- :name => apk.label,
224
- :version => apk.manifest.version_code,
225
- :short_version => apk.manifest.version_name
226
- }
227
- if more
228
- info[:icons] = apk.icon.map do |name, data|
229
- tfile = Tempfile.new SecureRandom.hex
230
- tfile.write data
231
- @tmpfiles.push tfile
232
- {
233
- :name => File.basename(name),
234
- :path => tfile.path
235
- }
236
- end
237
- end
238
- info
239
- end
240
- def _ipa_info(path, quiet = false, more = false, bin = false)
241
- path = _path path
242
- ipa = Lagunitas::IPA.new path
243
- puts '> 正在解析 ipa 文件...' if !quiet
244
- app = ipa.app
245
- info = {
246
- :type => 'ios',
247
- :identifier => app.identifier,
248
- :name => app.name,
249
- :display_name => app.display_name,
250
- :version => app.version,
251
- :short_version => app.short_version,
252
- :devices => app.devices,
253
- :release_type => app.release_type
254
- # :distribution_name => app.distribution_name
255
- }
256
- if more
257
- info[:icons] = app.icons.map do |icon|
258
- tfile = Tempfile.new SecureRandom.hex
259
- @tmpfiles.push tfile
260
- File.copy icon[:path], tfile.path
261
- {
262
- :name => File.basename(icon[:path]),
263
- :path => tfile.path
264
- }
265
- end
266
- info[:plist] = app.info
267
- end
268
- ipa.cleanup
269
- info
270
- end
271
- def _fir_info(identifier, type = 'ios', quiet = false)
272
- if !_opt_token
273
- puts Paint['! 您还没有登录', :red]
274
- puts Paint['! 请 fir login USER_TOKEN 登录', :green]
275
- exit
276
- end
277
- body = { :token => @token }
278
- body[:type] = type
279
- puts '> 正在获取 fir 的应用信息...' if !quiet
280
- res = RestClient.get "http://fir.im/api/v2/app/info/#{identifier}?#{URI.encode_www_form body}"
281
- JSON.parse res.body, :symbolize_names => true
282
- end
283
- def _fir_put(id, body, quiet = false)
284
- if !_opt_token
285
- puts Paint['! 您还没有登录', :red]
286
- puts Paint['! 请 fir login USER_TOKEN 登录', :green]
287
- exit
288
- end
289
- body[:token] = @token
290
- puts '> 正在更新 fir 的应用信息...' if !quiet
291
- RestClient.put "http://fir.im/api/v2/app/#{id}?#{URI.encode_www_form body}", body
292
- puts '> 更新成功'
293
- end
294
- def _fir_vput(id, body, quiet = false)
295
- if !_opt_token
296
- puts Paint['! 您还没有登录', :red]
297
- puts Paint['! 请 fir login USER_TOKEN 登录', :green]
298
- exit
299
- end
300
- body[:token] = @token
301
- puts '> 正在更新 fir 的应用版本信息...' if !quiet
302
- RestClient.put "http://fir.im/api/v2/appVersion/#{id}/complete?#{URI.encode_www_form body}", body
303
- puts '> 更新成功'
304
- end
305
- def _opt_token
306
- @token = options[:token] || @ipa_config['token'] || nil
307
- end
308
- def _opt_email
309
- @email = options[:email] || @ipa_config['email'] || nil
310
- end
311
- def _opt_resign
312
- return options[:resign] if defined? options[:resign]
313
- @resign = options[:resign] || @ipa_config['resign'] || nil
314
- end
315
- def _is_ipa(path)
316
- /\.ipa$/.match path
317
- end
318
- def _is_apk(path)
319
- /\.apk$/.match path
320
- end
321
- end