nabokov 0.1.0
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 +7 -0
- data/.gitattributes +1 -0
- data/.gitignore +43 -0
- data/.rspec +2 -0
- data/.rubocop.yml +113 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -0
- data/LICENSE +21 -0
- data/README.md +16 -0
- data/Rakefile +22 -0
- data/bin/nabokov +5 -0
- data/lib/nabokov/commands/runner.rb +22 -0
- data/lib/nabokov/commands/setup.rb +75 -0
- data/lib/nabokov/commands/syncers/localizations_repo_syncer.rb +101 -0
- data/lib/nabokov/commands/syncers/project_syncer.rb +106 -0
- data/lib/nabokov/commands/syncers/syncer.rb +68 -0
- data/lib/nabokov/core/file_manager.rb +36 -0
- data/lib/nabokov/core/nabokovfile.rb +66 -0
- data/lib/nabokov/core/nabokovfile_content_validator.rb +51 -0
- data/lib/nabokov/core/nabokovfile_keys.rb +34 -0
- data/lib/nabokov/git/git_repo.rb +137 -0
- data/lib/nabokov/helpers/informator.rb +83 -0
- data/lib/nabokov/helpers/merger.rb +86 -0
- data/lib/nabokov/models/strings_file.rb +7 -0
- data/lib/nabokov/version.rb +8 -0
- data/lib/nabokov.rb +14 -0
- data/nabokov.gemspec +31 -0
- data/spec/fixtures/.DS_Store +0 -0
- data/spec/fixtures/README.md +1 -0
- data/spec/fixtures/de.strings +1 -0
- data/spec/fixtures/en.strings +1 -0
- data/spec/fixtures/nabokovfile_example.yaml +9 -0
- data/spec/fixtures/nabokovfile_example_invalid.yaml +2 -0
- data/spec/fixtures/nabokovfile_example_without_master_branch.yaml +8 -0
- data/spec/fixtures/test_git_setup/existed_pre_commit_file +0 -0
- data/spec/fixtures/test_git_setup/existed_pre_commit_file_alias +0 -0
- data/spec/fixtures/test_git_setup/not_executable_pre_commit_file +0 -0
- data/spec/fixtures/test_localizations_repo_syncer/localizations_repo_fixtures/README.md +1 -0
- data/spec/fixtures/test_localizations_repo_syncer/nabokovfile.yaml +8 -0
- data/spec/fixtures/test_project_syncer/localizations_repo_fixtures/de.strings +2 -0
- data/spec/fixtures/test_project_syncer/localizations_repo_fixtures/en.strings +2 -0
- data/spec/fixtures/test_project_syncer/project_repo_fixtures/de.strings +2 -0
- data/spec/fixtures/test_project_syncer/project_repo_fixtures/en.strings +2 -0
- data/spec/fixtures/test_project_syncer/project_repo_fixtures/nabokovfile.yaml +9 -0
- data/spec/lib/nabokov/commands/localizations_repo_syncer_spec.rb +137 -0
- data/spec/lib/nabokov/commands/project_syncer_spec.rb +61 -0
- data/spec/lib/nabokov/commands/runner_spec.rb +7 -0
- data/spec/lib/nabokov/commands/setup_spec.rb +101 -0
- data/spec/lib/nabokov/commands/syncer_spec.rb +23 -0
- data/spec/lib/nabokov/core/file_manager_spec.rb +115 -0
- data/spec/lib/nabokov/core/nabokovfile_content_validator_spec.rb +155 -0
- data/spec/lib/nabokov/core/nabokovfile_keyes_spec.rb +31 -0
- data/spec/lib/nabokov/core/nabokovfile_spec.rb +53 -0
- data/spec/lib/nabokov/git/git_repo_spec.rb +670 -0
- data/spec/lib/nabokov/helpers/informator_spec.rb +49 -0
- data/spec/lib/nabokov/helpers/merger_spec.rb +114 -0
- data/spec/lib/nabokov/models/strings_file_spec.rb +7 -0
- data/spec/spec_helper.rb +11 -0
- metadata +238 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dd71abc02d1c0469d5514b59900199790b2e9b8b
|
4
|
+
data.tar.gz: 355a0707bbe1ea7196595098da5450a948868be1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dc9ce5a4b6104c0b69074553d3a72ef7f49ae69ab83dd00a7051d44cd54ef69c0642abe87d5ad9d3e4c987fb1541be7a1cb181b1ad7712313ef411d2d7fbdd1e
|
7
|
+
data.tar.gz: 828af0acfaa649d4713954b55d711a3c4a7fc32553ed47e79b87f5249a0b1e8ed1ee0b901243631db111d4d1b0c8851d039c83aaef7fa2a86c00ea2d7f4fec89
|
data/.gitattributes
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
CHANGELOG.md merge=union
|
data/.gitignore
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
.DS_Store
|
2
|
+
|
3
|
+
/.bundle/
|
4
|
+
/.yardoc
|
5
|
+
/Gemfile.lock
|
6
|
+
/_yardoc/
|
7
|
+
/coverage/
|
8
|
+
/doc/
|
9
|
+
/pkg/
|
10
|
+
/spec/reports/
|
11
|
+
/tmp/
|
12
|
+
/test/tmp/
|
13
|
+
/test/version_tmp/
|
14
|
+
/tmp/
|
15
|
+
|
16
|
+
## Specific to RubyMotion:
|
17
|
+
.dat*
|
18
|
+
.repl_history
|
19
|
+
build/
|
20
|
+
|
21
|
+
## Documentation cache and generated files:
|
22
|
+
/.yardoc/
|
23
|
+
/_yardoc/
|
24
|
+
/doc/
|
25
|
+
/rdoc/
|
26
|
+
|
27
|
+
## Environment normalisation:
|
28
|
+
/.bundle/
|
29
|
+
/vendor/bundle
|
30
|
+
/lib/bundler/man/
|
31
|
+
/vendor/
|
32
|
+
|
33
|
+
# for a library or gem, you might want to ignore these files since the code is
|
34
|
+
# intended to run in multiple environments; otherwise, check them in:
|
35
|
+
# Gemfile.lock
|
36
|
+
# .ruby-version
|
37
|
+
# .ruby-gemset
|
38
|
+
|
39
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
40
|
+
.rvmrc
|
41
|
+
fastlane/report.xml
|
42
|
+
.keys
|
43
|
+
.idea
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'spec/fixtures/**/*'
|
4
|
+
|
5
|
+
Style/StringLiterals:
|
6
|
+
EnforcedStyle: double_quotes
|
7
|
+
Enabled: true
|
8
|
+
|
9
|
+
# kind_of? is a good way to check a type
|
10
|
+
Style/ClassCheck:
|
11
|
+
EnforcedStyle: kind_of?
|
12
|
+
|
13
|
+
# It's better to be more explicit about the type
|
14
|
+
Style/BracesAroundHashParameters:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
# specs sometimes have useless assignments, which is fine
|
18
|
+
Lint/UselessAssignment:
|
19
|
+
Exclude:
|
20
|
+
- '**/spec/**/*'
|
21
|
+
|
22
|
+
# We could potentially enable the 2 below:
|
23
|
+
Style/IndentHash:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/AlignHash:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
# HoundCI doesn't like this rule
|
30
|
+
Style/DotPosition:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
# We allow !! as it's an easy way to convert ot boolean
|
34
|
+
Style/DoubleNegation:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
# Cop supports --auto-correct.
|
38
|
+
Lint/UnusedBlockArgument:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
# We want to allow class Fastlane::Class
|
42
|
+
Style/ClassAndModuleChildren:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
Metrics/AbcSize:
|
46
|
+
Max: 60
|
47
|
+
|
48
|
+
# The %w might be confusing for new users
|
49
|
+
Style/WordArray:
|
50
|
+
MinSize: 19
|
51
|
+
|
52
|
+
# raise and fail are both okay
|
53
|
+
Style/SignalException:
|
54
|
+
Enabled: false
|
55
|
+
|
56
|
+
# Better too much 'return' than one missing
|
57
|
+
Style/RedundantReturn:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
# Having if in the same line might not always be good
|
61
|
+
Style/IfUnlessModifier:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
# and and or is okay
|
65
|
+
Style/AndOr:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
# Configuration parameters: CountComments.
|
69
|
+
Metrics/ClassLength:
|
70
|
+
Max: 320
|
71
|
+
|
72
|
+
Metrics/CyclomaticComplexity:
|
73
|
+
Max: 17
|
74
|
+
|
75
|
+
# Configuration parameters: AllowURI, URISchemes.
|
76
|
+
Metrics/LineLength:
|
77
|
+
Max: 370
|
78
|
+
|
79
|
+
# Configuration parameters: CountKeywordArgs.
|
80
|
+
Metrics/ParameterLists:
|
81
|
+
Max: 10
|
82
|
+
|
83
|
+
Metrics/PerceivedComplexity:
|
84
|
+
Max: 18
|
85
|
+
|
86
|
+
# Sometimes it's easier to read without guards
|
87
|
+
Style/GuardClause:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
# something = if something_else
|
91
|
+
# that's confusing
|
92
|
+
Style/ConditionalAssignment:
|
93
|
+
Enabled: false
|
94
|
+
|
95
|
+
# Better to have too much self than missing a self
|
96
|
+
Style/RedundantSelf:
|
97
|
+
Enabled: false
|
98
|
+
|
99
|
+
Metrics/MethodLength:
|
100
|
+
Max: 60
|
101
|
+
|
102
|
+
# We're not there yet
|
103
|
+
Style/Documentation:
|
104
|
+
Enabled: false
|
105
|
+
|
106
|
+
# Adds complexity
|
107
|
+
Style/IfInsideElse:
|
108
|
+
Enabled: false
|
109
|
+
|
110
|
+
# danger specific
|
111
|
+
|
112
|
+
Style/BlockComments:
|
113
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Anton Domashnev
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Nabokov
|
2
|
+
Move mobile localization process up to the next level 🚀
|
3
|
+
|
4
|
+
|
5
|
+
[](https://github.com/Antondomashnev/nabokov/blob/master/LICENSE)
|
6
|
+
|
7
|
+
## Current status
|
8
|
+
This project is on the early development stage and is *not ready for production*. The `master` branch is empty and now the current status can be checked in `develop` branch. Also readme is not ready at all and we'll be updated as soon as the first functional becomes to the `master` branch.
|
9
|
+
|
10
|
+
## Behind the scene
|
11
|
+
This is my first ruby project except small experiments on the local machine before. So please bare it in mind and I would really appreciate any point about code style or even better way to implement something
|
12
|
+
|
13
|
+
## License
|
14
|
+
This project is open source under the MIT license, which means you have full access to the source code and can modify it to fit your own needs.
|
15
|
+
|
16
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rubocop/rake_task"
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
RSpec::Core::RakeTask.new(:specs)
|
7
|
+
rescue LoadError
|
8
|
+
puts "Please use `bundle exec` to get all the rake commands"
|
9
|
+
end
|
10
|
+
|
11
|
+
task default: :spec
|
12
|
+
|
13
|
+
desc "Nabokov's tests"
|
14
|
+
task :spec do
|
15
|
+
Rake::Task["specs"].invoke
|
16
|
+
Rake::Task["rubocop"].invoke
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Run RuboCop on the lib/specs directory"
|
20
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
21
|
+
task.patterns = Dir.glob(["lib/**/*.rb", "spec/**/*.rb"]) - Dir.glob(["spec/fixtures/**/*"])
|
22
|
+
end
|
data/bin/nabokov
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "nabokov/version"
|
2
|
+
require "nabokov/helpers/informator"
|
3
|
+
require "claide"
|
4
|
+
require "cork"
|
5
|
+
|
6
|
+
module Nabokov
|
7
|
+
class Runner < CLAide::Command
|
8
|
+
self.abstract_command = true
|
9
|
+
self.summary = "Nabokov, the simple tool to keep your mobile app localization up to date."
|
10
|
+
self.version = Nabokov::VERSION
|
11
|
+
self.command = "nabokov"
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
super
|
15
|
+
@cork = Cork::Board.new(silent: argv.option("silent", false), verbose: argv.option("verbose", false))
|
16
|
+
end
|
17
|
+
|
18
|
+
def ui
|
19
|
+
@ui ||= Informator.new(@cork)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "nabokov/commands/runner"
|
2
|
+
require "fileutils"
|
3
|
+
require "nabokov/core/nabokovfile"
|
4
|
+
require "nabokov/core/file_manager"
|
5
|
+
require "nabokov/git/git_repo"
|
6
|
+
|
7
|
+
module Nabokov
|
8
|
+
# Command to setup the project repo to use nabokov
|
9
|
+
# It setups the pre commit hook to start Nabokov::LocalizationsRepoSyncer
|
10
|
+
class Setup < Runner
|
11
|
+
self.summary = "Setups the repository hook to sync app localizations."
|
12
|
+
self.command = "setup"
|
13
|
+
self.description = "Installs the pre-commit git hook with the logic to run 'nabokov sync localizations'"
|
14
|
+
|
15
|
+
attr_reader :pre_commit_file
|
16
|
+
|
17
|
+
def initialize(argv)
|
18
|
+
@pre_commit_file = argv.option("pre_commit_file")
|
19
|
+
@pre_commit_file ||= default_pre_commit_file
|
20
|
+
@git_path = argv.option("git_path")
|
21
|
+
@git_path ||= default_git_path
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
ensure_pre_commit_file_exists
|
27
|
+
ensure_pre_commit_file_is_executable
|
28
|
+
ensure_hook_is_installed
|
29
|
+
ui.important "nabokov pre commit git hook is installed"
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def ensure_pre_commit_file_exists
|
36
|
+
@pre_commit_file = File.realpath(@pre_commit_file) if File.symlink?(@pre_commit_file)
|
37
|
+
return if File.exist?(@pre_commit_file)
|
38
|
+
|
39
|
+
raise ".git folder is not found at '#{@git_path}'" unless Dir.exist?(@git_path)
|
40
|
+
|
41
|
+
FileUtils.mkdir_p("#{@git_path}/hooks")
|
42
|
+
@pre_commit_file = "#{@git_path}/hooks/pre-commit"
|
43
|
+
FileUtils.touch(@pre_commit_file)
|
44
|
+
FileUtils.chmod("u=xwr", @pre_commit_file)
|
45
|
+
end
|
46
|
+
|
47
|
+
def ensure_pre_commit_file_is_executable
|
48
|
+
raise "pre commit file at '#{@pre_commit_file}' is not executable by the effective user id of this process" unless File.executable?(@pre_commit_file)
|
49
|
+
end
|
50
|
+
|
51
|
+
def ensure_hook_is_installed
|
52
|
+
git_repo_path = ""
|
53
|
+
IO.popen("git rev-parse --show-toplevel", "r+") do |pipe|
|
54
|
+
git_repo_path = pipe.read
|
55
|
+
end
|
56
|
+
return if File.foreach(@pre_commit_file).grep(/git_repo_path/).any?
|
57
|
+
|
58
|
+
File.open(@pre_commit_file, "r+") do |f|
|
59
|
+
f.puts("#!/usr/bin/env bash")
|
60
|
+
f.puts("current_repo_path=\$(git rev-parse --show-toplevel)")
|
61
|
+
f.puts("nabokovfile_path=\"$current_repo_path/Nabokovfile.yaml\"")
|
62
|
+
f.puts("tracking_repo_path=\"#{git_repo_path.strip}\"")
|
63
|
+
f.puts("if [ \"$current_repo_path\" == \"$tracking_repo_path\" ] && gem list -i nabokov && [ -e \"$nabokovfile_path\" ]; then nabokov sync localizations --nabokovfile=$nabokovfile_path || exit 1; fi")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_git_path
|
68
|
+
".git"
|
69
|
+
end
|
70
|
+
|
71
|
+
def default_pre_commit_file
|
72
|
+
"#{default_git_path}/hooks/pre-commit"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "nabokov/commands/syncers/syncer"
|
2
|
+
require "nabokov/core/file_manager"
|
3
|
+
require "nabokov/helpers/merger"
|
4
|
+
|
5
|
+
module Nabokov
|
6
|
+
class LocalizationsRepoSyncer < Syncer
|
7
|
+
self.abstract_command = false
|
8
|
+
self.command = "localizations"
|
9
|
+
self.summary = "Synchronize remote localizations repo with the project localization strings."
|
10
|
+
|
11
|
+
def initialize(argv)
|
12
|
+
super
|
13
|
+
@synchronized_file_names = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.options
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
ui.important("Nabokov starts localizations repo synchronization")
|
26
|
+
super
|
27
|
+
checkout_temporary_branch
|
28
|
+
has_changes = update_localization_files
|
29
|
+
checkout_master_branch
|
30
|
+
fetch_master_branch_changes
|
31
|
+
if has_changes && merge_master_branch_with_temporary(@rescue_commit_sha) == Nabokov::MergerResult::SUCCEEDED
|
32
|
+
push_changes_to_remote
|
33
|
+
end
|
34
|
+
delete_temporary_branch
|
35
|
+
notify_user_about_finish
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def notify_user_about_finish
|
41
|
+
if @synchronized_file_names.count > 0
|
42
|
+
ui.say("#{@synchronized_file_names} have been updated on remote, your are all set for now 🎉")
|
43
|
+
else
|
44
|
+
ui.say("Nothing to synchronize, localizations in the remote repo and project repo are the same.")
|
45
|
+
end
|
46
|
+
ui.important("Nabokov has finished localizations remote repo synchronization")
|
47
|
+
end
|
48
|
+
|
49
|
+
def checkout_temporary_branch
|
50
|
+
ui.say("Checkout a temporary branch for new localization strings...") if self.verbose
|
51
|
+
self.git_repo.checkout_branch(temporary_branch)
|
52
|
+
end
|
53
|
+
|
54
|
+
def update_localization_files
|
55
|
+
has_changes = false
|
56
|
+
self.nabokovfile.project_localization_file_paths.each do |localization_file_name, localization_file_path|
|
57
|
+
ui.say("Copying strings file from '#{localization_file_path}' to the localization repo...") if self.verbose
|
58
|
+
new_file_path = FileManager.copy_and_rename(localization_file_path, self.git_repo.local_path, localization_file_name.to_s)
|
59
|
+
self.git_repo.add(new_file_path)
|
60
|
+
if self.git_repo.changes?
|
61
|
+
@synchronized_file_names << localization_file_name
|
62
|
+
self.git_repo.commit("Nabokov localization file '#{localization_file_name}' update...")
|
63
|
+
has_changes = true
|
64
|
+
else
|
65
|
+
ui.say("'#{localization_file_name}' file doesn't have any changes to commit...") if self.verbose
|
66
|
+
end
|
67
|
+
end
|
68
|
+
has_changes
|
69
|
+
end
|
70
|
+
|
71
|
+
def checkout_master_branch
|
72
|
+
ui.say("Checkout master branch...") if self.verbose
|
73
|
+
self.git_repo.checkout_branch(self.nabokovfile.localizations_repo_master_branch)
|
74
|
+
@rescue_commit_sha = self.git_repo.log(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
def fetch_master_branch_changes
|
78
|
+
ui.say("Fetching remote master branch changes...") if self.verbose
|
79
|
+
self.git_repo.pull
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete_temporary_branch
|
83
|
+
ui.say("Deleting temporary branch...") if self.verbose
|
84
|
+
self.git_repo.delete_branch(temporary_branch)
|
85
|
+
end
|
86
|
+
|
87
|
+
def push_changes_to_remote
|
88
|
+
ui.say("Pushing changes to remote...") if self.verbose
|
89
|
+
self.git_repo.push
|
90
|
+
end
|
91
|
+
|
92
|
+
def merge_master_branch_with_temporary(rescue_commit_sha = nil)
|
93
|
+
merger = Merger.new(ui, self.git_repo, rescue_commit_sha)
|
94
|
+
merger.merge(self.nabokovfile.localizations_repo_master_branch, temporary_branch)
|
95
|
+
end
|
96
|
+
|
97
|
+
def temporary_branch
|
98
|
+
"nabokov/temporary_branch"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "nabokov/commands/syncers/syncer"
|
2
|
+
require "nabokov/core/file_manager"
|
3
|
+
require "nabokov/helpers/merger"
|
4
|
+
require "nabokov/models/strings_file"
|
5
|
+
|
6
|
+
module Nabokov
|
7
|
+
class ProjectSyncer < Syncer
|
8
|
+
self.abstract_command = false
|
9
|
+
self.command = "project"
|
10
|
+
self.summary = "Synchronize project localization strings with the remote localizations repo."
|
11
|
+
|
12
|
+
def initialize(argv)
|
13
|
+
super
|
14
|
+
@synchronized_file_names = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate!
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.options
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
ui.important("Nabokov starts project repo synchronization")
|
27
|
+
super
|
28
|
+
fetch_localization_repo_master_branch_changes
|
29
|
+
init_project_git_repo
|
30
|
+
checkout_project_repo_temporary_branch
|
31
|
+
has_changes = update_localization_files_in_project_repo
|
32
|
+
checkout_project_repo_original_branch
|
33
|
+
if has_changes
|
34
|
+
merge_project_repo_original_branch_with_temporary
|
35
|
+
end
|
36
|
+
delete_temporary_branch
|
37
|
+
notify_user_about_finish
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def notify_user_about_finish
|
43
|
+
if @synchronized_file_names.count > 0
|
44
|
+
ui.say("#{@synchronized_file_names} have been updated in project, your are all set for now 🎉")
|
45
|
+
else
|
46
|
+
ui.say("Nothing to synchronize, localizations in the remote repo and project repo are the same.")
|
47
|
+
end
|
48
|
+
ui.important("Nabokov has finished project repo synchronization")
|
49
|
+
end
|
50
|
+
|
51
|
+
def init_project_git_repo
|
52
|
+
@project_git_repo = GitRepo.new(@nabokovfile.project_local_path)
|
53
|
+
raise "Could not find the project repo at '#{Dir.exist?(@project_git_repo.local_path)}'" unless Dir.exist?(@project_git_repo.local_path)
|
54
|
+
ui.say("Found existed project repo at #{@project_git_repo.local_path}...") if self.verbose
|
55
|
+
@project_git_repo.init
|
56
|
+
@project_repo_original_branch = @project_git_repo.current_branch
|
57
|
+
end
|
58
|
+
|
59
|
+
def merge_project_repo_original_branch_with_temporary
|
60
|
+
merger = Merger.new(ui, @project_git_repo)
|
61
|
+
merger.merge(@project_repo_original_branch, temporary_branch)
|
62
|
+
end
|
63
|
+
|
64
|
+
def checkout_project_repo_temporary_branch
|
65
|
+
ui.say("Checkout porject repo temporary branch...") if self.verbose
|
66
|
+
@project_git_repo.checkout_branch(temporary_branch)
|
67
|
+
end
|
68
|
+
|
69
|
+
def checkout_project_repo_original_branch
|
70
|
+
ui.say("Checkout project repo #{@project_repo_original_branch} branch...") if self.verbose
|
71
|
+
@project_git_repo.checkout_branch(@project_repo_original_branch)
|
72
|
+
end
|
73
|
+
|
74
|
+
def update_localization_files_in_project_repo
|
75
|
+
has_changes = false
|
76
|
+
self.nabokovfile.project_localization_file_paths.each do |localization_file_name, localization_file_path|
|
77
|
+
localization_file_path_in_localization_repo = "#{self.git_repo.local_path}/#{localization_file_name}.#{Nabokov::StringsFile.extension}"
|
78
|
+
ui.say("Copying strings file from '#{localization_file_path_in_localization_repo}' to the project repo...") if self.verbose
|
79
|
+
new_file_path = FileManager.copy(localization_file_path_in_localization_repo, localization_file_path)
|
80
|
+
@project_git_repo.add(new_file_path)
|
81
|
+
if @project_git_repo.changes?
|
82
|
+
@synchronized_file_names << localization_file_name
|
83
|
+
@project_git_repo.commit("Nabokov has updated localization file '#{localization_file_name}'...")
|
84
|
+
has_changes = true
|
85
|
+
else
|
86
|
+
ui.say("'#{localization_file_name}' file doesn't have any changes to commit...") if self.verbose
|
87
|
+
end
|
88
|
+
end
|
89
|
+
has_changes
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch_localization_repo_master_branch_changes
|
93
|
+
ui.say("Fetching localization repo remote master branch changes...") if self.verbose
|
94
|
+
self.git_repo.pull
|
95
|
+
end
|
96
|
+
|
97
|
+
def delete_temporary_branch
|
98
|
+
ui.say("Deleting temporary branch...") if self.verbose
|
99
|
+
@project_git_repo.delete_branch(temporary_branch)
|
100
|
+
end
|
101
|
+
|
102
|
+
def temporary_branch
|
103
|
+
"nabokov/temporary_branch"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "nabokov/commands/runner"
|
2
|
+
require "nabokov/core/nabokovfile"
|
3
|
+
require "nabokov/core/file_manager"
|
4
|
+
require "nabokov/git/git_repo"
|
5
|
+
|
6
|
+
module Nabokov
|
7
|
+
class Syncer < Runner
|
8
|
+
attr_reader :nabokovfile
|
9
|
+
attr_reader :git_repo
|
10
|
+
|
11
|
+
self.abstract_command = true
|
12
|
+
self.summary = "Synchronization between localizations and project."
|
13
|
+
self.command = "sync"
|
14
|
+
|
15
|
+
def initialize(argv)
|
16
|
+
nabokovfile = argv.option("nabokovfile")
|
17
|
+
unless nabokovfile
|
18
|
+
pwd_nabokovfile = Pathname.pwd + "Nabokovfile.yaml"
|
19
|
+
nabokovfile = pwd_nabokovfile if File.exist?(pwd_nabokovfile)
|
20
|
+
end
|
21
|
+
raise "--nabokovfile is a required parameter and could not be nil" if nabokovfile.nil?
|
22
|
+
|
23
|
+
@nabokovfile_path = nabokovfile if File.exist?(nabokovfile)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate!
|
28
|
+
super
|
29
|
+
if self.class == Syncer && !@nabokovfile_path
|
30
|
+
help! "Could not find a Nabokovfile."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.options
|
35
|
+
[
|
36
|
+
["--nabokovfile=<path/to/nabokovfile>", "The location of your Nabokovfile"]
|
37
|
+
].concat(super)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
initialize_nabokov_file
|
42
|
+
init_git_repo
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize_nabokov_file
|
47
|
+
@nabokovfile = Nabokovfile.new(@nabokovfile_path)
|
48
|
+
ui.say("Hooray, your Nabokovfile is valid...") if self.verbose
|
49
|
+
end
|
50
|
+
|
51
|
+
def init_git_repo
|
52
|
+
@git_repo = GitRepo.new(@nabokovfile.localizations_repo_local_path, @nabokovfile.localizations_repo_url)
|
53
|
+
if Dir.exist?(@git_repo.local_path)
|
54
|
+
ui.say("Found existed repo at #{@git_repo.local_path}...") if self.verbose
|
55
|
+
@git_repo.init
|
56
|
+
else
|
57
|
+
ui.say("Cloning the localization repo from #{@git_repo.remote_url} into #{@git_repo.local_path}...") if self.verbose
|
58
|
+
@git_repo.clone
|
59
|
+
end
|
60
|
+
checkout_master_branch
|
61
|
+
end
|
62
|
+
|
63
|
+
def checkout_master_branch
|
64
|
+
ui.say("Checkout master branch...") if self.verbose
|
65
|
+
@git_repo.checkout_branch(@nabokovfile.localizations_repo_master_branch)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module Nabokov
|
5
|
+
# This class is the wrapper around the FileUtils
|
6
|
+
class FileManager
|
7
|
+
# Copies given file to the given destination
|
8
|
+
def self.copy(original_file_path, destination_file_path)
|
9
|
+
raise "Couldn't find file at '#{original_file_path}'" unless File.exist?(original_file_path)
|
10
|
+
FileUtils.cp(original_file_path, destination_file_path)
|
11
|
+
File.expand_path(destination_file_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Copies given file to the given destination and renames to the given name
|
15
|
+
# Note: the extension of the file remains the same that's why new name can not contain '.'
|
16
|
+
def self.copy_and_rename(original_file_path, to_directory, new_name)
|
17
|
+
raise "Couldn't find file at '#{original_file_path}'" unless File.exist?(original_file_path)
|
18
|
+
raise "Couldn't find directory at '#{to_directory}'" unless Dir.exist?(to_directory)
|
19
|
+
raise "New name of the file could not be empty" if new_name.empty?
|
20
|
+
raise "New name of the file '#{new_name}' contains invalid character '.'" if new_name.include?(".")
|
21
|
+
|
22
|
+
original_file_pathname = Pathname.new(original_file_path)
|
23
|
+
original_file_extension = original_file_pathname.extname
|
24
|
+
new_file_pathname = Pathname.new(to_directory) + Pathname.new(new_name + original_file_extension)
|
25
|
+
new_file_path = new_file_pathname.to_s
|
26
|
+
FileUtils.cp(original_file_path, new_file_path)
|
27
|
+
File.expand_path(new_file_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Removes the filve at the given path
|
31
|
+
def self.remove(path)
|
32
|
+
raise "Can not file neither file nor directory at '#{path}'" unless File.exist?(path) or Dir.exist?(path)
|
33
|
+
FileUtils.rm_rf(path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|