fastlane-plugin-stream_actions 0.4.15 → 0.4.17

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
  SHA256:
3
- metadata.gz: d8d838fe7bdba0809e724b8ceffcdd8a9681c65597a7315085c8eda719afd520
4
- data.tar.gz: e8e093b56d26b398288396cff51df3b063e776a80a9aa82231d9859d5b76317a
3
+ metadata.gz: 46352486158d9628e7e172f2df0b4ab29f6b0ac575e66fd8fe8c92a014c43b42
4
+ data.tar.gz: fad45a69555125e8bc8b55e026e47b1fb35348450abdcc0357c176c57a4ecd21
5
5
  SHA512:
6
- metadata.gz: abcc77e118f5c4c023c099544a850f0e8f2989d83510d15e9bd086b0f1a9fe00f6ea07ef753ca3cf382508032f5a26b6877ca06d3dbfb040776ab0c1a6df16f1
7
- data.tar.gz: 3d7c1c83fab37d3096946d1064d30e82a733995dd7cca21813f568de18d64f61dca101241a75ca1cd8f93a0f01f308257406fead0f15ff7e62f1f47a12c09839
6
+ metadata.gz: 9a5a36c1870b78a0764e92eaf5a6374b189df978c29b116815622cd123a5c48588408170265724c9601c2e607589ed5a1c93e8fcc012152d6ced161363511fa7
7
+ data.tar.gz: 1f15575af4166ba23aa48d292bd0e1590a58b1094c8a3678235531af6727b8a384264f18f194c534555b0b47d91a72a8250f1c132dc762d012ad99fc0ac64f12
@@ -9,7 +9,14 @@ module Fastlane
9
9
  class BuildTestCoverageMapAction < Action
10
10
  def self.run(params)
11
11
  source_root = File.expand_path(params[:source_root] || Dir.pwd)
12
- sources, tests = params[:class_dirs] ? jacoco_coverage(params, source_root) : llvm_coverage(params, source_root)
12
+ sources, tests =
13
+ if params[:class_dirs]
14
+ jacoco_coverage(params, source_root)
15
+ elsif params[:binaries]
16
+ llvm_coverage(params, source_root)
17
+ else
18
+ lcov_coverage(params, source_root)
19
+ end
13
20
 
14
21
  carry_over_failed_tests(tests, sources, params) unless params[:failed_tests].to_a.empty?
15
22
  drop_untracked(tests, sources, source_root)
@@ -31,7 +38,6 @@ module Fastlane
31
38
  test_dirs = Dir.glob(File.join(params[:coverage_dir], '*', '*')).select { |dir| File.directory?(dir) }
32
39
  UI.user_error!("No per-test coverage found in '#{params[:coverage_dir]}'") if test_dirs.empty?
33
40
 
34
- UI.user_error!('Pass the instrumented binaries or the class_dirs') if params[:binaries].to_a.empty?
35
41
  binaries = params[:binaries].select { |binary| File.exist?(binary) }
36
42
  UI.user_error!('None of the instrumented binaries exist') if binaries.empty?
37
43
 
@@ -88,6 +94,24 @@ module Fastlane
88
94
  [source_of_class.values.flatten.uniq, tests]
89
95
  end
90
96
 
97
+ # Flutter: one folder of lcov files per test, `<coverage_dir>/<TestFile>/<test description>/`, e.g. from
98
+ # `flutter test --coverage --coverage-path <dir>/lcov.info --name <test>`. lcov lists every library the app loaded,
99
+ # so a file counts as executed when one of its lines has hits.
100
+ def self.lcov_coverage(params, source_root)
101
+ reports = Helper::TestCoverageMapHelper.lcov_reports(params[:coverage_dir])
102
+ UI.user_error!("No per-test coverage found in '#{params[:coverage_dir]}'") if reports.empty?
103
+
104
+ UI.message("Building the coverage map of #{reports.size} tests ⌛️")
105
+ base_dir = File.expand_path(params[:lcov_base_dir] || source_root)
106
+ sources = []
107
+ tests = reports.transform_values do |files|
108
+ files = files.map { |file, executed| [relative_path(File.expand_path(file, base_dir), source_root), executed] }.reject { |file, _| file.nil? }
109
+ sources |= files.map(&:first)
110
+ files.select(&:last).map(&:first).uniq
111
+ end
112
+ [sources, tests]
113
+ end
114
+
91
115
  # Class name => `<package path>/<source file name>`, from an XML report of the class files without execution data.
