This commit is contained in:
icyleaf
2019-12-12 19:09:39 +08:00
commit 6c790e0a9b
18 changed files with 713 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
# Ruby CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/ruby:2.4.2
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: install dependencies
command: bundle check || bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor
key: v1-dependencies-{{ checksum "Gemfile" }}
# run tests!
- run:
name: run tests
command: bundle exec rake
# collect reports
- store_test_results:
path: ~/repo/test-results
- store_artifacts:
path: ~/repo/test-results
destination: test-results
+12
View File
@@ -0,0 +1,12 @@
*.gem
Gemfile.lock
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
fastlane/README.md
fastlane/report.xml
coverage
test-results
+5
View File
@@ -0,0 +1,5 @@
--require spec_helper
--color
--format d
--format RspecJunitFormatter
--out test-results/rspec/rspec.xml
+185
View File
@@ -0,0 +1,185 @@
---
Style/MultipleComparison:
Enabled: false
Style/PercentLiteralDelimiters:
Enabled: false
Style/ClassCheck:
EnforcedStyle: kind_of?
Style/FrozenStringLiteralComment:
Enabled: false
Style/SafeNavigation:
Enabled: false
Performance/RegexpMatch:
Enabled: false
Performance/StringReplacement:
Enabled: false
Style/NumericPredicate:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/ModuleLength:
Enabled: false
Style/VariableNumber:
Enabled: false
Style/MethodMissing:
Enabled: false
MultilineBlockChain:
Enabled: false
Style/NumericLiteralPrefix:
Enabled: false
Style/TernaryParentheses:
Enabled: false
Style/EmptyMethod:
Enabled: false
Style/BracesAroundHashParameters:
Enabled: false
Lint/UselessAssignment:
Exclude:
- "**/spec/**/*"
Require/MissingRequireStatement:
Exclude:
- "**/spec/**/*.rb"
- "**/spec_helper.rb"
- spaceship/lib/spaceship/babosa_fix.rb
- "**/Fastfile"
- "**/*.gemspec"
- rakelib/**/*
- "**/*.rake"
- "**/Rakefile"
- fastlane/**/*
- supply/**/*
Layout/IndentHash:
Enabled: false
Layout/AlignHash:
Enabled: false
Layout/DotPosition:
Enabled: false
Style/DoubleNegation:
Enabled: false
Style/SymbolArray:
Enabled: false
Layout/IndentHeredoc:
Enabled: false
Style/MixinGrouping:
Exclude:
- "**/spec/**/*"
Lint/HandleExceptions:
Enabled: false
Lint/UnusedBlockArgument:
Enabled: false
Lint/AmbiguousBlockAssociation:
Enabled: false
Style/GlobalVars:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/SpecialGlobalVars:
Enabled: false
Metrics/AbcSize:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Style/WordArray:
MinSize: 19
Style/SignalException:
Enabled: false
Style/RedundantReturn:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/AndOr:
Enabled: true
EnforcedStyle: conditionals
Metrics/ClassLength:
Max: 320
Metrics/LineLength:
Max: 370
Metrics/ParameterLists:
Max: 17
Metrics/PerceivedComplexity:
Max: 18
Style/GuardClause:
Enabled: false
Style/StringLiterals:
Enabled: false
Style/ConditionalAssignment:
Enabled: false
Style/RedundantSelf:
Enabled: false
Lint/UnusedMethodArgument:
Enabled: false
Lint/ParenthesesAsGroupedExpression:
Exclude:
- "**/spec/**/*"
Style/PredicateName:
Enabled: false
Style/PerlBackrefs:
Enabled: false
Layout/SpaceAroundOperators:
Exclude:
- "**/spec/actions_specs/xcodebuild_spec.rb"
AllCops:
TargetRubyVersion: 2.0
Include:
- "*/lib/assets/*Template"
- "*/lib/assets/*TemplateAndroid"
Exclude:
- "**/lib/assets/custom_action_template.rb"
- "./vendor/**/*"
- "**/lib/assets/DefaultFastfileTemplate"
- "**/lib/assets/MatchfileTemplate"
- "**/spec/fixtures/broken_files/broken_file.rb"
Style/FileName:
Exclude:
- "**/Dangerfile"
- "**/Brewfile"
- "**/Gemfile"
- "**/Podfile"
- "**/Rakefile"
- "**/Fastfile"
- "**/Deliverfile"
- "**/Snapfile"
- "**/*.gemspec"
Style/Documentation:
Enabled: false
Style/MutableConstant:
Enabled: false
Style/ZeroLengthPredicate:
Enabled: false
Style/IfInsideElse:
Enabled: false
Style/CollectionMethods:
Enabled: false
CrossPlatform/ForkUsage:
Exclude:
- "**/plugins/template/**/*"
Lint/IsStringUsage:
Include:
- gym/**/*
- screengrab/**/*
- supply/**/*
Style/MethodCallWithArgsParentheses:
Enabled: true
IgnoredMethods:
- require
- require_relative
- fastlane_require
- gem
- program
- command
- raise
- attr_accessor
- attr_reader
- desc
- lane
- private_lane
- platform
- to
- describe
- it
- be
- context
- before
- after
+4
View File
@@ -0,0 +1,4 @@
# os: osx # enable this if you need macOS support
language: ruby
rvm:
- 2.2.4
+6
View File
@@ -0,0 +1,6 @@
source('https://rubygems.org')
gemspec
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Zealot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+63
View File
@@ -0,0 +1,63 @@
# Zealot plugin
[![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-zealot)
上传移动应用(iPhone、Android)到 [Zealot](https://github.com/getzealot/zealot) 自建 App 分发系统。
> Zealot 是一个开源、可自建服务的移动应用上传没有如此简单、解放开发打包的烦恼,轻松放权给测试、产品、运营等使用 App 的人员,深度与 Jenkins 和 Gitlab 集成。
## 快速上手
这是一个 [_fastlane_](https://github.com/fastlane/fastlane) 插件。如果要使用 `fastlane-plugin-zealot` 可通过下面方法添加到 fastlane 体系中:
```bash
$ fastlane add_plugin zealot
```
## 参数说明
```
+-----------------+-------------------------------------------------+------------------------+----------+
| zealot Options |
+-----------------+-------------------------------------------------+------------------------+----------+
| Key | Description | Env Var | Default |
+-----------------+-------------------------------------------------+------------------------+----------+
| endpoint | The endpoint of zealot | ZEALOT_ENDPOINT | |
| token | The token of user | ZEALOT_TOKEN | |
| channel_key | The key of app's channel | ZEALOT_CHANNEL_KEY | |
| file | The path of app file. Optional if you use the | ZEALOT_FILE | |
| | `gym`, `ipa`, `xcodebuild` or `gradle` action. | | |
| name | The name of app to display on zealot | ZEALOT_NAME | |
| changelog | The changelog of app | ZEALOT_CHANGELOG | |
| slug | The slug of app | ZEALOT_SLUG | |
| release_type | The release type of app | ZEALOT_RELEASE_TYPE | |
| branch | The name of git branch | ZEALOT_BRANCH | |
| git_commit | The hash of git commit | ZEALOT_GIT_COMMIT | |
| password | The password of app to download | ZEALOT_PASSWORD | |
| source | The name of upload source | ZEALOT_SOURCE | fastlane |
| timeout | Request timeout in seconds | ZEALOT_TIMEOUT | |
| hide_user_token | replase user token to *** to keep secret | ZEALOT_HIDE_USER_TOKEN | true |
| fail_on_error | Should an error uploading app cause a failure? | ZEALOT_FAIL_ON_ERROR | false |
| | (true/false) | | |
+-----------------+-------------------------------------------------+------------------------+----------+
```
## 举个例子
检查[范例 `Fastfile` 文件](fastlane/Fastfile) 来看看如何使用插件吧(其实啥都没有写)
## Issues and Feedback
For any other issues and feedback about this plugin, please submit it to this repository.
## Troubleshooting
If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
## Using _fastlane_ Plugins
For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
## About _fastlane_
_fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
+9
View File
@@ -0,0 +1,9 @@
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new
require 'rubocop/rake_task'
RuboCop::RakeTask.new(:rubocop)
task(default: [:spec, :rubocop])
+35
View File
@@ -0,0 +1,35 @@
# coding: utf-8
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'fastlane/plugin/zealot/version'
Gem::Specification.new do |spec|
spec.name = 'fastlane-plugin-zealot'
spec.version = Fastlane::Zealot::VERSION
spec.author = 'icyleaf'
spec.email = 'icyleaf.cn@gmail.com'
spec.summary = 'Upload a new build to Zealot'
spec.homepage = "https://github.com/getzealot/fastlane-plugin-zealot"
spec.license = "MIT"
spec.files = Dir["lib/**/*"] + %w(README.md LICENSE)
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
# Don't add a dependency to fastlane or fastlane_re
# since this would cause a circular dependency
# spec.add_dependency 'your-dependency', '~> 1.0.0'
spec.add_development_dependency('pry')
spec.add_development_dependency('bundler')
spec.add_development_dependency('rspec')
spec.add_development_dependency('rspec_junit_formatter')
spec.add_development_dependency('rake')
spec.add_development_dependency('rubocop', '0.49.1')
spec.add_development_dependency('rubocop-require_tools')
spec.add_development_dependency('simplecov')
spec.add_development_dependency('fastlane', '>= 2.137.0')
end
+8
View File
@@ -0,0 +1,8 @@
lane :test do
zealot(
endpoint: 'http://localhost:3000',
token: '...',
channel_key: '...',
file: 'app.apk'
)
end
+1
View File
@@ -0,0 +1 @@
# Autogenerated by fastlane
+16
View File
@@ -0,0 +1,16 @@
require 'fastlane/plugin/zealot/version'
module Fastlane
module Zealot
# Return all .rb files inside the "actions" and "helper" directory
def self.all_classes
Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
end
end
end
# By default we want to import all available actions and helpers
# A plugin can contain any number of actions and plugins
Fastlane::Zealot.all_classes.each do |current|
require current
end
@@ -0,0 +1,260 @@
require 'fastlane/action'
require_relative '../helper/zealot_helper'
module Fastlane
module Actions
module SharedValues
ZEALOT_RELEASE_URL = :ZEALOT_RELEASE_URL
ZEALOT_QRCODE_URL = :ZEALOT_QRCODE_URL
ZEALOT_INSTALL_URL = :ZEALOT_INSTALL_URL
end
class ZealotAction < Action
def self.run(params)
upload_url = params[:endpoint]
timeout = params[:timeout]
fail_on_error = params[:fail_on_error]
form = build_params(params)
# Dump request form
print_table(form, params[:hide_user_token])
response = upload(upload_url, form, timeout, fail_on_error)
if parse_response(response, upload_url, fail_on_error)
UI.success("Release URL: #{Actions.lane_context[SharedValues::ZEALOT_RELEASE_URL]}")
UI.success("QRCode URL: #{Actions.lane_context[SharedValues::ZEALOT_QRCODE_URL]}")
UI.success("Download URL: #{Actions.lane_context[SharedValues::ZEALOT_INSTALL_URL]}")
UI.success('Build successfully uploaded to Zealot.')
end
end
def self.upload(upload_url, form, timeout, fail_on_error)
require 'faraday'
require 'faraday_middleware'
UI.success("Uploading to #{upload_url} ...")
connection = Faraday.new(url: upload_url) do |builder|
builder.request(:multipart)
builder.request(:url_encoded)
builder.request(:retry, max: 3, interval: 5)
builder.response(:json, content_type: /\bjson$/)
builder.use(FaradayMiddleware::FollowRedirects)
builder.adapter(:net_http)
end
begin
connection.post do |req|
req.url('/api/apps/upload')
req.options.timeout = timeout
req.body = form
end
rescue Faraday::Error::TimeoutError
show_error('Uploading build to Apphost timed out ⏳', fail_on_error)
end
end
private_class_method :upload
def self.parse_response(response, upload_url, fail_on_error)
return show_error("Error uploading to Apphost: empty response", fail_on_error) unless response
UI.verbose response.body.to_s
if (body = response.body) && (error = body['error'])
return show_error("Error uploading to Apphost: #{response.body}", fail_on_error)
end
Actions.lane_context[SharedValues::ZEALOT_RELEASE_URL] = body['release_url']
Actions.lane_context[SharedValues::ZEALOT_INSTALL_URL] = body['install_url']
Actions.lane_context[SharedValues::ZEALOT_QRCODE_URL] = body['qrcode_url']
true
end
private_class_method :parse_response
def self.build_params(params)
form = {
token: params[:token],
channel_key: params[:channel_key],
file: Faraday::UploadIO.new(params[:file], 'application/octet-stream')
}
form.merge(avialable_params(params))
end
private_class_method :build_params
UPLOAD_PARAMS_KEYS = %w[
name changelog release_type
slug source branch git_commit password
].freeze
def self.avialable_params(params)
UPLOAD_PARAMS_KEYS.each_with_object({}) do |key, obj|
value = params.fetch(key.to_sym, ask: false)
obj[key.to_sym] = value if value && !value.empty?
end
end
private_class_method :avialable_params
def self.print_table(form, hide_user_token)
rows = form.dup
rows[:file] = rows[:file].path if rows[:file]
rows[:token] = '*' * 10 if hide_user_token
puts Terminal::Table.new(
title: "Summary for zealot #{Fastlane::Zealot::VERSION}".green,
rows: rows
)
end
def self.show_error(message, fail_on_error)
if fail_on_error
UI.user_error!(message)
else
UI.error(message)
end
false
end
private_class_method :show_error
#####################################################
# @!group Documentation
#####################################################
def self.description
'Upload a new build to Zealot'
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :endpoint,
env_name: 'ZEALOT_ENDPOINT',
description: 'The endpoint of zealot',
type: String),
FastlaneCore::ConfigItem.new(key: :token,
env_name: 'ZEALOT_TOKEN',
description: 'The token of user',
sensitive: true,
verify_block: proc do |value|
UI.user_error!("No user token for Zealot given, pass using `token: 'token'`") if value.nil? || value.empty?
end,
type: String),
FastlaneCore::ConfigItem.new(key: :channel_key,
env_name: 'ZEALOT_CHANNEL_KEY',
description: 'The key of app\'s channel',
type: String),
FastlaneCore::ConfigItem.new(key: :file,
env_name: 'ZEALOT_FILE',
description: 'The path of app file. Optional if you use the `gym`, `ipa`, `xcodebuild` or `gradle` action. ',
default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH] || Dir['*.ipa'].last || Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] || Dir['*.apk'].last,
verify_block: proc do |value|
UI.user_error!("Couldn't pass empty to file") if value.nil? || value.empty?
UI.user_error!("Couldn't find file at path '#{value}'") unless File.exist?(value)
end,
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :name,
env_name: 'ZEALOT_NAME',
description: 'The name of app to display on zealot',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :changelog,
env_name: 'ZEALOT_CHANGELOG',
description: 'The changelog of app',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :slug,
env_name: 'ZEALOT_SLUG',
description: 'The slug of app',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :release_type,
env_name: 'ZEALOT_RELEASE_TYPE',
description: 'The release type of app',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :branch,
env_name: 'ZEALOT_BRANCH',
description: 'The name of git branch',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :git_commit,
env_name: 'ZEALOT_GIT_COMMIT',
description: 'The hash of git commit',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :password,
env_name: 'ZEALOT_PASSWORD',
description: 'The password of app to download',
optional: true,
type: String),
FastlaneCore::ConfigItem.new(key: :source,
env_name: 'ZEALOT_SOURCE',
description: 'The name of upload source',
optional: true,
default_value: 'fastlane',
type: String),
FastlaneCore::ConfigItem.new(key: :timeout,
env_name: 'ZEALOT_TIMEOUT',
description: 'Request timeout in seconds',
type: Integer,
optional: true),
FastlaneCore::ConfigItem.new(key: :hide_user_token,
env_name: 'ZEALOT_HIDE_USER_TOKEN',
description: 'replase user token to *** to keep secret',
optional: true,
default_value: true,
type: Boolean,),
FastlaneCore::ConfigItem.new(key: :fail_on_error,
env_name: 'ZEALOT_FAIL_ON_ERROR',
description: 'Should an error uploading app cause a failure? (true/false)',
optional: true,
default_value: false,
type: Boolean)
]
end
def self.example_code
[
'zealot(
endpoint: "...",
token: "...",
channel_key: "..."
)',
'zealot(
endpoint: "...",
token: "...",
channel_key: "...",
name: "custom_app_name",
release_type: "adhoc",
branch: "develop",
git_commit: "8s20an32",
source: "jenkins",
file: "./app.{ipa,apk}"
)',
]
end
def self.output
[
[
'ZEALOT_RELEASE_URL', 'The release URL of the newly uploaded build',
'ZEALOT_INSTALL_URL', 'The install URL of the newly uploaded build',
'ZEALOT_QRCODE_URL', 'The QRCode URL of the newly uploaded build'
]
]
end
def self.category
:beta
end
def self.authors
['icyleaf']
end
def self.is_supported?(_)
true
end
end
end
end
@@ -0,0 +1,16 @@
require 'fastlane_core/ui/ui'
module Fastlane
UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
module Helper
class ZealotHelper
# class methods that you define here become available in your action
# as `Helper::ZealotHelper.your_method`
#
def self.show_message
UI.message("Hello from the zealot plugin helper!")
end
end
end
end
+5
View File
@@ -0,0 +1,5 @@
module Fastlane
module Zealot
VERSION = "0.1.0"
end
end
+15
View File
@@ -0,0 +1,15 @@
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
require 'simplecov'
# SimpleCov.minimum_coverage 95
SimpleCov.start
# This module is only used to check the environment is currently a testing env
module SpecHelper
end
require 'fastlane' # to import the Action super class
require 'fastlane/plugin/zealot' # import the actual plugin
Fastlane.load_actions # load other actions (in case your plugin calls other actions or shared values)
+9
View File
@@ -0,0 +1,9 @@
describe Fastlane::Actions::ZealotAction do
describe '#run' do
it 'prints a message' do
expect(Fastlane::UI).to receive(:message).with("The zealot plugin is working!")
Fastlane::Actions::ZealotAction.run(nil)
end
end
end