ETLane 0.1.42 → 0.1.46

Sign up to get free protection for your applications and to get access to all the features.
@@ -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