92
116
  # Classes without code (interfaces, annotations, constants) are empty `<class/>` elements and stay out, like
93
117
  # declaration-only files in llvm-cov. One report per folder, as JaCoCo fails on a class name that two folders
@@ -234,7 +258,7 @@ module Fastlane
234
258
  #####################################################
235
259
 
236
260
  def self.description
237
- 'Builds a map of which source files every test executes, from per-test LLVM or JaCoCo coverage'
261
+ 'Builds a map of which source files every test executes, from per-test LLVM, JaCoCo or lcov coverage'
238
262
  end
239
263
 
240
264
  def self.details
@@ -248,6 +272,9 @@ module Fastlane
248
272
  `<package>.<TestClass>#<testMethod>.ec`, which the test orchestrator writes with `-e coverage true
249
273
  -e coverageFilePath <dir>/`. A source file counts as executed when one of its classes ran a probe.
250
274
 
275
+ Flutter: pass neither, and a `coverage_dir` with one folder of lcov `.info` files per test, `<TestFile>/<test>/`,
276
+ e.g. from `flutter test --coverage --name <test>`. A source file counts as executed when one of its lines has hits.
277
+
251
278
  The resulting JSON map feeds `select_impacted_tests`.
252
279
  DETAILS
253
280
  end
@@ -260,7 +287,8 @@ module Fastlane
260
287
  [
261
288
  FastlaneCore::ConfigItem.new(
262
289
  key: :coverage_dir,
263
- description: 'Folder with per-test profiles, laid out as <TestClass>/<testMethod>/*.profraw',
290
+ description: 'Folder with per-test coverage: <TestClass>/<testMethod>/*.profraw (iOS), *.ec files (Android) ' \
291
+ 'or <TestFile>/<test>/*.info (Flutter)',
264
292
  verify_block: proc { |path| UI.user_error!("Coverage folder '#{path}' does not exist") unless File.directory?(path) }
265
293
  ),
266
294
  FastlaneCore::ConfigItem.new(
@@ -280,6 +308,11 @@ module Fastlane
280
308
  description: 'Android: path to the JaCoCo CLI jar (org.jacoco.cli nodeps). Downloaded when not set',
281
309
  optional: true
282
310
  ),
311
+ FastlaneCore::ConfigItem.new(
312
+ key: :lcov_base_dir,
313
+ description: 'Flutter: folder that relative lcov SF paths start from (the package that ran the tests), source_root by default',
314
+ optional: true
315
+ ),
283
316
  FastlaneCore::ConfigItem.new(
284
317
  key: :source_root,
285
318
  description: 'Repository root, the current directory by default (fastlane runs actions from the repository root). ' \
@@ -17,7 +17,8 @@ module Fastlane
17
17
  version_number
18
18
  end
19
19
 
20
- other_action.set_github_release(
20
+ make_latest = params[:make_latest]
21
+ release = other_action.set_github_release(
21
22
  repository_name: params[:github_repo],
22
23
  api_token: params[:github_token],
23
24
  name: version_number,
@@ -25,8 +26,10 @@ module Fastlane
25
26
  description: changes,
26
27
  commitish: other_action.current_branch,
27
28
  upload_assets: params[:upload_assets],
28
- is_prerelease: version_number.downcase.include?('beta')
29
+ is_prerelease: version_number.downcase.include?('beta'),
30
+ is_draft: !make_latest
29
31
  )
32
+ publish_without_marking_latest(params, release) unless make_latest
30
33
  end
31
34
 
32
35
  unless skip_pods
@@ -43,6 +46,19 @@ module Fastlane
43
46
  other_action.ensure_git_status_clean unless params[:skip_git_status_check]
44
47
  end
45
48
 
49
+ # GitHub marks a release as Latest when it is created, so a release that must not be Latest
50
+ # (e.g. a patch of an older major version) is created as a draft and published with `make_latest: false`.
51
+ def self.publish_without_marking_latest(params, release)
52
+ UI.user_error!('Failed to create the GitHub release') unless release.kind_of?(Hash) && release['id']
53
+
54
+ other_action.github_api(
55
+ api_token: params[:github_token],
56
+ http_method: 'PATCH',
57
+ path: "repos/#{params[:github_repo]}/releases/#{release['id']}",
58
+ body: { draft: false, make_latest: 'false' }
59
+ )
60
+ end
61
+
46
62
  def self.ensure_release_tag_is_new(version_number)
47
63
  if other_action.git_tag_exists(tag: version_number)
48
64
  UI.user_error!("Tag for version #{version_number} already exists!")
@@ -132,6 +148,13 @@ module Fastlane
132
148
  is_string: true,
133
149
  optional: true
134
150
  ),
151
+ FastlaneCore::ConfigItem.new(
152
+ key: :make_latest,
153
+ description: 'Mark the GitHub release as Latest? Pass false for releases of an older major version',
154
+ is_string: false,
155
+ optional: true,
156
+ default_value: true
157
+ ),
135
158
  FastlaneCore::ConfigItem.new(
136
159
  key: :upload_assets,
137
160
  description: 'Path to assets to be uploaded with the release',
@@ -6,9 +6,10 @@ module Fastlane
6
6
  module Actions
7
7
  class SelectImpactedTestsAction < Action
8
8
  CODE_EXTENSIONS = %w[.swift .m .mm .h .kt .java .dart .js .jsx .ts .tsx].freeze
9
- # Kotlin's `enum class` and `fun interface` name the type after the second keyword.
10
- MODIFIERS = '(?:(?:public|open|internal|fileprivate|private|protected|final|indirect|nonisolated|data|sealed|abstract|annotation|value|inline|inner|enum(?=\\s+class\\b)|fun(?=\\s+interface\\b)|export|default)\\s+)*'
11
- DECLARATION = /^\s*(?:@\w+(?:\([^)\n]*\))?\s+)*#{MODIFIERS}(class|struct|enum|protocol|actor|typealias|extension|interface|object)\s+([A-Za-z_]\w*)/.freeze
9
+ # Kotlin's `enum class` and `fun interface`, and Dart's `interface class` and `mixin class`, name the type after the second keyword.
10
+ MODIFIERS = '(?:(?:public|open|internal|fileprivate|private|protected|final|indirect|nonisolated|data|sealed|abstract|base|annotation|value|inline|inner|enum(?=\\s+class\\b)|fun(?=\\s+interface\\b)|interface(?=\\s+class\\b)|mixin(?=\\s+class\\b)|export|default)\\s+)*'
11
+ # Dart's `extension type` declares a new type, unlike an `extension`.
12
+ DECLARATION = /^\s*(?:@\w+(?:\([^)\n]*\))?\s+)*#{MODIFIERS}(class|struct|enum|protocol|actor|typealias|typedef|mixin|extension\s+type(?:\s+const)?|extension|interface|object)\s+([A-Za-z_]\w*)/.freeze
12
13
 
13
14
  def self.run(params)
14
15
  source_root = File.expand_path(params[:source_root] || Dir.pwd)
@@ -58,6 +58,26 @@ module Fastlane
58
58
  end
59
59
  end
60
60
 
61
+ # `TestFile/test` => [[source file, executed?], ...] of the lcov reports in `<coverage_dir>/<TestFile>/<test>/`.
62
+ def self.lcov_reports(coverage_dir)
63
+ Dir.glob(File.join(coverage_dir, '*', '*', '*.info'))
64
+ .group_by { |report| File.dirname(report).split('/').last(2).join('/') }
65
+ .transform_values { |reports| reports.flat_map { |report| lcov_files(report) } }
66
+ end
67
+
68
+ # [[source file, executed?], ...] of an lcov report, in the order it lists them.
69
+ def self.lcov_files(report)
70
+ files = []
71
+ File.foreach(report) do |line|
72
+ if line.start_with?('SF:')
73
+ files << [line[3..-1].strip, false]
74
+ elsif line.start_with?('DA:') && !files.empty? && line.split(',')[1].to_i.positive?
75
+ files.last[1] = true
76
+ end
77
+ end
78
+ files
79
+ end
80
+
61
81
  def self.read_map(path)
62
82
  map = JSON.parse(File.read(path))
63
83
  UI.user_error!("Unsupported coverage map version #{map['version']}") unless map['version'] == MAP_VERSION
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module StreamActions
3
- VERSION = '0.4.15'
3
+ VERSION = '0.4.17'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-stream_actions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.15
4
+ version: 0.4.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - GetStream
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-20 00:00:00.000000000 Z
11
+ date: 2026-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xctest_list