fastlane-plugin-fastci 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3146bd84ef5e60a31ebd02b181d9bed929cac5e9a651a7731bc1408f537ce1e3
4
+ data.tar.gz: 03f0624a9601c1c6f4f06128aa1f628bfaae08583845a05f179b84a667400610
5
+ SHA512:
6
+ metadata.gz: 822e2c959fab80db94225eab444117546d4816def1b5cd05cbcfba87a23bafe76d6ce8bc28b02f0d9913254186943f571c47288e46122b2da8771c30922e1a70
7
+ data.tar.gz: 0baf543b4f84328b7c7cea5e09e72f4a35a0c1d508887a90e84985027d830ba06b623cd22bc788bf16135398febd94578fa20f621302195f3a4e364583c0f2a3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Watermelon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # fastci plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-ld)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-ld`, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin fastci
11
+ ```
12
+
13
+ ## About fastci
14
+
15
+ 这个插件集成了打包、上传 appstore、上传蒲公英、钉钉通知、无用图片检测、无用代码检测、重复代码检测、swiftlint 代码检测
16
+
17
+ ## Example
18
+
19
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
20
+
21
+ ## About _fastlane_
22
+
23
+ _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).
@@ -0,0 +1,116 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 代码分析
7
+ class AnalyzeSwiftlintAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始代码分析 |*************")
10
+
11
+ # 检查是否安装了 SwiftLint
12
+ unless system("which swiftlint > /dev/null")
13
+ sh("brew install swiftlint")
14
+ end
15
+
16
+ is_all = params[:is_all] || true
17
+ is_from_package = params[:is_from_package] || false
18
+ configuration = params[:configuration] || "Debug"
19
+
20
+ # 如果不是从打包流程调用,需要先构建项目
21
+ if is_from_package == false
22
+ UI.message("*************| 构建项目以生成索引存储 |*************")
23
+
24
+ options = {
25
+ clean: true,
26
+ silent: true,
27
+ workspace: Environment.workspace,
28
+ scheme: Environment.scheme,
29
+ configuration: configuration,
30
+ buildlog_path: Constants.BUILD_LOG_DIR,
31
+ skip_archive: true,
32
+ skip_package_ipa: true
33
+ }
34
+ config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
35
+ Gym::Manager.new.work(config)
36
+ end
37
+
38
+ log_dir = File.expand_path(Constants.BUILD_LOG_DIR)
39
+ log_file = sh("ls -t #{log_dir}/*.log | head -n 1").strip
40
+
41
+ analyze_file = File.expand_path(Constants.SWIFTLINT_ANALYZE_FILE)
42
+
43
+ if is_all
44
+ sh "swiftlint analyze --compiler-log-path #{log_file} > #{analyze_file} || true"
45
+ else
46
+ commit_hash = params[:commit_hash] || read_cached_txt(Constants.COMMIT_HASH_FILE)
47
+ swift_files = CommonHelper.get_git_modified_swift_files(commit_hash)
48
+
49
+ if swift_files.empty?
50
+ UI.message("*************|❗没有 Swift 变更文件,跳过代码分析❗|*************")
51
+ return
52
+ end
53
+
54
+ files_to_analyze = swift_files.join(" ")
55
+ sh "swiftlint analyze --compiler-log-path #{log_file} #{files_to_analyze} > #{analyze_file} || true"
56
+ end
57
+
58
+ # 输出代码分析报告
59
+ UI.message("*************| 开始输出代码分析报告 |*************")
60
+
61
+ CommonHelper.generate_and_open_html(
62
+ "Code Analyze Report",
63
+ "generate_lint_html.py",
64
+ Constants.SWIFTLINT_ANALYZE_FILE,
65
+ Constants.SWIFTLINT_ANALYZE_HTML_FILE
66
+ )
67
+ end
68
+
69
+ def self.description
70
+ "静态代码分析"
71
+ end
72
+
73
+ def self.available_options
74
+ [
75
+ FastlaneCore::ConfigItem.new(
76
+ key: :is_all,
77
+ description: "是否检查所有文件",
78
+ optional: true,
79
+ default_value: true,
80
+ type: Boolean
81
+ ),
82
+ FastlaneCore::ConfigItem.new(
83
+ key: :is_from_package,
84
+ description: "是否从打包流程调用",
85
+ optional: true,
86
+ default_value: false,
87
+ type: Boolean
88
+ ),
89
+ FastlaneCore::ConfigItem.new(
90
+ key: :configuration,
91
+ description: "构建配置",
92
+ optional: true,
93
+ default_value: "Release",
94
+ type: String
95
+ ),
96
+ FastlaneCore::ConfigItem.new(
97
+ key: :commit_hash,
98
+ description: "上一次提交哈希, 会比较该哈希到最新哈希",
99
+ optional: true,
100
+ default_value: nil,
101
+ type: String
102
+ )
103
+ ]
104
+ end
105
+
106
+ def self.is_supported?(platform)
107
+ platform == :ios
108
+ end
109
+
110
+ def self.category
111
+ :testing
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,105 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 重复代码检查
7
+ class DetectDuplicityCodeAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始重复代码检查 |*************")
10
+
11
+ # 检查是否安装了 PMD
12
+ unless system("which pmd > /dev/null")
13
+ sh("brew install pmd")
14
+ end
15
+
16
+ is_all = params[:is_all] || true
17
+ # 项目路径
18
+ project_path = Dir.pwd
19
+
20
+ if is_all
21
+ detect_path = "#{project_path}"
22
+ pmd_command = "--dir #{detect_path}"
23
+ else
24
+ commit_hash = params[:commit_hash] || CommonHelper.read_cached_txt(COMMIT_HASH_FILE)
25
+ swift_files = CommonHelper.get_git_modified_swift_files(commit_hash)
26
+
27
+ if swift_files.empty?
28
+ UI.message("*************|❗没有 Swift 变更文件,跳过重复代码检查❗|*************")
29
+ return
30
+ end
31
+
32
+ # 创建文件列表
33
+ file_list_path = Constants.DUPLICITY_CODE_MODIFIED_FILE
34
+ swift_files_absolute = swift_files.map { |file| File.expand_path(file, project_path) }
35
+ File.open(file_list_path, "w") { |file| file.puts(swift_files_absolute) }
36
+
37
+ pmd_command = "--file-list #{file_list_path}"
38
+ end
39
+
40
+ # 忽略文件夹
41
+ ignore_paths = [
42
+ "#{project_path}/Pods/**"
43
+ ]
44
+ exclude_options = ignore_paths.map { |path| "--exclude #{path}" }.join(" ")
45
+
46
+ sh("
47
+ pmd cpd \
48
+ --minimum-tokens 100 \
49
+ #{pmd_command} \
50
+ --language swift \
51
+ --format xml \
52
+ #{exclude_options} \
53
+ --ignore-annotations \
54
+ --ignore-identifiers \
55
+ --ignore-literals \
56
+ --no-fail-on-error \
57
+ --no-fail-on-violation \
58
+ > #{Constants.DUPLICITY_CODE_FILE}
59
+ ")
60
+
61
+ # 输出无用代码检查报告
62
+ UI.message("*************| 开始输出重复代码检查报告 |*************")
63
+
64
+ CommonHelper.generate_and_open_html(
65
+ "Duplicity Code Report",
66
+ "generate_duplicity_code_html.py",
67
+ Constants.DUPLICITY_CODE_FILE,
68
+ Constants.DUPLICITY_CODE_HTML_FILE
69
+ )
70
+ end
71
+
72
+ def self.description
73
+ "重复代码检测"
74
+ end
75
+
76
+ def self.available_options
77
+ [
78
+ FastlaneCore::ConfigItem.new(
79
+ key: :is_all,
80
+ description: "是否检查所有文件",
81
+ optional: true,
82
+ default_value: true,
83
+ type: Boolean
84
+ ),
85
+ FastlaneCore::ConfigItem.new(
86
+ key: :commit_hash,
87
+ description: "上一次提交哈希, 会比较该哈希到最新哈希",
88
+ optional: true,
89
+ default_value: nil,
90
+ type: String
91
+ )
92
+ ]
93
+ end
94
+
95
+ def self.is_supported?(platform)
96
+ platform == :ios
97
+ end
98
+
99
+ def self.category
100
+ :testing
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,102 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 无用代码检查
7
+ class DetectUnusedCodeAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始无用代码检查 |*************")
10
+
11
+ # 检查是否安装了 Periphery
12
+ unless system("which periphery > /dev/null")
13
+ sh("brew install periphery")
14
+ end
15
+
16
+ is_from_package = params[:is_from_package] || false
17
+ configuration = params[:configuration] || "Debug"
18
+
19
+ # 如果不是从打包流程调用,需要先构建项目
20
+ if is_from_package == false
21
+ puts "*************| 构建项目以生成索引存储 |*************"
22
+
23
+ options = {
24
+ clean: true,
25
+ silent: true,
26
+ workspace: Environment.workspace,
27
+ scheme: Environment.scheme,
28
+ configuration: configuration,
29
+ buildlog_path: Constants.BUILD_LOG_DIR,
30
+ skip_archive: true,
31
+ skip_package_ipa: true
32
+ }
33
+ config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
34
+ Gym::Manager.new.work(config)
35
+ end
36
+
37
+ log_dir = File.expand_path(Constants.BUILD_LOG_DIR)
38
+ log_file = sh("ls -t #{log_dir}/*.log | head -n 1").strip
39
+ index_store_path = CommonHelper.extract_index_store_path(log_file)
40
+
41
+ schemes = Environment.schemes
42
+ if schemes.empty?
43
+ schemes = Environment.scheme
44
+ end
45
+
46
+ # 运行 Periphery 扫描
47
+ periphery_output = sh("
48
+ periphery scan \
49
+ --skip-build \
50
+ --project #{Environment.workspace} \
51
+ --schemes #{schemes.map(&:strip).join(" ")} \
52
+ --index-store-path '#{index_store_path}' \
53
+ --format xcode 2>/dev/null || true
54
+ ")
55
+
56
+ CommonHelper.write_cached_txt(Constants.UNUSED_CODE_FILE, periphery_output)
57
+
58
+ # 输出无用代码检查报告
59
+ UI.message("*************| 开始输出无用代码检查报告 |*************")
60
+
61
+ CommonHelper.generate_and_open_html(
62
+ "Unused Code Report",
63
+ "generate_unused_code_html.py",
64
+ Constants.UNUSED_CODE_FILE,
65
+ Constants.UNUSED_CODE_HTML_FILE
66
+ )
67
+ end
68
+
69
+ def self.description
70
+ "无用代码检测"
71
+ end
72
+
73
+ def self.available_options
74
+ [
75
+ FastlaneCore::ConfigItem.new(
76
+ key: :is_from_package,
77
+ description: "是否从打包流程调用",
78
+ optional: true,
79
+ default_value: false,
80
+ type: Boolean
81
+ ),
82
+ FastlaneCore::ConfigItem.new(
83
+ key: :configuration,
84
+ description: "构建配置",
85
+ optional: true,
86
+ default_value: "Release",
87
+ type: String
88
+ )
89
+ ]
90
+ end
91
+
92
+ def self.is_supported?(platform)
93
+ platform == :ios
94
+ end
95
+
96
+ def self.category
97
+ :testing
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,54 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 无用图片检查
7
+ class DetectUnusedImageAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始无用图片检查 |*************")
10
+
11
+ # 检查是否安装了 FengNiao
12
+ unless system("which fengniao > /dev/null")
13
+ sh("brew install mint")
14
+ sh("mint install onevcat/fengniao")
15
+ end
16
+
17
+ fengniao_output = sh("
18
+ fengniao \
19
+ --list-only \
20
+ --exclude Carthage Pods \
21
+ ")
22
+
23
+ CommonHelper.write_cached_txt(Constants.UNUSED_IMAGE_FILE, fengniao_output)
24
+
25
+ # 输出无用图片检查报告
26
+ UI.message("*************| 开始输出无用图片检查报告 |*************")
27
+
28
+ CommonHelper.generate_and_open_html(
29
+ "Unused Image Report",
30
+ "generate_unused_image_html.py",
31
+ Constants.UNUSED_IMAGE_FILE,
32
+ Constants.UNUSED_IMAGE_HTML_FILE
33
+ )
34
+ end
35
+
36
+ def self.description
37
+ "无用图片检测"
38
+ end
39
+
40
+ def self.available_options
41
+ []
42
+ end
43
+
44
+ def self.is_supported?(platform)
45
+ platform == :ios
46
+ end
47
+
48
+ def self.category
49
+ :testing
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 安装 p12 证书
7
+ class InstallCertificateAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始安装 p12 证书 |*************")
10
+
11
+ certificate_paths = Dir.glob("../#{Environment.certificate_folder_name}/*.p12")
12
+ certificate_paths.each do |path|
13
+ import_certificate(
14
+ certificate_path: File.expand_path(path),
15
+ certificate_password: "#{Environment.certificate_password}",
16
+ keychain_name: "login.keychain",
17
+ keychain_password: "#{Environment.keychain_password}"
18
+ )
19
+ end
20
+ end
21
+
22
+ def self.description
23
+ "安装 p12 证书"
24
+ end
25
+
26
+ def self.available_options
27
+ []
28
+ end
29
+
30
+ def self.is_supported?(platform)
31
+ platform == :ios
32
+ end
33
+
34
+ def self.category
35
+ :code_signing
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 安装 provisioningProfile
7
+ class InstallProfileAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始安装 provisioningProfile |*************")
10
+
11
+ provisioning_profile_paths = Dir.glob("../#{Environment.provisioningProfile_folder_name}/*.mobileprovision")
12
+ provisioning_profile_paths.each do |path|
13
+ install_provisioning_profile(
14
+ path: File.expand_path(path)
15
+ )
16
+ end
17
+ end
18
+
19
+ def self.description
20
+ "安装 provisioningProfile"
21
+ end
22
+
23
+ def self.available_options
24
+ []
25
+ end
26
+
27
+ def self.is_supported?(platform)
28
+ platform == :ios
29
+ end
30
+
31
+ def self.category
32
+ :code_signing
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ require 'fastlane/action'
2
+ include Fastlane::Helper
3
+
4
+ module Fastlane
5
+ module Actions
6
+ # 钉钉通知
7
+ class NotiDingdingAction < Action
8
+ def self.run(params)
9
+ UI.message("*************| 开始钉钉消息通知 |*************")
10
+ notiText = params[:notiText] || ""
11
+
12
+ curl = %Q{
13
+ curl 'https://oapi.dingtalk.com/robot/send?access_token=#{Environment.dingdingToken}' \
14
+ -H 'Content-Type:application/json' \
15
+ -d '{
16
+ "msgtype":"markdown",
17
+ "markdown":{
18
+ "title":"#{Environment.scheme} 打包通知",
19
+ "text":"#{notiText}"
20
+ }
21
+ }'
22
+ }
23
+ system curl
24
+ end
25
+
26
+ def self.description
27
+ "钉钉通知"
28
+ end
29
+
30
+ def self.available_options
31
+ [
32
+ FastlaneCore::ConfigItem.new(
33
+ key: :notiText,
34
+ description: "要发送的钉钉通知内容",
35
+ optional: false, # 是否可选
36
+ type: String
37
+ ),
38
+ ]
39
+ end
40
+
41
+ def self.is_supported?(platform)
42
+ platform == :ios
43
+ end
44
+
45
+ def self.category
46
+ :notifications
47
+ end
48
+
49
+ end
50
+ end
51
+ end