apprepo 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +26 -23
- data/lib/Repofile +40 -0
- data/lib/apprepo.rb +39 -11
- data/lib/apprepo/commands_generator.rb +108 -0
- data/lib/apprepo/detect_values.rb +38 -0
- data/lib/apprepo/download_metadata.rb +44 -0
- data/lib/apprepo/loader.rb +11 -0
- data/lib/apprepo/manifest.rb +26 -30
- data/lib/apprepo/options.rb +98 -0
- data/lib/apprepo/runner.rb +77 -0
- data/lib/apprepo/setup.rb +49 -0
- data/lib/apprepo/upload_assets.rb +22 -0
- data/lib/apprepo/upload_descriptor.rb +8 -9
- data/lib/apprepo/upload_metadata.rb +192 -0
- data/lib/apprepo/uploader.rb +80 -54
- data/lib/apprepo/version.rb +2 -2
- data/lib/test.rb +27 -26
- metadata +21 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81a7e5718e2671942e58361dc795a29e06d8a9f1
|
4
|
+
data.tar.gz: 215a9a69fdad6db0af88b8c28925942f78232214
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b71daddcecc92af2962c7ade8a340b44a1d304faea01025577b054f0577589439050a7a41e0e8078b7c6bba62ae65251e71555ed8c2ca6a197374998bc20acd
|
7
|
+
data.tar.gz: 3cc18b0e4a8e16046839d36b893d6014115845ea37e2b7e044225a4b59844f117c234fef20e59b455aa54480cc28d0bc1fdbbcf05ba101f3a80178755c41ca2d
|
data/README.md
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
</a>
|
7
7
|
</h3>
|
8
8
|
<p align="center">
|
9
|
-
<b>apprepo</b> •
|
10
9
|
<a href="https://github.com/fastlane/fastlane/tree/master/deliver">deliver</a> •
|
11
10
|
<a href="https://github.com/fastlane/fastlane/tree/master/snapshot">snapshot</a> •
|
12
11
|
<a href="https://github.com/fastlane/fastlane/tree/master/frameit">frameit</a> •
|
@@ -19,9 +18,12 @@
|
|
19
18
|
<a href="https://github.com/fastlane/boarding">boarding</a> •
|
20
19
|
<a href="https://github.com/fastlane/fastlane/tree/master/gym">gym</a> •
|
21
20
|
<a href="https://github.com/fastlane/fastlane/tree/master/scan">scan</a> •
|
22
|
-
<a href="https://github.com/fastlane/fastlane/tree/master/match">match</a>
|
21
|
+
<a href="https://github.com/fastlane/fastlane/tree/master/match">match</a> •
|
22
|
+
<b>apprepo</b>
|
23
23
|
</p>
|
24
|
-
|
24
|
+
<br/>
|
25
|
+
<hr/>
|
26
|
+
<br/>
|
25
27
|
|
26
28
|
<p align="center">
|
27
29
|
<img src="assets/apprepo.png" height="110">
|
@@ -30,14 +32,18 @@
|
|
30
32
|
apprepo
|
31
33
|
============
|
32
34
|
|
35
|
+
**This project is now in preparation phase. It is secure, therefore none of it works except for tests against test server with sample account & shared public key.**
|
36
|
+
|
33
37
|
[![Twitter: @igraczech](https://img.shields.io/badge/contact-%40igraczech-green.svg?style=flat)](https://twitter.com/igraczech)
|
34
38
|
[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/fastlane/fastlane/blob/master/deliver/LICENSE)
|
35
|
-
[![Gem](https://img.shields.io/
|
39
|
+
[![Gem](https://img.shields.io/gem/v/apprepo.svg?style=flat)](http://rubygems.org/gems/apprepo)
|
36
40
|
[![Build Status](https://img.shields.io/circleci/project/suculent/apprepo.svg?style=flat)](https://circleci.com/gh/suculent/apprepo)
|
37
41
|
|
38
|
-
######
|
42
|
+
###### Deliver IPAs, icons & metadata to AppRepo (or any other SFTP server) using a single command.
|
43
|
+
|
44
|
+
`apprepo` can upload ipa files, app icons and metadata to your Enterprise iOS distribution site from the command line.
|
39
45
|
|
40
|
-
|
46
|
+
You can vote for Android support as well, Michal.
|
41
47
|
|
42
48
|
Get in contact with the developer on Twitter: [@igraczech](https://twitter.com/igraczech)
|
43
49
|
|
@@ -53,12 +59,12 @@ Get in contact with the developer on Twitter: [@igraczech](https://twitter.com/i
|
|
53
59
|
|
54
60
|
-------
|
55
61
|
|
56
|
-
<h5 align="center"><code>
|
62
|
+
<h5 align="center"><code>apprepo</code> is now just a couple days old 3rd party extension of <a href="https://fastlane.tools">fastlane</a>: The easiest way to automate building and releasing your iOS and Android apps. I'm in contact with Felix Krause to make this part of Fastlane as open SFTP uploader adjacent to `Deliver`.</h5>
|
57
63
|
|
58
|
-
# Features
|
59
|
-
-
|
64
|
+
# Future Features
|
65
|
+
- Submit to AppRepo completely automatically
|
60
66
|
- Upload a new ipa file to AppRepo without Xcode from any Mac
|
61
|
-
- Maintain your app metadata locally and push changes
|
67
|
+
- Maintain your app metadata locally and push changes to AppRepo
|
62
68
|
- Easily implement a real Continuous Deployment process using [fastlane](https://fastlane.tools)
|
63
69
|
- Store the configuration in git to easily deploy from **any** Mac, including your Continuous Integration server
|
64
70
|
|
@@ -66,7 +72,6 @@ To upload builds to AppStore check out [deliver](https://github.com/fastlane/fas
|
|
66
72
|
|
67
73
|
To upload builds to TestFlight check out [pilot](https://github.com/fastlane/fastlane/tree/master/pilot).
|
68
74
|
|
69
|
-
##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx)
|
70
75
|
|
71
76
|
# Installation
|
72
77
|
|
@@ -80,31 +85,29 @@ Make sure, you have the latest version of the Xcode command line tools installed
|
|
80
85
|
|
81
86
|
# Quick Start
|
82
87
|
|
83
|
-
The guide will create all the necessary files for you, using the existing app metadata from
|
88
|
+
The guide will create all the necessary files for you, [in future also] using the existing app metadata.json from AppRepo (if any). AppRepo on-premise service has nothing to do with iTunesConnect or AppStore Distribution. Its purpose is solely for Enterprise In-House distribution and can leverage your Apple Developer Account credentials to fetch currently valid applications and their bundle identifiers.
|
89
|
+
|
90
|
+
Delivery module for your custom SFTP server (e.g. AppRepo, where it specifically supports custom metadata.json format instead of default plist manifest [future todo]).
|
84
91
|
|
85
92
|
- ```cd [your_project_folder]```
|
86
93
|
- ```apprepo init```
|
87
|
-
- Enter your AppRepo credentials (path to RSA private key)
|
88
|
-
- Enter your
|
94
|
+
- Enter your AppRepo credentials (absolute path to RSA private key for your SFTP server)
|
95
|
+
- Enter your `APPREPO` APPCODE for this application in AppRepo, you can omit this for own SFTP server (or expect it to be a directory at your SFTP home path as we don't want to limit your creativity).
|
89
96
|
- Enjoy a good drink, while the computer does all the work for you
|
90
97
|
|
91
98
|
From now on, you can run `apprepo` to deploy a new update, or just upload new app metadata and icons.
|
92
99
|
|
93
|
-
Already using Jenkins and just updated to `apprepo`? Check out the [Migration Guide](https://github.com/fastlane/fastlane/blob/master/deliver/MigrationGuide.md).
|
94
100
|
|
95
101
|
# Usage
|
96
102
|
|
97
|
-
...
|
103
|
+
... to be done ...
|
98
104
|
|
99
105
|
# Credentials
|
100
106
|
|
101
|
-
...
|
102
|
-
|
103
|
-
### How does this thing even work? Is magic involved? 🎩###
|
107
|
+
... to be done ...
|
104
108
|
|
105
|
-
...
|
106
109
|
|
107
|
-
# Tips
|
110
|
+
# Fastlane Tips
|
108
111
|
|
109
112
|
## [`fastlane`](https://fastlane.tools) Toolchain
|
110
113
|
|
@@ -125,14 +128,14 @@ Already using Jenkins and just updated to `apprepo`? Check out the [Migration Gu
|
|
125
128
|
##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx)
|
126
129
|
|
127
130
|
|
128
|
-
## Editing the ```
|
131
|
+
## Editing the ```Repofile```
|
129
132
|
Change syntax highlighting to *Ruby*.
|
130
133
|
|
131
134
|
# Need help?
|
132
135
|
Please submit an issue on GitHub and provide information about your setup
|
133
136
|
|
134
137
|
# Code of Conduct
|
135
|
-
Help us keep `apprepo` open and inclusive. Please read and follow our [Code of Conduct](https://github.com/
|
138
|
+
Help us keep `apprepo` open and inclusive. Please read and follow our [Code of Conduct](https://github.com/suculent/apprepo/blob/master/CODE_OF_CONDUCT.md).
|
136
139
|
|
137
140
|
# License
|
138
141
|
This project is licensed under the terms of the MIT license. See the LICENSE file.
|
data/lib/Repofile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# THIS FILE IS USED FOR TESTING ONLY!
|
4
|
+
|
5
|
+
###################### More Options ######################
|
6
|
+
# If you want to have even more control, check out the documentation
|
7
|
+
# https://github.com/suculent/apprepo/blob/master/Repofile.md
|
8
|
+
|
9
|
+
|
10
|
+
###################### Automatically generated ######################
|
11
|
+
# Feel free to remove the following line if you use fastlane (which you should)
|
12
|
+
|
13
|
+
# THIS FILE IS USED FOR TESTING ONLY!
|
14
|
+
app_identifier "com.apprepo.test"
|
15
|
+
ipa File.dirname(__FILE__)+"/../sampleapp.ipa"
|
16
|
+
app_version "1.0"
|
17
|
+
meta_title "TestApp"
|
18
|
+
repo_description "Some description is required for AppRepo users to know, that is this app."
|
19
|
+
|
20
|
+
#
|
21
|
+
# Authentication
|
22
|
+
#
|
23
|
+
|
24
|
+
# You can use your '~/.ssh/config' file to setup RSA keys to AppRepo, or use following options:
|
25
|
+
|
26
|
+
repo_url "repo.teacloud.net"
|
27
|
+
repo_user "circle"
|
28
|
+
repo_key "./lib/assets/circle.pub"
|
29
|
+
|
30
|
+
# app_identifier "" # your app's bundle identifier
|
31
|
+
|
32
|
+
# notify true # Add this to automatically notify the AppRepo users after uploading metadata/binary.
|
33
|
+
|
34
|
+
# manifest_path "./manifest.json" # Path to the manifest.json you want to use.
|
35
|
+
|
36
|
+
# meta_subtitle "Some application"
|
37
|
+
|
38
|
+
# meta_description "Detailed application description."
|
39
|
+
|
40
|
+
# app_icon './AppIcon.png'
|
data/lib/apprepo.rb
CHANGED
@@ -1,25 +1,53 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# encoding: utf-8
|
4
|
+
|
3
5
|
require 'json'
|
6
|
+
|
4
7
|
require_relative 'apprepo/version'
|
8
|
+
require_relative 'apprepo/options'
|
9
|
+
|
5
10
|
require_relative 'apprepo/uploader'
|
6
|
-
require_relative 'apprepo/
|
11
|
+
require_relative 'apprepo/upload_metadata'
|
12
|
+
require_relative 'apprepo/upload_assets'
|
13
|
+
require_relative 'apprepo/commands_generator'
|
14
|
+
|
15
|
+
require_relative 'apprepo/detect_values'
|
16
|
+
require_relative 'apprepo/runner'
|
17
|
+
require_relative 'apprepo/setup'
|
18
|
+
require_relative 'apprepo/loader'
|
19
|
+
|
20
|
+
require_relative 'apprepo/upload_descriptor' # will deprecate or replace :app info
|
21
|
+
|
7
22
|
require 'fastlane_core'
|
8
23
|
|
9
24
|
module AppRepo
|
10
|
-
|
11
25
|
class << self
|
12
|
-
|
13
|
-
|
26
|
+
def initialize
|
27
|
+
UI.message('[AppRepo] Initializing...')
|
14
28
|
end
|
15
29
|
end
|
16
|
-
|
30
|
+
|
31
|
+
Encoding.default_external = Encoding::UTF_8
|
32
|
+
Encoding.default_internal = Encoding::UTF_8
|
33
|
+
|
34
|
+
# Generate options (simulates commands_generator.rb)
|
17
35
|
Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
|
18
36
|
UI = FastlaneCore::UI
|
19
37
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
38
|
+
appcode = 'ruby-test'
|
39
|
+
|
40
|
+
# Setup descriptor (appcode, ipa, metadata - from repofile)!
|
41
|
+
UI.message('[AppRepoTest] UploadDescriptor.new')
|
42
|
+
uploadDescriptor = UploadDescriptor.new(appcode) # not used yet
|
43
|
+
uploadDescriptor.appcode = appcode
|
44
|
+
|
45
|
+
# Test Uploader (OK)
|
46
|
+
#UI.message('[AppRepoTest] Uploader.new')
|
47
|
+
#upload = Uploader.new('repo.teacloud.net', 'circle', File.dirname(__FILE__) + '/../assets/circle.key', appcode)
|
48
|
+
#upload.run
|
49
|
+
|
50
|
+
UI.message('[AppRepoTest] AppRepo::CommandsGenerator.new.run')
|
51
|
+
CommandsGenerator.new.run
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'commander'
|
2
|
+
|
3
|
+
HighLine.track_eof = false
|
4
|
+
|
5
|
+
module AppRepo
|
6
|
+
class CommandsGenerator
|
7
|
+
include Commander::Methods
|
8
|
+
|
9
|
+
def self.start
|
10
|
+
FastlaneCore::UpdateChecker.start_looking_for_update('apprepo')
|
11
|
+
new.run
|
12
|
+
ensure
|
13
|
+
FastlaneCore::UpdateChecker.show_update_status('apprepo', AppRepo::VERSION)
|
14
|
+
end
|
15
|
+
|
16
|
+
# rubocop:disable Metrics/AbcSize
|
17
|
+
# rubocop:disable Metrics/MethodLength
|
18
|
+
def run
|
19
|
+
program :version, AppRepo::VERSION
|
20
|
+
program :description, AppRepo::DESCRIPTION
|
21
|
+
program :help, 'Author', 'Matej Sychra <suculent@me.com>'
|
22
|
+
program :help, 'Website', 'https://github.com/suculent/apprepo'
|
23
|
+
program :help, 'GitHub', 'https://github.com/suculent/apprepo/tree/master/apprepo'
|
24
|
+
program :help_formatter, :compact
|
25
|
+
|
26
|
+
FastlaneCore::CommanderGenerator.new.generate(AppRepo::Options.available_options)
|
27
|
+
|
28
|
+
global_option('--verbose') { $verbose = true }
|
29
|
+
|
30
|
+
always_trace!
|
31
|
+
|
32
|
+
command :run do |c|
|
33
|
+
c.syntax = 'apprepo'
|
34
|
+
c.description = 'Upload IPA and metadata to SFTP (e.g. AppRepo)'
|
35
|
+
c.action do |_args, options|
|
36
|
+
options = FastlaneCore::Configuration.create(AppRepo::Options.available_options, options.__hash__)
|
37
|
+
loaded = options.load_configuration_file('Repofile')
|
38
|
+
loaded = true if options[:repo_description] || options[:ipa]
|
39
|
+
unless loaded
|
40
|
+
if UI.confirm('No AppRepo configuration found in the current directory. Do you want to setup apprepo?')
|
41
|
+
require 'apprepo/setup'
|
42
|
+
# AppRepo::Runner.new(options) # to login...
|
43
|
+
AppRepo::Setup.new.run(options)
|
44
|
+
return 0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
AppRepo::Runner.new(options).run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
command :submit_build do |c|
|
52
|
+
c.syntax = 'apprepo submit_build'
|
53
|
+
c.description = 'Submit a specific build-nr for review, use latest for the latest build'
|
54
|
+
c.action do |_args, options|
|
55
|
+
options = FastlaneCore::Configuration.create(AppRepo::Options.available_options, options.__hash__)
|
56
|
+
options.load_configuration_file('Repofile')
|
57
|
+
options[:submit_for_review] = true
|
58
|
+
options[:build_number] = 'latest' unless options[:build_number]
|
59
|
+
AppRepo::Runner.new(options).run
|
60
|
+
end
|
61
|
+
end
|
62
|
+
command :init do |c|
|
63
|
+
c.syntax = 'apprepo init'
|
64
|
+
c.description = 'Create the initial `apprepo` configuration based on an existing app'
|
65
|
+
c.action do |_args, options|
|
66
|
+
if File.exist?('Repofile') || File.exist?('fastlane/Repofile')
|
67
|
+
UI.important('You already got a running apprepo setup in this directory')
|
68
|
+
return 0
|
69
|
+
end
|
70
|
+
|
71
|
+
require 'apprepo/setup'
|
72
|
+
options = FastlaneCore::Configuration.create(AppRepo::Options.available_options, options.__hash__)
|
73
|
+
AppRepo::Runner.new(options) # to login...
|
74
|
+
AppRepo::Setup.new.run(options)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
command :download_metadata do |c|
|
79
|
+
c.syntax = 'apprepo download_metadata'
|
80
|
+
c.description = 'Downloads existing metadata and stores it locally. This overwrites the local files.'
|
81
|
+
|
82
|
+
c.action do |_args, options|
|
83
|
+
options = FastlaneCore::Configuration.create(AppRepo::Options.available_options, options.__hash__)
|
84
|
+
options.load_configuration_file('Repofile')
|
85
|
+
AppRepo::Runner.new(options) # to login...
|
86
|
+
containing = FastlaneCore::Helper.fastlane_enabled? ? './fastlane' : '.'
|
87
|
+
path = options[:metadata_path] || File.join(containing, 'metadata')
|
88
|
+
res = ENV['APPREPO_FORCE_OVERWRITE']
|
89
|
+
res ||= UI.confirm("Do you want to overwrite existing metadata on path '#{File.expand_path(path)}'?")
|
90
|
+
if res
|
91
|
+
require 'apprepo/setup'
|
92
|
+
# TODO: Fetch version from IPA or else
|
93
|
+
v = options[:app_version].latest_version
|
94
|
+
AppRepo::Setup.new.generate_metadata_files(v, path)
|
95
|
+
else
|
96
|
+
return 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
# rubocop:enable Metrics/AbcSize
|
101
|
+
# rubocop:enable Metrics/MethodLength
|
102
|
+
|
103
|
+
default_command :run
|
104
|
+
|
105
|
+
run!
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module AppRepo
|
2
|
+
class DetectValues
|
3
|
+
def run!(options)
|
4
|
+
#find_app_identifier(options
|
5
|
+
find_folders(options)
|
6
|
+
find_version(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_app_identifier(options)
|
10
|
+
return if options[:app_identifier]
|
11
|
+
|
12
|
+
if options[:ipa]
|
13
|
+
identifier = FastlaneCore::IpaFileAnalyser.fetch_app_identifier(options[:ipa])
|
14
|
+
elsif options[:pkg]
|
15
|
+
identifier = FastlaneCore::PkgFileAnalyser.fetch_app_identifier(options[:pkg])
|
16
|
+
end
|
17
|
+
|
18
|
+
options[:app_identifier] = identifier if identifier.to_s.length > 0
|
19
|
+
options[:app_identifier] ||= UI.input('The Bundle Identifier of your App: ')
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_folders(options)
|
23
|
+
containing = Helper.fastlane_enabled? ? './fastlane' : '.'
|
24
|
+
if options[:metadata_path] != nil
|
25
|
+
puts "Containing: '"+options[:metadata_path]+"' folder for TODO RENAME example_manifest.json"
|
26
|
+
options[:metadata_path] ||= File.join(containing, '/../manifest.json')
|
27
|
+
puts "Options: '"+options[:metadata_path]+"'"
|
28
|
+
FileUtils.mkdir_p(options[:metadata_path])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_version(options)
|
33
|
+
if options[:ipa] != nil
|
34
|
+
options[:app_version] ||= FastlaneCore::IpaFileAnalyser.fetch_app_version(options[:ipa])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module AppRepo
|
2
|
+
class DownloadMetadata
|
3
|
+
def self.run(options, path)
|
4
|
+
UI.message('Downloading existing metadata.json...')
|
5
|
+
download(options, path)
|
6
|
+
UI.success('Successfully downloaded all metadata')
|
7
|
+
rescue => ex
|
8
|
+
UI.error(ex)
|
9
|
+
UI.error("Couldn't download already existing metadata from AppRepo.")
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.download(_options, _folder_path)
|
13
|
+
UI.message('TODO: DOWNLOAD existing metadata.json NOT IMPLEMENTED')
|
14
|
+
|
15
|
+
# v = options[:app].latest_version
|
16
|
+
|
17
|
+
# v.screenshots.each do |language, screenshots|
|
18
|
+
# screenshots.each do |screenshot|
|
19
|
+
# file_name = [screenshot.sort_order, screenshot.device_type, screenshot.sort_order].join("_")
|
20
|
+
# original_file_extension = File.basename(screenshot.original_file_name)
|
21
|
+
# file_name += "." + original_file_extension
|
22
|
+
|
23
|
+
# UI.message("Downloading existing screenshot '#{file_name}'")
|
24
|
+
|
25
|
+
# # If the screen shot is for an appleTV we need to store it in a way that we'll know it's an appleTV
|
26
|
+
# # screen shot later as the screen size is the same as an iPhone 6 Plus in landscape.
|
27
|
+
# if screenshot.device_type == "appleTV"
|
28
|
+
# containing_folder = File.join(folder_path, "appleTV", screenshot.language)
|
29
|
+
# else
|
30
|
+
# containing_folder = File.join(folder_path, screenshot.language)
|
31
|
+
# end
|
32
|
+
|
33
|
+
# begin
|
34
|
+
# FileUtils.mkdir_p(containing_folder)
|
35
|
+
# rescue
|
36
|
+
# # if it's already there
|
37
|
+
# end
|
38
|
+
# path = File.join(containing_folder, file_name)
|
39
|
+
# File.write(path, open(screenshot.url).read)
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'fastlane_core/languages'
|
2
|
+
|
3
|
+
module Deliver
|
4
|
+
module Loader
|
5
|
+
def self.language_folders(root)
|
6
|
+
Dir.glob(File.join(root, '*')).select do |path|
|
7
|
+
File.directory?(path) && ALL_LANGUAGES.include?(File.basename(path).downcase)
|
8
|
+
end.sort
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|