ETLane 0.1.42 → 0.1.46

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.
@@ -0,0 +1,5 @@
1
+ struct Images: Codable {
2
+ let err: String?
3
+ let images: [String: String]?
4
+ let status: Int?
5
+ }
@@ -0,0 +1,80 @@
1
+ import Common
2
+ import Foundation
3
+
4
+ final class PreviewDownloader {
5
+
6
+ private let outputURL: URL
7
+ private let session = URLSession.shared
8
+
9
+ init(outputURL: URL) {
10
+ self.outputURL = outputURL
11
+ }
12
+
13
+ func download(deploys: [Deploy]) throws {
14
+ let downloadGroup = DispatchGroup()
15
+ let fm = FileManager.default
16
+ let previewsURL = self.outputURL.appendingPathComponent("previews")
17
+ try fm.createDirectory(at: previewsURL, withIntermediateDirectories: true, attributes: [:])
18
+
19
+ for deploy in deploys {
20
+ let localeURL = previewsURL.appendingPathComponent(deploy[.locale])
21
+
22
+ do {
23
+ try fm.createDirectory(at: localeURL, withIntermediateDirectories: true, attributes: [:])
24
+ } catch {
25
+ print("Create locale folder error: \(error.locd)")
26
+ }
27
+ if let preview = URL(string: deploy[.iPhone8]) {
28
+ let to = localeURL.appendingPathComponent("iphone58.mp4")
29
+ downloadGroup.enter()
30
+ self.download(from: preview, to: to) {
31
+ downloadGroup.leave()
32
+ }
33
+ }
34
+ if let preview = URL(string: deploy[.iPhone11]) {
35
+ let to = localeURL.appendingPathComponent("iphone65.mp4")
36
+ downloadGroup.enter()
37
+ self.download(from: preview, to: to) {
38
+ downloadGroup.leave()
39
+ }
40
+ }
41
+ let timestamp = deploy[.previewTimestamp]
42
+ if timestamp.isEmpty {
43
+ do {
44
+ let timestampURL = localeURL.appendingPathComponent("timestamp")
45
+ try timestamp.write(to: timestampURL, atomically: true, encoding: .utf8)
46
+ print("Save timestamp: \(timestamp)")
47
+ } catch {
48
+ print("Timestamp write error: \(error)")
49
+ }
50
+ }
51
+ downloadGroup.wait()
52
+ }
53
+
54
+ }
55
+
56
+ private func download(from: URL, to: URL, completion: @escaping () -> Void) {
57
+ print("Download \(from) to: \(to)")
58
+ let request = URLRequest(
59
+ url: from,
60
+ cachePolicy: .
61
+ reloadIgnoringLocalCacheData,
62
+ timeoutInterval: 5 * 60
63
+ )
64
+ self.session.downloadTask(with: request) { (url, response, error) in
65
+ if let url = url, error == nil {
66
+ do {
67
+ try? FileManager.default.removeItem(at: to)
68
+ try FileManager.default.copyItem(at: url, to: to)
69
+ print("Did finish download: \(from)")
70
+ } catch {
71
+ print("Copy error: \(error)")
72
+ }
73
+ } else if let error = error {
74
+ print("Download error: \(error)")
75
+ }
76
+ completion()
77
+ }.resume()
78
+ }
79
+
80
+ }
@@ -0,0 +1,25 @@
1
+ import ArgumentParser
2
+
3
+ struct ResourcesParser: ParsableArguments {
4
+
5
+ @Option(name: .customLong("output"), help: "Path to output folder")
6
+ var output: String
7
+
8
+ @Argument(help: "Google TSV path")
9
+ var tsv: String
10
+
11
+ @Option(help: "Download screenshots?")
12
+ var downloadScreenshots = false
13
+
14
+ @Option(help: "Figma token")
15
+ var figmaToken: String
16
+
17
+ @Option(help: "Figma screenshots page")
18
+ var figmaPage: String?
19
+
20
+ @Option(help: "Figma project id")
21
+ var figmaProjectId: String
22
+
23
+ @Option(help: "Download preview?")
24
+ var downloadPreview = false
25
+ }
@@ -0,0 +1,150 @@
1
+ import Common
2
+ import Foundation
3
+
4
+ final class ScreenshotDownloader {
5
+
6
+ private let outputURL: URL
7
+ private let token: String
8
+ private let projectId: String
9
+ private let api: Api
10
+ private let cacheURL: URL
11
+
12
+ init(figmaApi: Api, outputURL: URL, token: String, projectId: String) {
13
+ self.outputURL = outputURL
14
+ self.token = token
15
+ self.projectId = projectId
16
+ self.api = figmaApi
17
+
18
+ self.cacheURL = self.outputURL.appendingPathComponent("screenshots_cache")
19
+ let fm = FileManager.default
20
+ try? fm.createDirectory(at: self.cacheURL, withIntermediateDirectories: true, attributes: [:])
21
+ }
22
+
23
+ private func downloadIds(_ ids: [String], repeatCount: Int = 5, scale: Int) -> Images? {
24
+ if repeatCount < 0 {
25
+ return nil
26
+ }
27
+ do {
28
+ let images = try self.api.images(
29
+ token: self.token,
30
+ projectId: self.projectId,
31
+ ids: ids,
32
+ scale: scale
33
+ )
34
+ if let err = images.err {
35
+ print("⛔️ Download error \(repeatCount - 1), try one more time: \(err)")
36
+ return self.downloadIds(ids, repeatCount: repeatCount - 1, scale: scale)
37
+ } else {
38
+ return images
39
+ }
40
+ } catch {
41
+ print("⛔️ Download batch error \(repeatCount - 1), try one more time after 15 sec: \(error.locd)")
42
+ Thread.sleep(forTimeInterval: 15)
43
+ return self.downloadIds(ids, repeatCount: repeatCount - 1, scale: scale)
44
+ }
45
+ }
46
+
47
+ func download(ids: [String], scale: Int) -> [Images] {
48
+ let downloadIDs = ids.unique
49
+ let batch = 10
50
+ var allImages = [Images]()
51
+ for idx in stride(from: downloadIDs.indices.lowerBound, to: downloadIDs.indices.upperBound, by: batch) {
52
+ print("⬇️ Fetching image batch: \(idx)")
53
+ let subsequence = downloadIDs[idx..<min(idx.advanced(by: batch), downloadIDs.count)]
54
+ if let images = self.downloadIds(Array(subsequence), repeatCount: 6, scale: scale) {
55
+ allImages.append(images)
56
+ self.downloadImages(images)
57
+ } else {
58
+ print("💥 Download batch error, maybe we should limit requests other way")
59
+ exit(1)
60
+ }
61
+ }
62
+ return allImages
63
+ }
64
+
65
+ private func downloadImages(_ images: Images) {
66
+ DispatchQueue.global().async {
67
+ if let images = images.images {
68
+ _ = DownloadBatch(images: images, url: self.cacheURL).download()
69
+ }
70
+ }
71
+ }
72
+
73
+ func download(screens: [Figma.Screen]) throws {
74
+ var imageIDs2x = [String]()
75
+ var imageIDs3x = [String]()
76
+
77
+ for screen in screens {
78
+ switch screen.device.scale {
79
+ case 2:
80
+ imageIDs2x.append(screen.id)
81
+ case 3:
82
+ imageIDs3x.append(screen.id)
83
+ default:
84
+ fatalError("🚨Unknown scale \(screen.device.scale)")
85
+ }
86
+ }
87
+
88
+ var allImages = [Images]()
89
+ allImages += self.download(ids: imageIDs2x, scale: 2)
90
+ allImages += self.download(ids: imageIDs3x, scale: 3)
91
+
92
+ var allImagesKeys = [String: String]()
93
+ allImages
94
+ .compactMap { $0.images }
95
+ .filter { !$0.isEmpty }
96
+ .forEach { (images) in
97
+ for image in images {
98
+ allImagesKeys[image.key] = image.value
99
+ }
100
+ }
101
+ let imageData = DownloadBatch(images: allImagesKeys, url: self.cacheURL).download()
102
+
103
+ let screenshotsURL = self.outputURL.appendingPathComponent("screenshots")
104
+ let fm = FileManager.default
105
+ do {
106
+ try fm.removeItem(at: screenshotsURL)
107
+ } catch {
108
+ print("Remove screenshots error: \(error)")
109
+ }
110
+ try fm.createDirectory(at: screenshotsURL, withIntermediateDirectories: true, attributes: [:])
111
+ print("ℹ️ Process screenshots at \(screenshotsURL)")
112
+ for screen in screens {
113
+
114
+ let localeURL = screenshotsURL.localeURL(for: screen)
115
+ do {
116
+ try fm.createDirectory(at: localeURL, withIntermediateDirectories: true, attributes: [:])
117
+
118
+ if let data = imageData[screen.id] {
119
+ print("ℹ️ Save screenshot \(localeURL.lastPathComponent)/\(screen.fileName)")
120
+ do {
121
+ try data.write(to: localeURL.appendingPathComponent(screen.fileName))
122
+ } catch {
123
+ print("⛔️ Save screenshot error: \(error.locd)")
124
+ }
125
+ }
126
+ } catch {
127
+ print("⛔️ Create locale folder error: \(error.locd)")
128
+ }
129
+ }
130
+ }
131
+
132
+ }
133
+
134
+ extension URL {
135
+ func localeURL(for screen: Figma.Screen) -> URL {
136
+ var url = self
137
+ if screen.device.isIMessage {
138
+ // скриншоты для iMessage должны лежать в папке iMessage/Locale/###.jpg
139
+ url.appendPathComponent("iMessage")
140
+ }
141
+ url.appendPathComponent(screen.locale)
142
+ return url
143
+ }
144
+ }
145
+
146
+ extension String {
147
+ var cacheName: String {
148
+ "\(self.MD5String).jpg"
149
+ }
150
+ }
@@ -0,0 +1,58 @@
1
+ import Foundation
2
+ import Common
3
+
4
+ do {
5
+ let options = ResourcesParser.parseOrExit()
6
+ let figmaApi = Api(baseURL: "https://api.figma.com/v1")
7
+
8
+ NSSetUncaughtExceptionHandler { (exception) in
9
+ let stack = exception.callStackReturnAddresses
10
+ print("Stack trace: \(stack)")
11
+ }
12
+
13
+ let deploys = try Deploy.fromTSV(options.tsv)
14
+
15
+ let output = (options.output as NSString).expandingTildeInPath
16
+ let outputURL = URL(fileURLWithPath: output)
17
+ let metadataURL = outputURL.appendingPathComponent("metadata")
18
+ let fm = FileManager.default
19
+ try fm.createDirectory(at: metadataURL, withIntermediateDirectories: true, attributes: [:])
20
+
21
+ print("Process metadate at \(metadataURL)")
22
+ for deploy in deploys {
23
+ let locale = metadataURL.appendingPathComponent(deploy[.locale])
24
+
25
+ do {
26
+ try fm.createDirectory(at: locale, withIntermediateDirectories: true, attributes: [:])
27
+ deploy.createFiles(at: locale)
28
+ } catch {
29
+ print("Create locale error: \(error)")
30
+ }
31
+ }
32
+
33
+ if options.downloadScreenshots, let pageId = options.figmaPage {
34
+ print("Load figma screenshots data")
35
+
36
+ let pages = try figmaApi.pages(token: options.figmaToken, projectId: options.figmaProjectId, page: pageId)
37
+ let screens = pages.screens(for: pageId)
38
+ print("Download figma screenshots data")
39
+ let downloader = ScreenshotDownloader(
40
+ figmaApi: figmaApi,
41
+ outputURL: outputURL,
42
+ token: options.figmaToken,
43
+ projectId: options.figmaProjectId
44
+ )
45
+ try downloader.download(screens: screens)
46
+ }
47
+
48
+ if options.downloadPreview {
49
+ let downloader = PreviewDownloader(outputURL: outputURL)
50
+ try downloader.download(deploys: deploys)
51
+ }
52
+
53
+ } catch {
54
+ print("\(error.locd)")
55
+ exit(1)
56
+ }
57
+ exit(0)
58
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ETLane
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.42
4
+ version: 0.1.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - teanet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-28 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'Xcode helper for upload builds and metadata
14
14
 
