ETLane 0.1.42 → 0.1.46
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Lanes/CommonFastfile +4 -8
- data/Lanes/actions/README.md +47 -0
- data/Lanes/actions/add_keys_to_lokalise.rb +106 -0
- data/Lanes/actions/android/README.md +53 -0
- data/Lanes/actions/android/lokalise_download.rb +144 -0
- data/Lanes/actions/android/lokalise_upload.rb +98 -0
- data/Lanes/actions/lokalise.rb +180 -0
- data/Lanes/actions/lokalise_metadata.rb +624 -0
- data/Lanes/actions/lokalise_upload.rb +165 -0
- data/Lanes/actions/previews.rb +157 -0
- data/Scripts/Sources/Common/Api.swift +142 -0
- data/Scripts/Sources/Common/Array.swift +8 -0
- data/Scripts/Sources/Common/Error.swift +10 -0
- data/Scripts/Sources/Common/MD5.swift +34 -0
- data/Scripts/Sources/Resources/Api+Figma.swift +43 -0
- data/Scripts/Sources/Resources/Deploy.swift +133 -0
- data/Scripts/Sources/Resources/Device.swift +42 -0
- data/Scripts/Sources/Resources/DownloadBatch.swift +108 -0
- data/Scripts/Sources/Resources/FigmaPages.swift +58 -0
- data/Scripts/Sources/Resources/Images.swift +5 -0
- data/Scripts/Sources/Resources/PreviewDownloader.swift +80 -0
- data/Scripts/Sources/Resources/ResourcesParser.swift +25 -0
- data/Scripts/Sources/Resources/ScreenshotDownloader.swift +150 -0
- data/Scripts/Sources/Resources/main.swift +58 -0
- metadata +25 -2
@@ -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.
|
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-
|
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
|