@@ -20,9 +20,32 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - Lanes/CommonFastfile
22
22
  - Lanes/ExampleAppfile
23
+ - Lanes/actions/README.md
24
+ - Lanes/actions/add_keys_to_lokalise.rb
25
+ - Lanes/actions/android/README.md
26
+ - Lanes/actions/android/lokalise_download.rb
27
+ - Lanes/actions/android/lokalise_upload.rb
28
+ - Lanes/actions/lokalise.rb
29
+ - Lanes/actions/lokalise_metadata.rb
30
+ - Lanes/actions/lokalise_upload.rb
31
+ - Lanes/actions/previews.rb
23
32
  - Scripts/Package.resolved
24
33
  - Scripts/Package.swift
25
34
  - Scripts/README.md
35
+ - Scripts/Sources/Common/Api.swift
36
+ - Scripts/Sources/Common/Array.swift
37
+ - Scripts/Sources/Common/Error.swift
38
+ - Scripts/Sources/Common/MD5.swift
39
+ - Scripts/Sources/Resources/Api+Figma.swift
40
+ - Scripts/Sources/Resources/Deploy.swift
41
+ - Scripts/Sources/Resources/Device.swift
42
+ - Scripts/Sources/Resources/DownloadBatch.swift
43
+ - Scripts/Sources/Resources/FigmaPages.swift
44
+ - Scripts/Sources/Resources/Images.swift
45
+ - Scripts/Sources/Resources/PreviewDownloader.swift
46
+ - Scripts/Sources/Resources/ResourcesParser.swift
47
+ - Scripts/Sources/Resources/ScreenshotDownloader.swift
48
+ - Scripts/Sources/Resources/main.swift
26
49
  homepage: https://github.com/teanet/ETLane
27
50
  licenses:
28
51
  - MIT