From be1e6cb003eb0e71cd89c6e87072b224cfc9d6f1 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Wed, 27 Jan 2016 14:28:37 -0200 Subject: [PATCH 01/61] [Spec] Including tests for spawn_helper utils --- spec/utils/git_helper_spec.js | 21 +++++++++++++++++++++ spec/utils/spawn_helper_spec.js | 19 +++++++++++++++++++ src/utils/git_helper.js | 22 ++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 spec/utils/git_helper_spec.js create mode 100644 spec/utils/spawn_helper_spec.js create mode 100644 src/utils/git_helper.js diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js new file mode 100644 index 00000000..b4f54572 --- /dev/null +++ b/spec/utils/git_helper_spec.js @@ -0,0 +1,21 @@ +import h from 'spec/spec_helper'; +import { default as gitHelper } from 'azk/utils/git_helper'; + +describe("Git Helper", function() { + let outputs = []; + let ui = h.mockUI(beforeEach, outputs); + + it("should run git --version", function() { + const command = gitHelper.version({verbose_level: 1, ui}); + return h.expect(command) + .to.eventually.not.be.undefined + .then(() => { + /**/console.log('\n>>---------\n outputs:\n', outputs, '\n>>---------\n');/*-debug-*/ + }); + }); + + it("should run git rev-parse HEAD", function() { + /**/console.log('\n>>---------\n __dirname:\n', __dirname, '\n>>---------\n');/*-debug-*/ + }); + +}); diff --git a/spec/utils/spawn_helper_spec.js b/spec/utils/spawn_helper_spec.js new file mode 100644 index 00000000..1551f882 --- /dev/null +++ b/spec/utils/spawn_helper_spec.js @@ -0,0 +1,19 @@ +import h from 'spec/spec_helper'; +import { lazy_require } from 'azk'; + +var lazy = lazy_require({ + spawnHelper: ['azk/utils/spawn_helper'], + printOutput: ['azk/utils/spawn_helper'], +}); + +describe("Spawn Helper", function() { + let outputs = []; + let ui = h.mockUI(beforeEach, outputs); + + it("should printOutput do not print if verbose_level = 0", function() { + const result = lazy.printOutput(ui.ok, 0, '[azk]', 'DATA'); + h.expect(result).to.be.undefined; + h.expect(outputs.length).to.equal(0); + }); + +}); diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js new file mode 100644 index 00000000..c7a97e39 --- /dev/null +++ b/src/utils/git_helper.js @@ -0,0 +1,22 @@ +import { lazy_require } from 'azk'; + +var lazy = lazy_require({ + spawnAsync : ['azk/utils/spawn_helper'], +}); + +var gitHelper = { + version: ({verbose_level, ui}) => { + return lazy.spawnAsync({ + executable : 'git', + params_array : [ + '--version' + ], + verbose_level : verbose_level, + uiOk : ui.ok.bind(ui), + spawn_prefix : '' + }); + + } +}; + +export default gitHelper; From 4e429a1f79d1c2442cd9c633f295718c26e7f12d Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Wed, 27 Jan 2016 16:30:55 -0200 Subject: [PATCH 02/61] [Cli] Refactoring spawn_helper.spawnAsync verbose option --- spec/manifest/get_project_spec.js | 40 ++++++++++------ spec/utils/spawn_helper_spec.js | 33 +++++++++++-- src/manifest/get_project.js | 77 +++++++++---------------------- src/utils/spawn_helper.js | 42 ++++++++--------- 4 files changed, 98 insertions(+), 94 deletions(-) diff --git a/spec/manifest/get_project_spec.js b/spec/manifest/get_project_spec.js index 4b9caee1..b9899e8f 100644 --- a/spec/manifest/get_project_spec.js +++ b/spec/manifest/get_project_spec.js @@ -5,22 +5,23 @@ import { promiseResolve } from 'azk/utils/promises'; describe('GetProject:', function() { - describe('parseCommandOptions:', function () { - // cli-router to parse arguments - var cli_options = {}; - var cli = new Cli(cli_options); + // cli-router to parse arguments + const cli_options = {}; + const cli = new Cli(cli_options); + + // parse arguments with parseCommandOptions + const cliRouterCleanParams = function (command_args) { + const doc_opts = { exit: false }; + doc_opts.argv = command_args.split(' ').splice(1); - // parse arguments with parseCommandOptions - var cliRouterCleanParams = function (command_args) { - var doc_opts = { exit: false }; - doc_opts.argv = command_args.split(' ').splice(1); + // parsed arguments + const cli_options = cli.router.cleanParams(cli.docopt(doc_opts)); - // parsed arguments - var cli_options = cli.router.cleanParams(cli.docopt(doc_opts)); + // azk start git repo parsed arguments + return GetProject.parseCommandOptions(cli_options); + }; - // azk start git repo parsed arguments - return GetProject.parseCommandOptions(cli_options); - }; + describe('parseCommandOptions:', function () { it('should git-ref=master with git-repo argument only', function() { var parsed_options = cliRouterCleanParams([ @@ -140,6 +141,19 @@ describe('GetProject:', function() { }); }); + it('should _gitspawn_VersionAsync get real current git version', function() { + var parsed_options = cliRouterCleanParams([ + 'azk start git@github.com:azukiapp/azkdemo.git', + '--git-ref master', + '-vv', + ].join(' ')); + getProject = new GetProject(ui, parsed_options); + return getProject._gitspawn_VersionAsync(2) + .then(function(result) { + h.expect(result.message).to.match(/git version/); + }); + }); + }); describe('_parseGitLsRemoteResult:', function () { diff --git a/spec/utils/spawn_helper_spec.js b/spec/utils/spawn_helper_spec.js index 1551f882..5de0b978 100644 --- a/spec/utils/spawn_helper_spec.js +++ b/spec/utils/spawn_helper_spec.js @@ -2,18 +2,45 @@ import h from 'spec/spec_helper'; import { lazy_require } from 'azk'; var lazy = lazy_require({ - spawnHelper: ['azk/utils/spawn_helper'], + spawnAsync: ['azk/utils/spawn_helper'], printOutput: ['azk/utils/spawn_helper'], }); describe("Spawn Helper", function() { let outputs = []; let ui = h.mockUI(beforeEach, outputs); + let stdOut = ui.stdout().write.bind(ui); it("should printOutput do not print if verbose_level = 0", function() { - const result = lazy.printOutput(ui.ok, 0, '[azk]', 'DATA'); - h.expect(result).to.be.undefined; + lazy.printOutput(stdOut, 0, '[azk]', 'DATA'); h.expect(outputs.length).to.equal(0); }); + it("should printOutput print if verbose_level = 1", function() { + lazy.printOutput(stdOut, 1, '[azk]', 'DATA'); + h.expect(outputs[0]).to.match(/\[azk\] DATA/); + }); + + it("should spawnAsync should return result and call scanFunction", function() { + const scanFunction = stdOut; + const spawnAsync_promise = lazy.spawnAsync('git', ['--version'], scanFunction); + + return spawnAsync_promise.then((result) => { + h.expect(result.error_code).to.equal(0); + h.expect(result.message).to.match(/git version/); + h.expect(outputs[0]).to.equal('$> git --version'); + h.expect(outputs[1]).to.match(/git version/); + }); + }); + + it("should spawnAsync should return result even without scanFunction", function() { + const spawnAsync_promise = lazy.spawnAsync('git', ['--version'], undefined); + + return spawnAsync_promise.then((result) => { + h.expect(result.error_code).to.equal(0); + h.expect(result.message).to.match(/git version/); + h.expect(outputs.length).to.equal(0); + }); + }); + }); diff --git a/src/manifest/get_project.js b/src/manifest/get_project.js index f59efc45..c2f2746e 100644 --- a/src/manifest/get_project.js +++ b/src/manifest/get_project.js @@ -2,7 +2,7 @@ import { path, lazy_require, config, log, fsAsync } from 'azk'; import { async, promiseResolve, promiseReject } from 'azk/utils/promises'; import { UIProxy } from 'azk/cli/ui'; import { matchFirstRegex, matchAllRegex, fulltrim, groupFromRegex } from 'azk/utils/regex_helper'; -import { spawnAsync } from 'azk/utils/spawn_helper'; +import { spawnAsync, printOutput } from 'azk/utils/spawn_helper'; import { GitCallError } from 'azk/utils/errors'; var lazy = lazy_require({ @@ -11,10 +11,16 @@ var lazy = lazy_require({ export class GetProject extends UIProxy { - constructor(...args) { - super(...args); + constructor(ui, args) { + super(ui, args); this.IS_NEW_GIT_VERSION_AFTER = '1.7.10'; this.is_new_git = null; + + this.gitOutput = (data) => printOutput( + this.ok.bind(this), + args.verbose_level, + '[git]', + data); } static valid(url) { @@ -196,8 +202,8 @@ export class GetProject extends UIProxy { null)); } - _getGitRemoteInfo(git_url, verbose_level) { - return this._gitspawn_LsRemoteAsync(git_url, verbose_level) + _getGitRemoteInfo(git_url) { + return this._gitspawn_LsRemoteAsync(git_url) .then((git_result_obj) => { this.ok('commands.start.get_project.getting_remote_info', {git_url}); var parsed_result = this._parseGitLsRemoteResult(git_result_obj.message); @@ -357,32 +363,15 @@ export class GetProject extends UIProxy { /********************** gitspaw _Async calls * **********************/ - _gitspawn_VersionAsync(verbose_level) { - return spawnAsync({ - executable : 'git', - params_array : [ - '--version' - ], - verbose_level : verbose_level, - uiOk : this.ok.bind(this), - spawn_prefix : '[git]' - }); + _gitspawn_VersionAsync() { + return spawnAsync('git', ['--version'], this.gitOutput); } - _gitspawn_LsRemoteAsync(git_url, verbose_level) { - return spawnAsync({ - executable : 'git', - params_array : [ - 'ls-remote', - git_url - ], - verbose_level : verbose_level, - uiOk : this.ok.bind(this), - spawn_prefix : '[git]' - }); + _gitspawn_LsRemoteAsync(git_url) { + return spawnAsync('git', ['ls-remote', git_url], this.gitOutput); } - _gitspawn_PullAsync(git_url, git_branch_tag_commit, dest_folder, verbose_level) { + _gitspawn_PullAsync(git_url, git_branch_tag_commit, dest_folder) { var git_params = [ '--git-dir', @@ -394,16 +383,10 @@ export class GetProject extends UIProxy { git_branch_tag_commit ]; - return spawnAsync({ - executable : 'git', - params_array : git_params, - verbose_level : verbose_level, - uiOk : this.ok.bind(this), - spawn_prefix : '[git]' - }); + return spawnAsync('git', git_params, this.gitOutput); } - _gitspawn_CloneAsync(git_url, git_branch_tag_commit, dest_folder, verbose_level) { + _gitspawn_CloneAsync(git_url, git_branch_tag_commit, dest_folder) { var git_params = [ 'clone', @@ -418,27 +401,11 @@ export class GetProject extends UIProxy { git_params.push(git_branch_tag_commit); } - return spawnAsync({ - executable : 'git', - params_array : git_params, - verbose_level : verbose_level, - uiOk : this.ok.bind(this), - spawn_prefix : '[git]' - }); + return spawnAsync('git', git_params, this.gitOutput); } - _gitspawn_CheckoutInFolderAsync(git_url, git_branch_tag_commit, dest_folder, verbose_level) { - return spawnAsync({ - executable : 'git', - params_array : [ - '-C', - dest_folder, - 'checkout', - git_branch_tag_commit - ], - verbose_level : verbose_level, - uiOk : this.ok.bind(this), - spawn_prefix : '[git]' - }); + _gitspawn_CheckoutInFolderAsync(git_url, git_branch_tag_commit, dest_folder) { + return spawnAsync('git', ['-C', dest_folder, 'checkout', git_branch_tag_commit], + this.gitOutput); } } diff --git a/src/utils/spawn_helper.js b/src/utils/spawn_helper.js index 96c19f6e..21aa90fa 100644 --- a/src/utils/spawn_helper.js +++ b/src/utils/spawn_helper.js @@ -7,6 +7,13 @@ var lazy = lazy_require({ colors : ['azk/utils/colors'], }); +/** + * Print output + * @param function uiOk some output function + * @param number verbose_level if 0 do not print + * @param prefix prefix some prefix + * @param string data what to print + */ export function printOutput(uiOk, verbose_level, prefix, data) { // exit if verbose is zero if (verbose_level === 0 || !data) { @@ -21,40 +28,29 @@ export function printOutput(uiOk, verbose_level, prefix, data) { uiOk(prefixed_output); } -export function spawnAsync(opts) { +/** + * Executes a command against shell + * @param string executable executable path + * @param array params_array array with all parameters + * @param function scanFunction [otional] output function + * @return Promisse { error_code: 0, message: 'COMMAND RESULT1\nCOMMAND RESULT2' } + */ +export function spawnAsync(executable, params_array, scanFunction) { return defer(function (resolve, reject) { - var spawn_cmd = spawn(opts.executable, opts.params_array); + var spawn_cmd = spawn(executable, params_array); var outputs = []; // print command - var full_command = ('$> ' + opts.executable + ' ' + lazy.colors.bold(opts.params_array.join(' '))); - printOutput( - opts.uiOk, - opts.verbose_level, - opts.spawn_prefix, - full_command - ); + scanFunction && scanFunction(`$> ${executable} ${lazy.colors.bold(params_array.join(' '))}`); spawn_cmd.stdout.on('data', function (data) { outputs.push(data); - - // print output - printOutput( - opts.uiOk, - opts.verbose_level, - opts.spawn_prefix, - data); + scanFunction && scanFunction(data); }); spawn_cmd.stderr.on('data', function (data) { outputs.push(data); - - // print output - printOutput( - opts.uiOk, - opts.verbose_level, - opts.spawn_prefix, - data); + scanFunction && scanFunction(data); }); spawn_cmd.on('close', function (code) { From 3c914ba7c9a2f7eb9f121857a21e6feac3a958da Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Thu, 28 Jan 2016 12:02:32 -0200 Subject: [PATCH 03/61] [Core] Azk.isDev property check if is dev version --- spec/index_spec.js | 14 ++++++++++++++ spec/utils/git_helper_spec.js | 10 +++++++--- spec/utils/spawn_helper_spec.js | 16 ++++++++-------- src/index.js | 8 ++++++++ src/utils/git_helper.js | 13 ++----------- 5 files changed, 39 insertions(+), 22 deletions(-) create mode 100644 spec/index_spec.js diff --git a/spec/index_spec.js b/spec/index_spec.js new file mode 100644 index 00000000..9287441d --- /dev/null +++ b/spec/index_spec.js @@ -0,0 +1,14 @@ +import h from 'spec/spec_helper'; +import Azk from 'azk'; + +describe('azk main module', function() { + + it('should `version` get azk current version', function() { + h.expect(Azk.version).to.match(/\d+\.\d+\.\d+/); + }); + + it('should `isDev` check if current azk is a dev version', function() { + h.expect(Azk.isDev).to.equal(true); + }); + +}); diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js index b4f54572..dc172442 100644 --- a/spec/utils/git_helper_spec.js +++ b/spec/utils/git_helper_spec.js @@ -6,16 +6,20 @@ describe("Git Helper", function() { let ui = h.mockUI(beforeEach, outputs); it("should run git --version", function() { - const command = gitHelper.version({verbose_level: 1, ui}); + const command = gitHelper.version(ui.stdout().write); return h.expect(command) .to.eventually.not.be.undefined .then(() => { - /**/console.log('\n>>---------\n outputs:\n', outputs, '\n>>---------\n');/*-debug-*/ + h.expect(outputs[0]).to.equal('$> git --version'); + h.expect(outputs[1]).to.match(/git version/); }); }); it("should run git rev-parse HEAD", function() { - /**/console.log('\n>>---------\n __dirname:\n', __dirname, '\n>>---------\n');/*-debug-*/ + const azkDevPathMatch = __dirname.match(process.env.AZK_ROOT_PATH); + /**/console.log('\n>>---------\n azkDevPathMatch:\n', azkDevPathMatch, '\n>>---------\n');/*-debug-*/ + const isAzkDev = azkDevPathMatch !== null; + /**/console.log('\n>>---------\n isAzkDev:\n', isAzkDev, '\n>>---------\n');/*-debug-*/ }); }); diff --git a/spec/utils/spawn_helper_spec.js b/spec/utils/spawn_helper_spec.js index 5de0b978..4fbc55f4 100644 --- a/spec/utils/spawn_helper_spec.js +++ b/spec/utils/spawn_helper_spec.js @@ -21,25 +21,25 @@ describe("Spawn Helper", function() { h.expect(outputs[0]).to.match(/\[azk\] DATA/); }); - it("should spawnAsync should return result and call scanFunction", function() { - const scanFunction = stdOut; - const spawnAsync_promise = lazy.spawnAsync('git', ['--version'], scanFunction); + it("should spawnAsync should return result even without scanFunction", function() { + const spawnAsync_promise = lazy.spawnAsync('git', ['--version'], undefined); return spawnAsync_promise.then((result) => { h.expect(result.error_code).to.equal(0); h.expect(result.message).to.match(/git version/); - h.expect(outputs[0]).to.equal('$> git --version'); - h.expect(outputs[1]).to.match(/git version/); + h.expect(outputs.length).to.equal(0); }); }); - it("should spawnAsync should return result even without scanFunction", function() { - const spawnAsync_promise = lazy.spawnAsync('git', ['--version'], undefined); + it("should spawnAsync should return result and call scanFunction", function() { + const scanFunction = stdOut; + const spawnAsync_promise = lazy.spawnAsync('git', ['--version'], scanFunction); return spawnAsync_promise.then((result) => { h.expect(result.error_code).to.equal(0); h.expect(result.message).to.match(/git version/); - h.expect(outputs.length).to.equal(0); + h.expect(outputs[0]).to.equal('$> git --version'); + h.expect(outputs[1]).to.match(/git version/); }); }); diff --git a/src/index.js b/src/index.js index b86a9994..5a5701fc 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,13 @@ class Azk { static get version() { return require('package.json').version; } + static get isDev() { + const currentDir = __dirname; + const azkRootPath = process.env.AZK_ROOT_PATH; + const azkDevPathMatch = currentDir.match(azkRootPath); + const isAzkDev = azkDevPathMatch !== null; + return isAzkDev; + } } // Default i18n method @@ -48,6 +55,7 @@ var GeralLib = { get fsAsync() { return require('file-async'); }, get utils () { return require('azk/utils'); }, get version() { return Azk.version; }, + get isDev() { return Azk.isDev; }, get isBlank() { return isBlank; }, get lazy_require() { diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index c7a97e39..5220d29a 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -5,17 +5,8 @@ var lazy = lazy_require({ }); var gitHelper = { - version: ({verbose_level, ui}) => { - return lazy.spawnAsync({ - executable : 'git', - params_array : [ - '--version' - ], - verbose_level : verbose_level, - uiOk : ui.ok.bind(ui), - spawn_prefix : '' - }); - + version: (scanFunction) => { + return lazy.spawnAsync('git', ['--version'], scanFunction); } }; From 3342a6c4c7e56bcf2bb8c855e9f8c0c28c57d2d8 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Thu, 28 Jan 2016 14:02:45 -0200 Subject: [PATCH 04/61] [Cli] azk version shows Azk.gitCommitId --- spec/index_spec.js | 4 ++-- spec/utils/git_helper_spec.js | 16 ++++++++++----- src/cmds/version.js | 6 ++++-- src/config.js | 3 ++- src/index.js | 37 ++++++++++++++++++++++------------- src/utils/git_helper.js | 15 ++++++++++++++ 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/spec/index_spec.js b/spec/index_spec.js index 9287441d..4584235a 100644 --- a/spec/index_spec.js +++ b/spec/index_spec.js @@ -7,8 +7,8 @@ describe('azk main module', function() { h.expect(Azk.version).to.match(/\d+\.\d+\.\d+/); }); - it('should `isDev` check if current azk is a dev version', function() { - h.expect(Azk.isDev).to.equal(true); + it('should `gitCommitId` check if current azk is a dev version', function() { + return h.expect(Azk.gitCommitId).to.eventually.not.be.undefined; }); }); diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js index dc172442..baf6c9b1 100644 --- a/spec/utils/git_helper_spec.js +++ b/spec/utils/git_helper_spec.js @@ -1,5 +1,6 @@ import h from 'spec/spec_helper'; import { default as gitHelper } from 'azk/utils/git_helper'; +import { config, path, fsAsync } from 'azk'; describe("Git Helper", function() { let outputs = []; @@ -16,10 +17,15 @@ describe("Git Helper", function() { }); it("should run git rev-parse HEAD", function() { - const azkDevPathMatch = __dirname.match(process.env.AZK_ROOT_PATH); - /**/console.log('\n>>---------\n azkDevPathMatch:\n', azkDevPathMatch, '\n>>---------\n');/*-debug-*/ - const isAzkDev = azkDevPathMatch !== null; - /**/console.log('\n>>---------\n isAzkDev:\n', isAzkDev, '\n>>---------\n');/*-debug-*/ + const azkRootPath = config('paths:azk_root'); + const git_path = path.join(azkRootPath, '.git'); + return fsAsync.exists(git_path) + .then((exists) => { + h.expect(exists).to.be.equal(true); + return gitHelper.revParse('HEAD', git_path, ui.stdout().write) + .then((commit_id) => { + h.expect(commit_id).to.not.be.undefined; + }); + }); }); - }); diff --git a/src/cmds/version.js b/src/cmds/version.js index ec066077..7ef737be 100644 --- a/src/cmds/version.js +++ b/src/cmds/version.js @@ -8,7 +8,9 @@ export default class Version extends CliTrackerController { } index() { - this.ui.output("azk %s", Azk.version); - return 0; + return Azk.gitCommitId.then((commitId) => { + this.ui.output(`azk version ${Azk.version}, build ${commitId}`); + return 0; + }); } } diff --git a/src/config.js b/src/config.js index 14291cd8..7761dcc3 100644 --- a/src/config.js +++ b/src/config.js @@ -52,6 +52,7 @@ var options = mergeConfig({ manifest : "Azkfile.js", locale : 'en-US', azk_dir : azk_dir, + azk_last_commit: envs("AZK_LAST_COMMIT"), flags : { show_deprecate: envs('AZK_HIDE_DEPRECATE', false), require_accept_use_terms: envs('AZK_REQUIRE_TERMS', true), @@ -79,7 +80,7 @@ var options = mergeConfig({ data_mnt_path : data_mnt_path, persistent_folders: path.join(data_mnt_path, 'persistent_folders'), sync_folders : path.join(data_mnt_path, 'sync_folders'), - analytics : path.join(data_path, azk_dir, "analytics"), + analytics : path.join(data_path, azk_dir, "analytics") }, logs_level: { console: (envs('AZK_DEBUG') ? 'debug' : envs('AZK_OUTPUT_LOG_LEVEL', 'error')), diff --git a/src/index.js b/src/index.js index 5a5701fc..9d8ce8b6 100644 --- a/src/index.js +++ b/src/index.js @@ -8,12 +8,21 @@ class Azk { static get version() { return require('package.json').version; } - static get isDev() { - const currentDir = __dirname; - const azkRootPath = process.env.AZK_ROOT_PATH; - const azkDevPathMatch = currentDir.match(azkRootPath); - const isAzkDev = azkDevPathMatch !== null; - return isAzkDev; + static get gitCommitId() { + const config = GeralLib.config; + const path = GeralLib.path; + + // git commit id from ENV + const azk_last_commit = config('azk_last_commit'); + if (azk_last_commit) { + return azk_last_commit; + } + + // git commit id from git_helper + const azkRootPath = config('paths:azk_root'); + const git_path = path.join(azkRootPath, '.git'); + const gitHelper = require('azk/utils/git_helper'); + return gitHelper.revParse('HEAD', git_path); } } @@ -49,14 +58,14 @@ var GeralLib = { }, // Internals alias - get os () { return require('os'); }, - get path () { return require('path'); }, - get fs () { return require('fs'); }, - get fsAsync() { return require('file-async'); }, - get utils () { return require('azk/utils'); }, - get version() { return Azk.version; }, - get isDev() { return Azk.isDev; }, - get isBlank() { return isBlank; }, + get os () { return require('os'); }, + get path () { return require('path'); }, + get fs () { return require('fs'); }, + get fsAsync () { return require('file-async'); }, + get utils () { return require('azk/utils'); }, + get version () { return Azk.version; }, + get gitCommitId() { return Azk.gitCommitId; }, + get isBlank () { return isBlank; }, get lazy_require() { return require('azk/utils').lazy_require; diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index 5220d29a..3e6ad14f 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -7,6 +7,21 @@ var lazy = lazy_require({ var gitHelper = { version: (scanFunction) => { return lazy.spawnAsync('git', ['--version'], scanFunction); + }, + + revParse: (revision, location, scanFunction) => { + return lazy.spawnAsync('git', [ + // --git-dir=.git rev-parse --verify HEAD + '--git-dir', + location, + 'rev-parse', + '--verify', + revision + ], scanFunction) + .then((result) => { + return result.message + .substring(0, 7); + }); } }; From 66789aa2112834f05266787bcaef7cc5ed61d586 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Thu, 28 Jan 2016 14:27:32 -0200 Subject: [PATCH 05/61] [Spec] azk version spec fixed --- spec/cmds/version_spec.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/cmds/version_spec.js b/spec/cmds/version_spec.js index 33a3e094..0176bd48 100644 --- a/spec/cmds/version_spec.js +++ b/spec/cmds/version_spec.js @@ -1,6 +1,5 @@ import h from 'spec/spec_helper'; import { Cli } from 'azk/cli'; -import Azk from 'azk'; describe('Azk cli, version controller', function() { var outputs = []; @@ -12,7 +11,7 @@ describe('Azk cli, version controller', function() { var doc_opts = { exit: false }; var run_options = { ui: ui }; - var version = `azk ${Azk.version}`; + var version_regex = /azk version \d+\.\d+\.\d+, build \w+/; it('should run a version command', function() { doc_opts.argv = 'version'; @@ -20,7 +19,7 @@ describe('Azk cli, version controller', function() { return cli.run(doc_opts, run_options).then((code) => { h.expect(code).to.eql(0); h.expect(options).to.have.property('version', true); - h.expect(outputs[0]).to.eql(version); + h.expect(outputs[0]).to.match(version_regex); }); }); @@ -28,7 +27,7 @@ describe('Azk cli, version controller', function() { doc_opts.argv = '--version'; return cli.run(doc_opts, run_options).then((code) => { h.expect(code).to.eql(0); - h.expect(outputs[0]).to.eql(version); + h.expect(outputs[0]).to.match(version_regex); }); }); }); From cc7e8b8fde389209aae93caa94922f2fcb128ed2 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Fri, 29 Jan 2016 17:47:34 -0200 Subject: [PATCH 06/61] [Code] gitCommitIdAsync can receive last_commit_id --- spec/index_spec.js | 10 ++++++++-- src/cmds/version.js | 5 ++++- src/index.js | 15 +++++++++------ src/utils/git_helper.js | 5 +++-- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/spec/index_spec.js b/spec/index_spec.js index 4584235a..cf550fe1 100644 --- a/spec/index_spec.js +++ b/spec/index_spec.js @@ -7,8 +7,14 @@ describe('azk main module', function() { h.expect(Azk.version).to.match(/\d+\.\d+\.\d+/); }); - it('should `gitCommitId` check if current azk is a dev version', function() { - return h.expect(Azk.gitCommitId).to.eventually.not.be.undefined; + it('should `gitCommitIdAsync` get current commit id from ENV', function() { + const last_commit_id = '123'; + return h.expect(Azk.gitCommitIdAsync(last_commit_id)) + .to.eventually.to.equal(last_commit_id); + }); + + it('should `gitCommitIdAsync` get current commit id from dev project', function() { + return h.expect(Azk.gitCommitIdAsync()).to.eventually.not.be.undefined; }); }); diff --git a/src/cmds/version.js b/src/cmds/version.js index 7ef737be..4220b477 100644 --- a/src/cmds/version.js +++ b/src/cmds/version.js @@ -1,5 +1,6 @@ import { CliTrackerController } from 'azk/cli/cli_tracker_controller.js'; import Azk from 'azk'; +import { config } from 'azk'; export default class Version extends CliTrackerController { constructor(...args) { @@ -8,7 +9,9 @@ export default class Version extends CliTrackerController { } index() { - return Azk.gitCommitId.then((commitId) => { + const azk_last_commit = config('azk_last_commit'); + return Azk.gitCommitIdAsync(azk_last_commit) + .then((commitId) => { this.ui.output(`azk version ${Azk.version}, build ${commitId}`); return 0; }); diff --git a/src/index.js b/src/index.js index 9d8ce8b6..79c56afb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,21 +1,24 @@ import { _, isBlank } from 'azk/utils'; +import { promiseResolve } from 'azk/utils/promises'; try { require("babel-polyfill"); } catch (e) {} class Azk { + static get version() { return require('package.json').version; } - static get gitCommitId() { - const config = GeralLib.config; + + static gitCommitIdAsync(azk_last_commit) { const path = GeralLib.path; + const config = GeralLib.config; // git commit id from ENV - const azk_last_commit = config('azk_last_commit'); - if (azk_last_commit) { - return azk_last_commit; + const commit_id = azk_last_commit; + if (commit_id) { + return promiseResolve(commit_id); } // git commit id from git_helper @@ -64,7 +67,7 @@ var GeralLib = { get fsAsync () { return require('file-async'); }, get utils () { return require('azk/utils'); }, get version () { return Azk.version; }, - get gitCommitId() { return Azk.gitCommitId; }, + get gitCommitIdAsync() { return Azk.gitCommitIdAsync; }, get isBlank () { return isBlank; }, get lazy_require() { diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index 3e6ad14f..f429a98b 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -1,4 +1,5 @@ import { lazy_require } from 'azk'; +import { promiseResolve } from 'azk/utils/promises'; var lazy = lazy_require({ spawnAsync : ['azk/utils/spawn_helper'], @@ -19,8 +20,8 @@ var gitHelper = { revision ], scanFunction) .then((result) => { - return result.message - .substring(0, 7); + const commit_id = result.message.substring(0, 7); + return promiseResolve(commit_id); }); } }; From d2fc0faefc1873fb4bd66c73db176ef0ffc29136 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Fri, 29 Jan 2016 18:22:54 -0200 Subject: [PATCH 07/61] [Code] GetProject gets git version from git_helper --- spec/manifest/get_project_spec.js | 18 +++++++++--------- spec/utils/git_helper_spec.js | 1 + src/manifest/get_project.js | 15 ++++++--------- src/utils/git_helper.js | 8 +++++++- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/spec/manifest/get_project_spec.js b/spec/manifest/get_project_spec.js index b9899e8f..0cbe4aec 100644 --- a/spec/manifest/get_project_spec.js +++ b/spec/manifest/get_project_spec.js @@ -126,31 +126,31 @@ describe('GetProject:', function() { }); it('should be an old git version when < 1.7.10', function() { - getProject._gitspawn_VersionAsync = () => promiseResolve({code: 0, message: '1.7.9'}); - return getProject._checkGitVersion(0) + getProject._gitHelper.version = () => promiseResolve('1.7.9'); + return getProject._checkGitVersion() .then(function() { h.expect(getProject.is_new_git).to.be.false; }); }); - it('should be a new git version when >= 1.7.10', function() { - getProject._gitspawn_VersionAsync = () => promiseResolve({code: 0, message: '1.7.10'}); - return getProject._checkGitVersion(0) + it('should be a new git version when >= 1.7.10', function() { + getProject._gitHelper.version = () => promiseResolve('1.7.10'); + return getProject._checkGitVersion() .then(function() { h.expect(getProject.is_new_git).to.be.true; }); }); - it('should _gitspawn_VersionAsync get real current git version', function() { + it('should _gitHelper.version get real current git version', function() { var parsed_options = cliRouterCleanParams([ 'azk start git@github.com:azukiapp/azkdemo.git', '--git-ref master', '-vv', ].join(' ')); getProject = new GetProject(ui, parsed_options); - return getProject._gitspawn_VersionAsync(2) - .then(function(result) { - h.expect(result.message).to.match(/git version/); + return getProject._gitHelper.version(getProject.gitOutput) + .then(function(git_version) { + h.expect(git_version).to.match(/\d+\.\d+\.\d+/); }); }); diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js index baf6c9b1..f3167a0e 100644 --- a/spec/utils/git_helper_spec.js +++ b/spec/utils/git_helper_spec.js @@ -28,4 +28,5 @@ describe("Git Helper", function() { }); }); }); + }); diff --git a/src/manifest/get_project.js b/src/manifest/get_project.js index c2f2746e..7197e443 100644 --- a/src/manifest/get_project.js +++ b/src/manifest/get_project.js @@ -1,12 +1,13 @@ import { path, lazy_require, config, log, fsAsync } from 'azk'; import { async, promiseResolve, promiseReject } from 'azk/utils/promises'; import { UIProxy } from 'azk/cli/ui'; -import { matchFirstRegex, matchAllRegex, fulltrim, groupFromRegex } from 'azk/utils/regex_helper'; +import { matchFirstRegex, matchAllRegex, fulltrim } from 'azk/utils/regex_helper'; import { spawnAsync, printOutput } from 'azk/utils/spawn_helper'; import { GitCallError } from 'azk/utils/errors'; var lazy = lazy_require({ semver: 'semver', + git_helper: 'azk/utils/git_helper', }); export class GetProject extends UIProxy { @@ -15,6 +16,7 @@ export class GetProject extends UIProxy { super(ui, args); this.IS_NEW_GIT_VERSION_AFTER = '1.7.10'; this.is_new_git = null; + this._gitHelper = lazy.git_helper; this.gitOutput = (data) => printOutput( this.ok.bind(this), @@ -187,12 +189,11 @@ export class GetProject extends UIProxy { }); } - _checkGitVersion(verbose_level) { + _checkGitVersion() { this.ok('commands.start.get_project.getting_git_version'); - return this._gitspawn_VersionAsync(verbose_level) - .then((git_result_obj) => { - var git_version = groupFromRegex(git_result_obj.message, /.*?(\d+\.\d+\.\d+)/, 1); + return this._gitHelper.version(this.gitOutput) + .then((git_version) => { this.is_new_git = lazy.semver.gte(git_version, this.IS_NEW_GIT_VERSION_AFTER); return git_version; }) @@ -363,10 +364,6 @@ export class GetProject extends UIProxy { /********************** gitspaw _Async calls * **********************/ - _gitspawn_VersionAsync() { - return spawnAsync('git', ['--version'], this.gitOutput); - } - _gitspawn_LsRemoteAsync(git_url) { return spawnAsync('git', ['ls-remote', git_url], this.gitOutput); } diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index f429a98b..6fce809c 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -1,5 +1,7 @@ import { lazy_require } from 'azk'; import { promiseResolve } from 'azk/utils/promises'; +import { groupFromRegex } from 'azk/utils/regex_helper'; +// matchFirstRegex, matchAllRegex, fulltrim, var lazy = lazy_require({ spawnAsync : ['azk/utils/spawn_helper'], @@ -7,7 +9,11 @@ var lazy = lazy_require({ var gitHelper = { version: (scanFunction) => { - return lazy.spawnAsync('git', ['--version'], scanFunction); + return lazy.spawnAsync('git', ['--version'], scanFunction) + .then((result) => { + var git_version = groupFromRegex(result.message, /.*?(\d+\.\d+\.\d+)/, 1); + return promiseResolve(git_version); + }); }, revParse: (revision, location, scanFunction) => { From bb723c6881d1ee512b8e02128367c3dd35bd4bd2 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Fri, 29 Jan 2016 18:51:26 -0200 Subject: [PATCH 08/61] [Code] GetProject gets git ls-remote from git_helper --- spec/utils/git_helper_spec.js | 33 +++++++++++++++++++++------------ src/manifest/get_project.js | 10 +++------- src/utils/git_helper.js | 17 +++++++++++++---- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js index f3167a0e..c2ad5ac1 100644 --- a/spec/utils/git_helper_spec.js +++ b/spec/utils/git_helper_spec.js @@ -1,12 +1,14 @@ import h from 'spec/spec_helper'; import { default as gitHelper } from 'azk/utils/git_helper'; -import { config, path, fsAsync } from 'azk'; +import { config, path } from 'azk'; describe("Git Helper", function() { let outputs = []; - let ui = h.mockUI(beforeEach, outputs); + const ui = h.mockUI(beforeEach, outputs); + const azkRootPath = config('paths:azk_root'); + const git_path = path.join(azkRootPath, '.git'); - it("should run git --version", function() { + it("should run scan `git --version` to stdout", function() { const command = gitHelper.version(ui.stdout().write); return h.expect(command) .to.eventually.not.be.undefined @@ -16,16 +18,23 @@ describe("Git Helper", function() { }); }); + it("should return version on `git --version`", function() { + const command = gitHelper.version(null); + return h.expect(command) + .to.eventually.to.match(/\d+\.\d+\.\d+/); + }); + it("should run git rev-parse HEAD", function() { - const azkRootPath = config('paths:azk_root'); - const git_path = path.join(azkRootPath, '.git'); - return fsAsync.exists(git_path) - .then((exists) => { - h.expect(exists).to.be.equal(true); - return gitHelper.revParse('HEAD', git_path, ui.stdout().write) - .then((commit_id) => { - h.expect(commit_id).to.not.be.undefined; - }); + return gitHelper.revParse('HEAD', git_path) + .then((commit_id) => { + h.expect(commit_id).to.not.be.undefined; + }); + }); + + it("should lsRemote get remote data @slow", function() { + return gitHelper.lsRemote('https://github.com/azukiapp/azk.git') + .then((result) => { + h.expect(result).to.match(/HEAD/g); }); }); diff --git a/src/manifest/get_project.js b/src/manifest/get_project.js index 7197e443..868b0ba6 100644 --- a/src/manifest/get_project.js +++ b/src/manifest/get_project.js @@ -204,10 +204,10 @@ export class GetProject extends UIProxy { } _getGitRemoteInfo(git_url) { - return this._gitspawn_LsRemoteAsync(git_url) - .then((git_result_obj) => { + return this._gitHelper.lsRemote(git_url, this.gitOutput) + .then((lsRemote_result) => { this.ok('commands.start.get_project.getting_remote_info', {git_url}); - var parsed_result = this._parseGitLsRemoteResult(git_result_obj.message); + var parsed_result = this._parseGitLsRemoteResult(lsRemote_result); return parsed_result; }) .catch(this._checkGitError( @@ -364,10 +364,6 @@ export class GetProject extends UIProxy { /********************** gitspaw _Async calls * **********************/ - _gitspawn_LsRemoteAsync(git_url) { - return spawnAsync('git', ['ls-remote', git_url], this.gitOutput); - } - _gitspawn_PullAsync(git_url, git_branch_tag_commit, dest_folder) { var git_params = [ diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index 6fce809c..d3e77360 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -16,11 +16,10 @@ var gitHelper = { }); }, - revParse: (revision, location, scanFunction) => { + revParse: (revision, git_path, scanFunction) => { return lazy.spawnAsync('git', [ - // --git-dir=.git rev-parse --verify HEAD '--git-dir', - location, + git_path, 'rev-parse', '--verify', revision @@ -29,7 +28,17 @@ var gitHelper = { const commit_id = result.message.substring(0, 7); return promiseResolve(commit_id); }); - } + }, + + lsRemote: (git_url, scanFunction) => { + return lazy.spawnAsync('git', [ + 'ls-remote', + git_url, + ], scanFunction) + .then((result) => { + return promiseResolve(result.message); + }); + }, }; export default gitHelper; From 05e05875311efa5e661bf13706bfd475cb126e3b Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Sat, 30 Jan 2016 15:09:32 -0200 Subject: [PATCH 09/61] [Code] GetProject clone, pull and checkout --- spec/manifest/get_project_spec.js | 15 ++--- spec/utils/git_helper_spec.js | 81 ++++++++++++++++++------ src/manifest/get_project.js | 102 +++++++----------------------- src/utils/git_helper.js | 83 ++++++++++++++++++++++-- 4 files changed, 163 insertions(+), 118 deletions(-) diff --git a/spec/manifest/get_project_spec.js b/spec/manifest/get_project_spec.js index 0cbe4aec..6153240f 100644 --- a/spec/manifest/get_project_spec.js +++ b/spec/manifest/get_project_spec.js @@ -1,7 +1,6 @@ import h from 'spec/spec_helper'; import { GetProject } from 'azk/manifest/get_project'; import { Cli } from 'azk/cli'; -import { promiseResolve } from 'azk/utils/promises'; describe('GetProject:', function() { @@ -126,19 +125,13 @@ describe('GetProject:', function() { }); it('should be an old git version when < 1.7.10', function() { - getProject._gitHelper.version = () => promiseResolve('1.7.9'); - return getProject._checkGitVersion() - .then(function() { - h.expect(getProject.is_new_git).to.be.false; - }); + getProject._checkGitVersion('1.7.9'); + h.expect(getProject.is_new_git).to.be.false; }); it('should be a new git version when >= 1.7.10', function() { - getProject._gitHelper.version = () => promiseResolve('1.7.10'); - return getProject._checkGitVersion() - .then(function() { - h.expect(getProject.is_new_git).to.be.true; - }); + getProject._checkGitVersion('1.7.10'); + h.expect(getProject.is_new_git).to.be.true; }); it('should _gitHelper.version get real current git version', function() { diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js index c2ad5ac1..a7c18720 100644 --- a/spec/utils/git_helper_spec.js +++ b/spec/utils/git_helper_spec.js @@ -2,40 +2,79 @@ import h from 'spec/spec_helper'; import { default as gitHelper } from 'azk/utils/git_helper'; import { config, path } from 'azk'; -describe("Git Helper", function() { - let outputs = []; - const ui = h.mockUI(beforeEach, outputs); +describe('Git Helper', function() { const azkRootPath = config('paths:azk_root'); const git_path = path.join(azkRootPath, '.git'); - it("should run scan `git --version` to stdout", function() { - const command = gitHelper.version(ui.stdout().write); - return h.expect(command) - .to.eventually.not.be.undefined - .then(() => { - h.expect(outputs[0]).to.equal('$> git --version'); - h.expect(outputs[1]).to.match(/git version/); - }); + it('should run scan `git --version` to stdout', function() { + let outputs = []; + const writeToOutput = (data) => { + outputs.push(data.toString()); + }; + return gitHelper.version(writeToOutput) + .then(() => { + h.expect(outputs).to.containSubset(['$> git --version']); + }); }); - it("should return version on `git --version`", function() { + it('should return version on `git --version`', function() { const command = gitHelper.version(null); return h.expect(command) .to.eventually.to.match(/\d+\.\d+\.\d+/); }); - it("should run git rev-parse HEAD", function() { - return gitHelper.revParse('HEAD', git_path) - .then((commit_id) => { - h.expect(commit_id).to.not.be.undefined; - }); + it('should run git rev-parse HEAD', function() { + const command = gitHelper.revParse('HEAD', git_path, null); + return h.expect(command) + .to.eventually.to.not.be.undefined; + }); + + it('should lsRemote get remote data @slow', function() { + const GIT_URL = 'https://github.com/azukiapp/azkdemo'; + const command = gitHelper.lsRemote(GIT_URL, null); + return h.expect(command) + .to.eventually.to.match(/HEAD/g); }); - it("should lsRemote get remote data @slow", function() { - return gitHelper.lsRemote('https://github.com/azukiapp/azk.git') - .then((result) => { - h.expect(result).to.match(/HEAD/g); + it('should clone, pull and checkout @slow', function() { + return h.tmp_dir().then((tmp_path) => { + const GIT_URL = 'https://github.com/azukiapp/azkdemo'; + const GIT_BRANCH_TAG_COMMIT = 'master'; + const DEST_FOLDER = tmp_path; + + return gitHelper.clone( + GIT_URL, + GIT_BRANCH_TAG_COMMIT, + DEST_FOLDER, + false, + null + ) + .then((result) => { + h.expect(result).to.match(/Cloning into/g); + }) + .then(() => { + return gitHelper.pull( + GIT_URL, + 'final', + DEST_FOLDER, + null + ); + }) + .then((result) => { + h.expect(result).to.match(/final/g); + }) + .then(() => { + return gitHelper.checkout( + '77284eb', + DEST_FOLDER, + null + ); + }) + .then((result) => { + h.expect(result).to.match(/77284eb/); + }); }); + }); }); diff --git a/src/manifest/get_project.js b/src/manifest/get_project.js index 868b0ba6..c7ab8f78 100644 --- a/src/manifest/get_project.js +++ b/src/manifest/get_project.js @@ -2,7 +2,7 @@ import { path, lazy_require, config, log, fsAsync } from 'azk'; import { async, promiseResolve, promiseReject } from 'azk/utils/promises'; import { UIProxy } from 'azk/cli/ui'; import { matchFirstRegex, matchAllRegex, fulltrim } from 'azk/utils/regex_helper'; -import { spawnAsync, printOutput } from 'azk/utils/spawn_helper'; +import { printOutput } from 'azk/utils/spawn_helper'; import { GitCallError } from 'azk/utils/errors'; var lazy = lazy_require({ @@ -18,7 +18,7 @@ export class GetProject extends UIProxy { this.is_new_git = null; this._gitHelper = lazy.git_helper; - this.gitOutput = (data) => printOutput( + this._gitOutput = (data) => printOutput( this.ok.bind(this), args.verbose_level, '[git]', @@ -102,7 +102,8 @@ export class GetProject extends UIProxy { var force_azk_start_url_endpoint = config('urls:force:endpoints:start'); this._sendForceAzkStart(command_parse_result, force_azk_start_url_endpoint); - yield this._checkGitVersion(command_parse_result.verbose_level); + var git_version = yield this._gitHelper.version(this._gitOutput); + this._checkGitVersion(git_version); var remoteInfo = yield this._getGitRemoteInfo( command_parse_result.git_url, @@ -129,14 +130,6 @@ export class GetProject extends UIProxy { git_destination_path : command_parse_result.git_destination_path, }); } - - // TODO: show menu options - // yield this._pullDestination( - // command_parse_result.git_url, - // command_parse_result.git_branch_tag_commit, - // command_parse_result.git_destination_path, - // command_parse_result.verbose_level); - } else { // clone to specific branch if (_isBranchOrTag && this.is_new_git) { @@ -189,22 +182,14 @@ export class GetProject extends UIProxy { }); } - _checkGitVersion() { + _checkGitVersion(git_version) { this.ok('commands.start.get_project.getting_git_version'); - - return this._gitHelper.version(this.gitOutput) - .then((git_version) => { - this.is_new_git = lazy.semver.gte(git_version, this.IS_NEW_GIT_VERSION_AFTER); - return git_version; - }) - .catch(this._checkGitError( - null, - null, - null)); + this.is_new_git = lazy.semver.gte(git_version, this.IS_NEW_GIT_VERSION_AFTER); + return git_version; } _getGitRemoteInfo(git_url) { - return this._gitHelper.lsRemote(git_url, this.gitOutput) + return this._gitHelper.lsRemote(git_url, this._gitOutput) .then((lsRemote_result) => { this.ok('commands.start.get_project.getting_remote_info', {git_url}); var parsed_result = this._parseGitLsRemoteResult(lsRemote_result); @@ -304,7 +289,7 @@ export class GetProject extends UIProxy { return filtered.length > 0; } - _checkDestinationFolder(git_destination_path/*, verbose_level*/) { + _checkDestinationFolder(git_destination_path) { this.ok('commands.start.get_project.checking_destination', { git_destination_path, }); @@ -312,21 +297,21 @@ export class GetProject extends UIProxy { return fsAsync.exists(git_destination_path); } - _pullDestination(git_url, git_branch_tag_commit, git_destination_path, verbose_level) { + _pullDestination(git_url, git_branch_tag_commit, git_destination_path) { this.ok('commands.start.get_project.git_pull', { git_url, git_branch_tag_commit, git_destination_path, }); - return this._gitspawn_PullAsync(git_url, - git_branch_tag_commit, - git_destination_path, - verbose_level) + return this._gitHelper.pull(git_url, + git_branch_tag_commit, + git_destination_path, + this._gitOutput) .catch(this._checkGitError(git_url, git_branch_tag_commit, git_destination_path)); } - _cloneToFolder(git_url, git_branch_tag_commit, git_destination_path, verbose_level) { + _cloneToFolder(git_url, git_branch_tag_commit, git_destination_path) { if (git_branch_tag_commit === 'master') { this.ok('commands.start.get_project.cloning_master_to_folder', { git_url, @@ -341,64 +326,23 @@ export class GetProject extends UIProxy { }); } - return this._gitspawn_CloneAsync(git_url, - git_branch_tag_commit, - git_destination_path, - verbose_level) + return this._gitHelper.clone(git_url, + git_branch_tag_commit, + git_destination_path, + this.is_new_git, + this._gitOutput) .catch(this._checkGitError(git_url, git_branch_tag_commit, git_destination_path)); } _checkoutToCommit(parsed_args) { this.ok('commands.start.get_project.checkout_to_commit', parsed_args); - return this._gitspawn_CheckoutInFolderAsync(parsed_args.git_url, - parsed_args.git_branch_tag_commit, - parsed_args.git_destination_path, - parsed_args.verbose_level) + return this._gitHelper.checkout(parsed_args.git_branch_tag_commit, + parsed_args.git_destination_path, + this._gitOutput) .catch(this._checkGitError( parsed_args.git_url, parsed_args.git_branch_tag_commit, parsed_args.git_destination_path)); } - - /********************** - gitspaw _Async calls * - **********************/ - _gitspawn_PullAsync(git_url, git_branch_tag_commit, dest_folder) { - - var git_params = [ - '--git-dir', - path.resolve(dest_folder, '.git'), - '--work-tree', - path.resolve(dest_folder), - 'pull', - git_url, - git_branch_tag_commit - ]; - - return spawnAsync('git', git_params, this.gitOutput); - } - - _gitspawn_CloneAsync(git_url, git_branch_tag_commit, dest_folder) { - - var git_params = [ - 'clone', - git_url, - dest_folder, - '--recursive' - ]; - - if (this.is_new_git) { - git_params.push('--single-branch'); - git_params.push('--branch'); - git_params.push(git_branch_tag_commit); - } - - return spawnAsync('git', git_params, this.gitOutput); - } - - _gitspawn_CheckoutInFolderAsync(git_url, git_branch_tag_commit, dest_folder) { - return spawnAsync('git', ['-C', dest_folder, 'checkout', git_branch_tag_commit], - this.gitOutput); - } } diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index d3e77360..02c596b4 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -1,5 +1,5 @@ -import { lazy_require } from 'azk'; -import { promiseResolve } from 'azk/utils/promises'; +import { lazy_require, path } from 'azk'; +import { promiseResolve, promiseReject } from 'azk/utils/promises'; import { groupFromRegex } from 'azk/utils/regex_helper'; // matchFirstRegex, matchAllRegex, fulltrim, @@ -11,8 +11,12 @@ var gitHelper = { version: (scanFunction) => { return lazy.spawnAsync('git', ['--version'], scanFunction) .then((result) => { - var git_version = groupFromRegex(result.message, /.*?(\d+\.\d+\.\d+)/, 1); - return promiseResolve(git_version); + if (result.error_code === 0) { + var git_version = groupFromRegex(result.message, /.*?(\d+\.\d+\.\d+)/, 1); + return promiseResolve(git_version); + } else { + return promiseReject(result.message); + } }); }, @@ -25,8 +29,12 @@ var gitHelper = { revision ], scanFunction) .then((result) => { - const commit_id = result.message.substring(0, 7); - return promiseResolve(commit_id); + if (result.error_code === 0) { + const commit_id = result.message.substring(0, 7); + return promiseResolve(commit_id); + } else { + return promiseReject(result.message); + } }); }, @@ -36,9 +44,70 @@ var gitHelper = { git_url, ], scanFunction) .then((result) => { - return promiseResolve(result.message); + if (result.error_code === 0) { + return promiseResolve(result.message); + } else { + return promiseReject(result.message); + } }); }, + + clone: (git_url, git_branch_tag_commit, dest_folder, is_new_git, scanFunction) => { + var git_params = [ + 'clone', + git_url, + dest_folder, + '--recursive' + ]; + + if (is_new_git) { + // git_params.push('--single-branch'); + git_params.push('--branch'); + git_params.push(git_branch_tag_commit); + } + + return lazy.spawnAsync('git', git_params, scanFunction) + .then((result) => { + if (result.error_code === 0) { + return promiseResolve(result.message); + } else { + return promiseReject(result.message); + } + }); + }, + + pull: (git_url, git_branch_tag_commit, dest_folder, scanFunction) => { + return lazy.spawnAsync('git', [ + '--git-dir', + path.resolve(dest_folder, '.git'), + '--work-tree', + path.resolve(dest_folder), + 'pull', + git_url, + git_branch_tag_commit + ], scanFunction) + .then((result) => { + if (result.error_code === 0) { + return promiseResolve(result.message); + } else { + return promiseReject(result.message); + } + }); + }, + + checkout: (git_branch_tag_commit, dest_folder, scanFunction) => { + return lazy.spawnAsync('git', + ['-C', dest_folder, 'checkout', git_branch_tag_commit], + scanFunction) + .then((result) => { + if (result.error_code === 0) { + return promiseResolve(result.message); + } else { + return promiseReject(result.message); + } + }); + }, + }; export default gitHelper; From 3d022930515eb50266fa0a558b1bd06a58e729bf Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Wed, 10 Feb 2016 18:28:16 -0200 Subject: [PATCH 10/61] [Cli] Version includes date --- spec/cmds/version_spec.js | 2 +- spec/index_spec.js | 12 ++++++++---- spec/utils/git_helper_spec.js | 6 ++++++ src/cmds/version.js | 14 +++++++++++--- src/config.js | 3 ++- src/index.js | 28 +++++++++++++++++++++++++--- src/utils/git_helper.js | 18 ++++++++++++++++++ 7 files changed, 71 insertions(+), 12 deletions(-) diff --git a/spec/cmds/version_spec.js b/spec/cmds/version_spec.js index 0176bd48..b16cc269 100644 --- a/spec/cmds/version_spec.js +++ b/spec/cmds/version_spec.js @@ -11,7 +11,7 @@ describe('Azk cli, version controller', function() { var doc_opts = { exit: false }; var run_options = { ui: ui }; - var version_regex = /azk version \d+\.\d+\.\d+, build \w+/; + var version_regex = /azk version \d+\.\d+\.\d+, build \w+, date \d+-\d+-\d+/; it('should run a version command', function() { doc_opts.argv = 'version'; diff --git a/spec/index_spec.js b/spec/index_spec.js index cf550fe1..33308af2 100644 --- a/spec/index_spec.js +++ b/spec/index_spec.js @@ -7,14 +7,18 @@ describe('azk main module', function() { h.expect(Azk.version).to.match(/\d+\.\d+\.\d+/); }); - it('should `gitCommitIdAsync` get current commit id from ENV', function() { + it('should commitId() get current commit id from ENV', function() { const last_commit_id = '123'; - return h.expect(Azk.gitCommitIdAsync(last_commit_id)) + return h.expect(Azk.commitId(last_commit_id)) .to.eventually.to.equal(last_commit_id); }); - it('should `gitCommitIdAsync` get current commit id from dev project', function() { - return h.expect(Azk.gitCommitIdAsync()).to.eventually.not.be.undefined; + it('should commitId() get current commit id from dev project', function() { + return h.expect(Azk.commitId()).to.eventually.not.be.undefined; + }); + + it('should commitDate get current commit date from dev project', function() { + return h.expect(Azk.commitDate()).to.eventually.not.be.undefined; }); }); diff --git a/spec/utils/git_helper_spec.js b/spec/utils/git_helper_spec.js index a7c18720..3c181f42 100644 --- a/spec/utils/git_helper_spec.js +++ b/spec/utils/git_helper_spec.js @@ -29,6 +29,12 @@ describe('Git Helper', function() { .to.eventually.to.not.be.undefined; }); + it('should run git show', function() { + const command = gitHelper.show('HEAD', '%ci', git_path, null); + return h.expect(command) + .to.eventually.to.match(/\d+-\d+-\d+.*/); + }); + it('should lsRemote get remote data @slow', function() { const GIT_URL = 'https://github.com/azukiapp/azkdemo'; const command = gitHelper.lsRemote(GIT_URL, null); diff --git a/src/cmds/version.js b/src/cmds/version.js index 4220b477..72417d85 100644 --- a/src/cmds/version.js +++ b/src/cmds/version.js @@ -8,11 +8,19 @@ export default class Version extends CliTrackerController { this.require_terms = false; } + // get version, commit id and commit date to create output index() { - const azk_last_commit = config('azk_last_commit'); - return Azk.gitCommitIdAsync(azk_last_commit) + let versionOutput = `azk version ${Azk.version}, build `; + const azk_last_commit_id = config('azk_last_commit_id'); + return Azk.commitId(azk_last_commit_id) .then((commitId) => { - this.ui.output(`azk version ${Azk.version}, build ${commitId}`); + versionOutput = versionOutput + commitId + ', date '; + const azk_last_commit_date = config('azk_last_commit_date'); + return Azk.commitDate(azk_last_commit_date); + }) + .then((commitDate) => { + versionOutput = versionOutput + commitDate; + this.ui.output(versionOutput); return 0; }); } diff --git a/src/config.js b/src/config.js index 7761dcc3..7b0d119f 100644 --- a/src/config.js +++ b/src/config.js @@ -52,7 +52,8 @@ var options = mergeConfig({ manifest : "Azkfile.js", locale : 'en-US', azk_dir : azk_dir, - azk_last_commit: envs("AZK_LAST_COMMIT"), + azk_last_commit_id: envs("AZK_LAST_COMMIT_ID"), + azk_last_commit_date: envs("AZK_LAST_COMMIT_DATE"), flags : { show_deprecate: envs('AZK_HIDE_DEPRECATE', false), require_accept_use_terms: envs('AZK_REQUIRE_TERMS', true), diff --git a/src/index.js b/src/index.js index 79c56afb..80d48154 100644 --- a/src/index.js +++ b/src/index.js @@ -11,12 +11,12 @@ class Azk { return require('package.json').version; } - static gitCommitIdAsync(azk_last_commit) { + static gitCommitIdAsync(azk_last_commit_id) { const path = GeralLib.path; const config = GeralLib.config; // git commit id from ENV - const commit_id = azk_last_commit; + const commit_id = azk_last_commit_id; if (commit_id) { return promiseResolve(commit_id); } @@ -27,6 +27,27 @@ class Azk { const gitHelper = require('azk/utils/git_helper'); return gitHelper.revParse('HEAD', git_path); } + + static gitCommitDateAsync(azk_last_commit_date) { + const path = GeralLib.path; + const config = GeralLib.config; + + // git commit id from ENV + const commit_date = azk_last_commit_date; + if (commit_date) { + return promiseResolve(commit_date); + } + + // git commit date from git_helper + const azkRootPath = config('paths:azk_root'); + const git_path = path.join(azkRootPath, '.git'); + const gitHelper = require('azk/utils/git_helper'); + return gitHelper.show('HEAD', '%ci', git_path, null) + .then((commit_date) => { + return commit_date.substring(0, 10); + }); + } + } // Default i18n method @@ -67,7 +88,8 @@ var GeralLib = { get fsAsync () { return require('file-async'); }, get utils () { return require('azk/utils'); }, get version () { return Azk.version; }, - get gitCommitIdAsync() { return Azk.gitCommitIdAsync; }, + get commitId () { return Azk.gitCommitIdAsync; }, + get commitDate () { return Azk.gitCommitDateAsync; }, get isBlank () { return isBlank; }, get lazy_require() { diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index 02c596b4..77fab6c9 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -38,6 +38,24 @@ var gitHelper = { }); }, + show: (revision, format, git_path, scanFunction) => { + return lazy.spawnAsync('git', [ + '--git-dir', + git_path, + 'show', + '-s', + '--format=' + format, + revision, + ], scanFunction) + .then((result) => { + if (result.error_code === 0) { + return promiseResolve(result.message); + } else { + return promiseReject(result.message); + } + }); + }, + lsRemote: (git_url, scanFunction) => { return lazy.spawnAsync('git', [ 'ls-remote', From e3118dde4a2b7b5a3c62cec1c221716c070475c5 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Wed, 10 Feb 2016 18:26:08 -0200 Subject: [PATCH 11/61] [Package] Adding '.package-envs' file into azk packages --- Makefile | 2 +- src/libexec/package-tools/mac/generate.sh | 1 + src/libexec/package-tools/pack.sh | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c1ed946e..9496580d 100644 --- a/Makefile +++ b/Makefile @@ -208,7 +208,7 @@ $(abspath $(2)/$(3)): $(abspath $(1)/$(3)) endef # copy regular files -FILES_FILTER = package.json bin shared .nvmrc CHANGELOG.md LICENSE README.md .dependencies +FILES_FILTER = package.json bin shared .nvmrc CHANGELOG.md LICENSE README.md .dependencies .package-envs FILES_ALL = $(shell cd ${AZK_ROOT_PATH} && find $(FILES_FILTER) -print 2>/dev/null | grep -v shared/completions) FILES_TARGETS = $(foreach file,$(addprefix $(PATH_USR_LIB_AZK)/, $(FILES_ALL)),$(abspath $(file))) $(foreach file,$(FILES_ALL),$(eval $(call COPY_FILES,$(AZK_ROOT_PATH),$(PATH_USR_LIB_AZK),$(file)))) diff --git a/src/libexec/package-tools/mac/generate.sh b/src/libexec/package-tools/mac/generate.sh index 0d3eac56..8321197c 100755 --- a/src/libexec/package-tools/mac/generate.sh +++ b/src/libexec/package-tools/mac/generate.sh @@ -56,6 +56,7 @@ class ${CLASS_NAME} < Formula prefix.install Dir['*'] prefix.install Dir['.nvmrc'] prefix.install Dir['.dependencies'] + prefix.install Dir['.package-envs'] end end diff --git a/src/libexec/package-tools/pack.sh b/src/libexec/package-tools/pack.sh index 1ed5e51e..4c267fc7 100755 --- a/src/libexec/package-tools/pack.sh +++ b/src/libexec/package-tools/pack.sh @@ -117,6 +117,15 @@ if [[ ! -z "${BUILD_DEB}" ]] || [[ ! -z "${BUILD_RPM}" ]]; then [[ ! -e $SECRET_KEY ]] && echo >&2 "Please inform an valid GPG key." && exit 3 fi +create_package_envs() { + AZK_LAST_COMMIT_ID=$(git rev-parse HEAD | cut -c 1-7) + AZK_LAST_COMMIT_DATE=$(git log -1 --format=%cd --date=short) + ( + echo "export AZK_LAST_COMMIT_ID=${AZK_LAST_COMMIT_ID}" + echo "export AZK_LAST_COMMIT_DATE=${AZK_LAST_COMMIT_DATE}" + ) > .package-envs +} + bump_version() { VERSION_NUMBER=$( cat package.json | grep -e "version" | cut -d' ' -f4 | sed -n 's/\"//p' | sed -n 's/\"//p' | sed -n 's/,//p' | sed s/-.*// ) @@ -235,6 +244,8 @@ source .dependencies LINUX_BUILD_WAS_EXECUTED=false +step_run "Creating .package-env file" --exit create_package_envs + step_run "Bumping version" --exit bump_version [[ $NO_MAKE != true ]] && step_run "Running make" --exit run_make From 6b7924a7ceda5ba0cc765f2fa31959827c5404c3 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Wed, 10 Feb 2016 19:53:42 -0200 Subject: [PATCH 12/61] Adding '.package-envs' into '.gitignore' --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b8353c97..1899d24f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /node_modules /build npm-debug.log +.package-envs From 97519f833346117b86ddaa5dae2015d752dae7f5 Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Wed, 10 Feb 2016 20:03:35 -0200 Subject: [PATCH 13/61] [Code] Import .package-envs if exist --- bin/azk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/azk b/bin/azk index ffe24c01..fee2f22c 100755 --- a/bin/azk +++ b/bin/azk @@ -49,6 +49,9 @@ export PATH=$AZK_NPM_PATH/.bin:$PATH # Load dependencies versions . ${AZK_ROOT_PATH}/.dependencies +if [ -e ${AZK_ROOT_PATH}/.package-envs ]; then + . ${AZK_ROOT_PATH}/.package-envs +fi export AZK_DOCKER_MIN_VERSION=${AZK_DOCKER_MIN_VERSION} export AZK_DOCKER_API_VERSION=${AZK_DOCKER_API_VERSION} export AZK_ISO_VERSION=${AZK_ISO_VERSION} From 039c0af728dc93b3c3018b073a62ed4e0a7fd2aa Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 11 Feb 2016 13:29:23 -0200 Subject: [PATCH 14/61] [Package] Fixing how version number is extracted from `azk version` --- Makefile | 4 ++-- shared/scripts/install.sh | 2 +- src/libexec/package-tools/arch/generate.sh | 2 +- src/libexec/package-tools/mac/generate.sh | 2 +- src/libexec/package-tools/mac/test.sh | 7 ++++--- src/libexec/package-tools/pack.sh | 6 +++--- src/libexec/package-tools/test.sh | 2 +- src/libexec/package-tools/ubuntu/generate.sh | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 9496580d..6e9dc62f 100644 --- a/Makefile +++ b/Makefile @@ -160,10 +160,10 @@ package_clean: @echo "task: $@" @rm -Rf ${AZK_PACKAGE_PREFIX}/..?* ${AZK_PACKAGE_PREFIX}/.[!.]* ${AZK_PACKAGE_PREFIX}/* -check_version: NEW_AZK_VERSION=$(shell ${PATH_USR_LIB_AZK}/bin/azk version) +check_version: NEW_AZK_VERSION=$(shell ${PATH_USR_LIB_AZK}/bin/azk version | sed -e 's/^azk version\ //; s/,.*//') check_version: @echo "task: $@" - @if [ ! "azk ${AZK_VERSION}" = "${NEW_AZK_VERSION}" ] ; then \ + @if [ ! "${AZK_VERSION}" = "${NEW_AZK_VERSION}" ] ; then \ echo 'Error to run: ${PATH_USR_LIB_AZK}/bin/azk version'; \ echo 'Expect: azk ${AZK_VERSION}'; \ echo 'Output: ${NEW_AZK_VERSION}'; \ diff --git a/shared/scripts/install.sh b/shared/scripts/install.sh index 2dc0251f..0f34f053 100755 --- a/shared/scripts/install.sh +++ b/shared/scripts/install.sh @@ -424,7 +424,7 @@ check_azk_installation() { azk_is_up_to_date() { AZK_TAGS_URL="https://api.github.com/repos/azukiapp/azk/tags" AZK_VERSIONS=$(curl -sSL ${AZK_TAGS_URL} | grep name) - AZK_CURRENT_VERSION=$(azk version | cut -d ' ' -f2) + AZK_CURRENT_VERSION=$(azk version | sed -e 's/^azk version\ //; s/,.*//') AZK_LATEST_VERSION=$( curl -sSL ${AZK_TAGS_URL} | \ grep name | \ head -1 | \ diff --git a/src/libexec/package-tools/arch/generate.sh b/src/libexec/package-tools/arch/generate.sh index ce596d44..74c601a9 100755 --- a/src/libexec/package-tools/arch/generate.sh +++ b/src/libexec/package-tools/arch/generate.sh @@ -21,7 +21,7 @@ abs_dir() { export AZK_ROOT_PATH=`cd \`abs_dir ${BASH_SOURCE:-$0}\`/../../..; pwd` export PATH=${AZK_ROOT_PATH}/bin:$PATH -export VERSION=${1:-$( azk version | awk '{ print $2 }' )} +export VERSION=${1:-$( azk version | sed -e 's/^azk version\ //; s/,.*//' )} export AUR_REPO_DIR=${2:-"/tmp/aur-azk"} RELEASE_CHANNEL=$( echo "${VERSION}" | sed s/[^\\-]*// | sed s/^\\-// | sed s/\\..*// ) diff --git a/src/libexec/package-tools/mac/generate.sh b/src/libexec/package-tools/mac/generate.sh index 8321197c..e3b7e80f 100755 --- a/src/libexec/package-tools/mac/generate.sh +++ b/src/libexec/package-tools/mac/generate.sh @@ -12,7 +12,7 @@ if [[ -z "${MAC_REPO_STAGE_BRANCH}" ]]; then exit 2 fi -export VERSION=$( azk version | awk '{ print $2 }' ) +export VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) SHA256=$(shasum -a 256 shasum -a 256 "package/brew/azk_${VERSION}.tar.gz" | awk '{print $1}') diff --git a/src/libexec/package-tools/mac/test.sh b/src/libexec/package-tools/mac/test.sh index e0392c54..78c4a48d 100755 --- a/src/libexec/package-tools/mac/test.sh +++ b/src/libexec/package-tools/mac/test.sh @@ -2,7 +2,7 @@ set -x -export VERSION=$( azk version | awk '{ print $2 }' ) +export VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) BASE_DIR=$( pwd ) SHA256=$(shasum -a 256 shasum -a 256 "package/brew/azk_${VERSION}.tar.gz" | awk '{print $1}') @@ -77,7 +77,8 @@ brew install azukiapp/azk/azk${CHANNEL_SUFFIX} cd $FORMULA_DIR git checkout $FORMULA_FILE -if [[ "$( bazk version )" == "azk ${VERSION}" ]]; then +BAZK_VERSION=$(bazk version | sed -e 's/^azk version\ //; s/,.*//') +if [[ "${BAZK_VERSION}" == "${VERSION}" ]]; then echo "azk ${VERSION} has been successfully installed." else echo "Failed to install azk ${VERSION}." @@ -93,4 +94,4 @@ if [[ ! -z "${TEST_DIR}" ]]; then ) fi -exit 0 \ No newline at end of file +exit 0 diff --git a/src/libexec/package-tools/pack.sh b/src/libexec/package-tools/pack.sh index 4c267fc7..18a7a0ab 100755 --- a/src/libexec/package-tools/pack.sh +++ b/src/libexec/package-tools/pack.sh @@ -279,7 +279,7 @@ if [[ $BUILD_DEB == true ]]; then EXTRA_FLAGS="LINUX_CLEAN=" fi - step_run "Creating deb packages" make package_deb ${EXTRA_FLAGS} + step_run "Creating deb packages" --exit make package_deb ${EXTRA_FLAGS} UBUNTU_VERSIONS=( "ubuntu12:precise" "ubuntu14:trusty" "ubuntu15:wily" ) for UBUNTU_VERSION in "${UBUNTU_VERSIONS[@]}"; do @@ -325,7 +325,7 @@ if [[ $BUILD_RPM == true ]]; then EXTRA_FLAGS="LINUX_CLEAN=" fi - step_run "Creating rpm packages" make package_rpm ${EXTRA_FLAGS} + step_run "Creating rpm packages" --exit make package_rpm ${EXTRA_FLAGS} FEDORA_VERSIONS=( "fedora20" "fedora23" ) for FEDORA_VERSION in "${FEDORA_VERSIONS[@]}"; do @@ -355,7 +355,7 @@ if [[ $BUILD_MAC == true ]]; then ( set -e step_run "Cleaning environment" rm -Rf package/brew - step_run "Creating Mac packages" make package_mac + step_run "Creating Mac packages" --exit make package_mac step_run "Generating Mac repository" ${AZK_BUILD_TOOLS_PATH}/mac/generate.sh if [[ $NO_TEST != true ]]; then step_run "Testing Mac repository" ${AZK_BUILD_TOOLS_PATH}/mac/test.sh $TEST_ARGS diff --git a/src/libexec/package-tools/test.sh b/src/libexec/package-tools/test.sh index dafa402f..2ec43991 100755 --- a/src/libexec/package-tools/test.sh +++ b/src/libexec/package-tools/test.sh @@ -15,7 +15,7 @@ set -e BASE_DIR=$( echo $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) | sed s#$(pwd)/##g ) -export VERSION=$( bin/azk version | awk '{ print $2 }' ) +export VERSION=$( bin/azk version | sed -e 's/^azk version\ //; s/,.*//' ) export SO=$1 if [[ $# == 2 ]]; then diff --git a/src/libexec/package-tools/ubuntu/generate.sh b/src/libexec/package-tools/ubuntu/generate.sh index eff0069c..8b6be755 100755 --- a/src/libexec/package-tools/ubuntu/generate.sh +++ b/src/libexec/package-tools/ubuntu/generate.sh @@ -28,7 +28,7 @@ set -e export PATH=`pwd`/bin:$PATH DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -export VERSION=$( azk version | awk '{ print $2 }' ) +export VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) export SECRET_KEY=$1 export LIBNSS_RESOLVER_VERSION=$2 export DISTRO=$3 && export REPO=azk-${DISTRO} From f634899f26e63f1133705d8e9b77f1b33c61bf4d Mon Sep 17 00:00:00 2001 From: Lucas Macedo Date: Thu, 18 Feb 2016 16:04:37 -0200 Subject: [PATCH 15/61] update docs wordpress --- .../common/azkfilejs/php_wordpress_mysql.md | 18 +++++++++++++----- .../php-wordpress-phpmyadmin.md | 11 ++++++++++- .../php-wordpress-phpmyadmin.md | 10 ++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/content/common/azkfilejs/php_wordpress_mysql.md b/docs/content/common/azkfilejs/php_wordpress_mysql.md index 167536cf..f887ba42 100644 --- a/docs/content/common/azkfilejs/php_wordpress_mysql.md +++ b/docs/content/common/azkfilejs/php_wordpress_mysql.md @@ -1,7 +1,9 @@ ```js systems({ - "wordpress": { - depends: ["mysql"], + wordpress: { + // Dependent systems + depends: ['mysql'], + // More images: http://images.azk.io image: {"docker": "azukiapp/php-fpm"}, workdir: "/azk/#{manifest.dir}", shell: "/bin/bash", @@ -14,9 +16,13 @@ systems({ domains: [ "#{system.name}.#{azk.default_domain}" ] }, ports: { + // exports global variables http: "80/tcp", }, envs: { + // Make sure that the PORT value is the same as the one + // in ports/http below, and that it's also the same + // if you're setting it in a .env file APP_DIR: "/azk/#{manifest.dir}", }, }, @@ -49,18 +55,20 @@ systems({ MYSQL_DATABASE: "#{envs.MYSQL_DATABASE}" }, }, - "phpmyadmin": { + phpmyadmin: { + // Dependent systems depends: ["mysql"], + // More images: http://images.azk.io image: { docker: "reduto/phpmyadmin" }, wait: {retry: 20, timeout: 1000}, - scalable: {default: 0, limit: 1}, + scalable: {default: 1, limit: 1}, http: { domains: [ "#{system.name}.#{azk.default_domain}" ] }, ports: { http: "80/tcp", }, - }, + } }); // Sets a default system (to use: start, stop, status, scale) diff --git a/docs/content/en/starting-from-scratch/php-wordpress-phpmyadmin.md b/docs/content/en/starting-from-scratch/php-wordpress-phpmyadmin.md index 578583df..ceec2ac1 100644 --- a/docs/content/en/starting-from-scratch/php-wordpress-phpmyadmin.md +++ b/docs/content/en/starting-from-scratch/php-wordpress-phpmyadmin.md @@ -14,7 +14,16 @@ To start the development environment ```sh $ azk start -o && azk logs --follow ``` - +### Config the wordpress +Rename the file wp-config-sample.php to wp-config.php +And update the Database connection for: +```sh +define('DB_NAME', getenv('MYSQL_DATABASE')); +define('DB_USER', getenv('MYSQL_USER')); +define('DB_PASSWORD', getenv('MYSQL_PASSWORD')); +define('DB_HOST', getenv('MYSQL_HOST') . ':' . getenv('MYSQL_PORT')); +``` +Update your Azkfile.js according to the example below. ### Examples #### PHP-FPM + MySQL + phpMyAdmin diff --git a/docs/content/pt-BR/starting-from-scratch/php-wordpress-phpmyadmin.md b/docs/content/pt-BR/starting-from-scratch/php-wordpress-phpmyadmin.md index 430b346b..ddf1e066 100644 --- a/docs/content/pt-BR/starting-from-scratch/php-wordpress-phpmyadmin.md +++ b/docs/content/pt-BR/starting-from-scratch/php-wordpress-phpmyadmin.md @@ -13,7 +13,17 @@ Para iniciar o ambiente de desenvolvimento ```sh $ azk start -o && azk logs --follow + +### Configurando o Wordpress +Renomeie o arquivo wp-config-sample.php para wp-config.php +E atualize os dados de conexão com o banco para: +```sh +define('DB_NAME', getenv('MYSQL_DATABASE')); +define('DB_USER', getenv('MYSQL_USER')); +define('DB_PASSWORD', getenv('MYSQL_PASSWORD')); +define('DB_HOST', getenv('MYSQL_HOST') . ':' . getenv('MYSQL_PORT')); ``` +Atualize seu Azkfile.js de acordo com o exemplo abaixo. ### Exemplos From ee758160fb8f6b911dbc6e362950c6b401f9a887 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Fri, 19 Feb 2016 00:49:57 -0200 Subject: [PATCH 16/61] [Core] Updating copyright --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 938f9131..176cfceb 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Share the love and star us here in Github! ## License -"Azuki", "azk" and the Azuki logo are copyright (c) 2013-2015 Azuki Serviços de Internet LTDA. +"Azuki", "azk" and the Azuki logo are copyright (c) 2013-2016 Azuki Serviços de Internet LTDA. **azk** source code is released under Apache 2 License. From 5cf55abc6b17074fed08b086dfa73462179eec63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Wed, 2 Mar 2016 00:20:02 -0300 Subject: [PATCH 17/61] [utils] Refactoring git helper to reduce code duplication --- src/utils/git_helper.js | 137 +++++++++++++--------------------------- 1 file changed, 45 insertions(+), 92 deletions(-) diff --git a/src/utils/git_helper.js b/src/utils/git_helper.js index 77fab6c9..fa555a69 100644 --- a/src/utils/git_helper.js +++ b/src/utils/git_helper.js @@ -1,101 +1,51 @@ import { lazy_require, path } from 'azk'; import { promiseResolve, promiseReject } from 'azk/utils/promises'; import { groupFromRegex } from 'azk/utils/regex_helper'; -// matchFirstRegex, matchAllRegex, fulltrim, var lazy = lazy_require({ spawnAsync : ['azk/utils/spawn_helper'], }); var gitHelper = { - version: (scanFunction) => { - return lazy.spawnAsync('git', ['--version'], scanFunction) - .then((result) => { - if (result.error_code === 0) { - var git_version = groupFromRegex(result.message, /.*?(\d+\.\d+\.\d+)/, 1); - return promiseResolve(git_version); - } else { - return promiseReject(result.message); - } - }); + version(scanFunction) { + let git_params = ['--version']; + let format = (result) => groupFromRegex(result.message, /.*?(\d+\.\d+\.\d+)/, 1); + return this._spawn_async(git_params, scanFunction, format); }, - revParse: (revision, git_path, scanFunction) => { - return lazy.spawnAsync('git', [ - '--git-dir', - git_path, - 'rev-parse', - '--verify', - revision - ], scanFunction) - .then((result) => { - if (result.error_code === 0) { - const commit_id = result.message.substring(0, 7); - return promiseResolve(commit_id); - } else { - return promiseReject(result.message); - } - }); + revParse(revision, git_path, scanFunction) { + let git_params = [ + '--git-dir', git_path, 'rev-parse', '--verify', revision + ]; + let format = (result) => result.message.substring(0, 7); + return this._spawn_async(git_params, scanFunction, format); }, - show: (revision, format, git_path, scanFunction) => { - return lazy.spawnAsync('git', [ - '--git-dir', - git_path, - 'show', - '-s', - '--format=' + format, - revision, - ], scanFunction) - .then((result) => { - if (result.error_code === 0) { - return promiseResolve(result.message); - } else { - return promiseReject(result.message); - } - }); + show(revision, format, git_path, scanFunction) { + let git_params = [ + '--git-dir', git_path, 'show', '-s', '--format=' + format, revision + ]; + return this._spawn_async(git_params, scanFunction); }, - lsRemote: (git_url, scanFunction) => { - return lazy.spawnAsync('git', [ - 'ls-remote', - git_url, - ], scanFunction) - .then((result) => { - if (result.error_code === 0) { - return promiseResolve(result.message); - } else { - return promiseReject(result.message); - } - }); + lsRemote(git_url, scanFunction) { + let git_params = [ 'ls-remote', git_url ]; + return this._spawn_async(git_params, scanFunction); }, - clone: (git_url, git_branch_tag_commit, dest_folder, is_new_git, scanFunction) => { - var git_params = [ - 'clone', - git_url, - dest_folder, - '--recursive' - ]; + clone(git_url, git_branch_tag_commit, dest_folder, is_new_git, scanFunction) { + var git_params = [ 'clone', git_url, dest_folder, '--recursive' ]; if (is_new_git) { - // git_params.push('--single-branch'); git_params.push('--branch'); git_params.push(git_branch_tag_commit); } - return lazy.spawnAsync('git', git_params, scanFunction) - .then((result) => { - if (result.error_code === 0) { - return promiseResolve(result.message); - } else { - return promiseReject(result.message); - } - }); + return this._spawn_async(git_params, scanFunction); }, - pull: (git_url, git_branch_tag_commit, dest_folder, scanFunction) => { - return lazy.spawnAsync('git', [ + pull(git_url, git_branch_tag_commit, dest_folder, scanFunction) { + let git_params = [ '--git-dir', path.resolve(dest_folder, '.git'), '--work-tree', @@ -103,27 +53,30 @@ var gitHelper = { 'pull', git_url, git_branch_tag_commit - ], scanFunction) - .then((result) => { - if (result.error_code === 0) { - return promiseResolve(result.message); - } else { - return promiseReject(result.message); - } - }); + ]; + + return this._spawn_async(git_params, scanFunction); + }, + + checkout(git_branch_tag_commit, dest_folder, scanFunction) { + let git_params = ['-C', dest_folder, 'checkout', git_branch_tag_commit]; + return this._spawn_async(git_params, scanFunction); }, - checkout: (git_branch_tag_commit, dest_folder, scanFunction) => { - return lazy.spawnAsync('git', - ['-C', dest_folder, 'checkout', git_branch_tag_commit], - scanFunction) - .then((result) => { - if (result.error_code === 0) { - return promiseResolve(result.message); - } else { - return promiseReject(result.message); - } - }); + _spawn_async(git_params, scanFunction, format = null) { + if (!format) { + format = (result) => result.message; + } + + return lazy + .spawnAsync('git', git_params, scanFunction) + .then((result) => { + if (result.error_code === 0) { + return promiseResolve(format(result)); + } else { + return promiseReject(result.message); + } + }); }, }; From 978f5b615b34a64bb72b2a4affb85ffca4cc407c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Wed, 2 Mar 2016 00:23:38 -0300 Subject: [PATCH 18/61] [cli] Updating CHANGELOG about the new `azk version` format --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 441842e9..149a4006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## dev * Enhancements + * [Cli] `azk version` changes to display the first 7 digits of the commit hash and the date of the corresponding commite version; * [Cli] Updating `i18n-cli`: now, it supports color syntax highlight; * [Cli] Replacing `colors` for `chalk` and adding `--no-color` option that outputs in only one color; * [Cli] Improving `ui.isInteractive()`: now, not only the existence of a `tty` is checked but also the parameter `--quiet`; From ead1bdcfb9d81c39ca49ab99c54b8ae9e309c546 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Wed, 2 Mar 2016 02:52:47 -0300 Subject: [PATCH 19/61] [Package] Fixing packing process to suit to the new `azk version` output --- Azkfile.js | 1 + src/libexec/package-tools/pack.sh | 5 ++--- src/libexec/package-tools/test-container.sh | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Azkfile.js b/Azkfile.js index b44c4808..34748c3b 100644 --- a/Azkfile.js +++ b/Azkfile.js @@ -6,6 +6,7 @@ var mounts = { "/azk/#{manifest.dir}": sync('./', { shell: true }), "/azk/#{manifest.dir}/package": path("./package"), "/azk/#{manifest.dir}/node_modules": persistent('node_modules-#{system.name}'), + "/azk/#{manifest.dir}/.package-envs": path("./.package-envs"), "/azk/demos" : path("../demos"), "/azk/build" : persistent('build-#{system.name}'), "/azk/lib" : persistent('lib-#{system.name}'), diff --git a/src/libexec/package-tools/pack.sh b/src/libexec/package-tools/pack.sh index 287db46e..6c009e15 100755 --- a/src/libexec/package-tools/pack.sh +++ b/src/libexec/package-tools/pack.sh @@ -263,9 +263,8 @@ source .dependencies LINUX_BUILD_WAS_EXECUTED=false calculate_azk_version -step_run "Creating .package-env file" --exit create_package_envs -step_run "Bumping version" --exit bump_version +step_run "Creating .package-env file" --exit create_package_envs [[ $NO_VERSION != true ]] && step_run "Bumping version" --exit bump_version [[ $NO_MAKE != true ]] && step_run "Running make" --exit run_make [[ $NO_AGENT != true ]] && step_run "Starting agent" --exit start_agent @@ -301,7 +300,7 @@ if [[ $BUILD_DEB == true ]]; then fi step_run "Creating deb packages" --exit \ - rm -rf /azk/aptly/public/pool/main/a/azk${PACKAGE_SUFFIX}/azk${PACKAGE_SUFFIX}_${VERSION}_amd64.deb \ + azk shell package -- rm -rf /azk/aptly/public/pool/main/a/azk${PACKAGE_SUFFIX}/azk${PACKAGE_SUFFIX}_${VERSION_NUMBER}*_amd64.deb \ && make package_deb ${EXTRA_FLAGS} UBUNTU_VERSIONS=( "ubuntu12:precise" "ubuntu14:trusty" "ubuntu15:wily" ) diff --git a/src/libexec/package-tools/test-container.sh b/src/libexec/package-tools/test-container.sh index 46fbf6aa..add53409 100755 --- a/src/libexec/package-tools/test-container.sh +++ b/src/libexec/package-tools/test-container.sh @@ -69,11 +69,11 @@ setup() { run_test() { set -e - DETECTED_VERSION=$( azk --version ) + DETECTED_VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) - if [[ "${DETECTED_VERSION}" != "azk ${VERSION}" ]]; then + if [[ "${DETECTED_VERSION}" != "${VERSION}" ]]; then echo "Version check failed." - echo "Detected: ${DETECTED_VERSION}" + echo "Detected: azk ${DETECTED_VERSION}" echo "Expected: azk ${VERSION}" fail 4 else From 2042214c7a12774a4de64ce9372ea6846ccf0baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slobodan=20Mi=C5=A1kovi=C4=87?= Date: Sun, 6 Mar 2016 00:53:10 -0800 Subject: [PATCH 20/61] Configurable VM swap factor via AZK_VM_SWAP_FACTOR --- src/agent/server.js | 1 + src/config.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/agent/server.js b/src/agent/server.js index 86938393..165b63ed 100644 --- a/src/agent/server.js +++ b/src/agent/server.js @@ -95,6 +95,7 @@ var Server = { var file = config("agent:vm:ssh_key") + ".pub"; var content = yield fsAsync.readFile(file); VM.setProperty(vm_name, "/VirtualBox/D2D/SSH_KEY", content.toString()); + VM.setProperty(vm_name, "/VirtualBox/D2D/swap_factor", config("agent:vm:swap_factor")); } if (!running && start) { diff --git a/src/config.js b/src/config.js index 46bd5527..302889c9 100644 --- a/src/config.js +++ b/src/config.js @@ -154,6 +154,7 @@ var options = mergeConfig({ blank_disk : path.join(envs('AZK_LIB_PATH'), "vm", envs('AZK_ISO_VERSION'), "azk-agent.vmdk.gz"), mount_point : '/media/sf_Root', authorized_key: '/home/docker/.ssh/authorized_keys', + swap_factor : envs('AZK_VM_SWAP_FACTOR', 1.5), }, // Used to carry global configuration switches the agent From 1800b8d3621db78a7f8ac64490df279cec8d354d Mon Sep 17 00:00:00 2001 From: Marcus Gadbem Date: Tue, 23 Feb 2016 13:41:20 -0300 Subject: [PATCH 21/61] Initial commit: setup folder and files --- .github/ISSUE_TEMPLATE.md | 0 .github/PULL_REQUEST_TEMPLATE.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..e69de29b diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..e69de29b From 5a4c6d79b0d9be573a109b2dbadcd226166f9664 Mon Sep 17 00:00:00 2001 From: Marcus Gadbem Date: Tue, 23 Feb 2016 13:42:47 -0300 Subject: [PATCH 22/61] Moves CONTRIBUTING template do .github/ folder --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md From 7fc5e52079989914e09c8f0f33f37ebc5e7b890a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Thu, 31 Mar 2016 16:55:50 -0300 Subject: [PATCH 23/61] [Code] Adding github template content and updating CHANGELOG --- .github/ISSUE_TEMPLATE.md | 35 ++++++++++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 40 ++++++++++++++++++++++++++++++++ CHANGELOG.md | 5 ++++ docs/Azkfile.js | 2 +- 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e69de29b..29dd912c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,35 @@ +---- Instructions: please remove it before posting ---- + +Hi. We've set up a few optional questions to help you to structure your issue. +It would also help us to assist you more quickly and efficiently. + +We strongly recommend you to write your message in English for 2 main reasons: + +1. Your issue can also be others' problems; +2. Makes it easier for our community users to help each others out. + +--------------------------------------------------------- + +1. What are you trying do accomplish? + +[Describe in a few words what you are trying to do] + +2. Steps to reproduce the described behavior + +[Give us enough data to reproduce this scenario] + +3. Output + +``` +[The error message you are facing] +``` + +4. Further info + +a. Which OS / distro? +b. Uses VirtualBox? Which version? +c. Can you provide us your Azkfile.js? + +5. Do you have any suggestions on how to tackle this? + +[Optional] diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e69de29b..b8d94684 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,40 @@ +---- Instructions: please remove it before posting ---- + +Hi. We've set up a few optional questions to help you to structure your PR. +It would also help us to assist you more quickly and efficiently. + +We strongly recommend you to write your message in English for 2 main reasons: + +1. Your improvement can fit others' needs; +2. Makes it easier for our community users to help each others out; + +There are two types of templates you can use: +--------------------------------------------------------- + +A. If it's a work in progress + +1. What is this PR objective? + +[What is this solving?] + +2. Tasklist + +- [ ] Task one +- [ ] Task two +- [ ] ... + +--------------------------------------------------------- + +B. If it's a finished PR + +1. What is this PR objective? + +[What is this solving or implementing?] + +2. How did you tackle the issue? + +[Write in details your decisions/perceptions and how you accomplished this] + +3. How to test it? + +[How can we test?] diff --git a/CHANGELOG.md b/CHANGELOG.md index fce71a32..eaf46688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## dev + +* Enhancements + * [Code] Adding github issue and PR templates; + ## v0.17.0 - (2016-02-19) * Enhancements diff --git a/docs/Azkfile.js b/docs/Azkfile.js index ce8482e8..b733439d 100644 --- a/docs/Azkfile.js +++ b/docs/Azkfile.js @@ -18,7 +18,7 @@ systems({ wait: {"retry": 20, "timeout": 2000}, mounts: { '/azk/#{manifest.dir}': path("."), - '/azk/CONTRIBUTING.md': path("../CONTRIBUTING.md"), + '/azk/CONTRIBUTING.md': path("../.github/CONTRIBUTING.md"), '/azk/node_modules': persistent("node_modules"), }, scalable: {"default": 1}, From db8be2e497761f65753c998461c1d5328607c4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Thu, 31 Mar 2016 17:12:07 -0300 Subject: [PATCH 24/61] [Docs] Updating changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf46688..35ee2edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Enhancements * [Code] Adding github issue and PR templates; + * [Docs] The instructions for use `azk` and `Wordpress` have been improved; ## v0.17.0 - (2016-02-19) From 75b87542922758cfe735c85b13efc6fd0860a1b6 Mon Sep 17 00:00:00 2001 From: Gullit Miranda Date: Tue, 9 Feb 2016 00:50:45 -0200 Subject: [PATCH 25/61] [utils] Adding `Versions` module to utils --- spec/utils/versions_spec.js | 55 +++++++++++++++++++++++++++++++++++ src/utils/index.js | 9 +++--- src/utils/versions.js | 58 +++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 spec/utils/versions_spec.js create mode 100644 src/utils/versions.js diff --git a/spec/utils/versions_spec.js b/spec/utils/versions_spec.js new file mode 100644 index 00000000..ce46dc25 --- /dev/null +++ b/spec/utils/versions_spec.js @@ -0,0 +1,55 @@ +import h from 'spec/spec_helper'; +import { Versions } from 'azk/utils'; + +describe("Azk Utils Versions", function() { + describe('parse version,', function () { + it("should error not regexp", function() { + var func = () => Versions.parse(null); + h.expect(func).to.throw(Error, /Cannot call method \'match\' of null/); + }); + + it("should invalid version from empty string", function() { + var result = Versions.parse(''); + h.expect(result).to.be.null; + }); + + it("should one version by special chars", function() { + h.expect(Versions.parse('~> 1.0' )).to.eql('1.0.0'); + h.expect(Versions.parse('~> 1.2.9' )).to.eql('1.2.9'); + h.expect(Versions.parse('~> 1.3-beta')).to.eql('1.3.0'); + }); + + it("should multiple versions by context", function() { + h.expect(Versions.parse('1.0 1.2' )).to.eql('1.0.0'); + h.expect(Versions.parse('1.0.1 or 1.2.3' )).to.eql('1.0.1'); + h.expect(Versions.parse('1.0.4 or 1.2-beta')).to.eql('1.0.4'); + }); + }); + + describe('match version,', function () { + // https://regex101.com/r/jQ6eE8/3 + var regex = /elixir[\s]*\:[\s]*\"(?:[~> ]*)?([0-9.]+)(?:(?:[or ]*)?([0-9.]+))?(?:.*)?\".*/gm; + + it("should error not regexp", function() { + var func = () => Versions.match(null, ''); + h.expect(func).to.throw(Error, /regex .* is not instance of \`RegExp\`/); + }); + + it("should invalid version from empty string", function() { + var result = Versions.match(regex, ''); + h.expect(result).to.not.be.undefined; + }); + + it("should one version by context", function() { + h.expect(Versions.match(regex, 'elixir: "~> 1.0",' )).to.eql(['1.0.0']); + h.expect(Versions.match(regex, 'elixir: "~> 1.2.9",' )).to.eql(['1.2.9']); + h.expect(Versions.match(regex, 'elixir: "~> 1.3-beta",')).to.eql(['1.3.0']); + }); + + it("should multiple versions by context", function() { + h.expect(Versions.match(regex, 'elixir: "~> 1.0 or 1.2",' )).to.eql(['1.0.0', '1.2.0']); + h.expect(Versions.match(regex, 'elixir: "~> 1.0.1 or 1.2.3",' )).to.eql(['1.0.1', '1.2.3']); + h.expect(Versions.match(regex, 'elixir: "~> 1.0.4 or 1.2-beta",')).to.eql(['1.0.4', '1.2.0']); + }); + }); +}); diff --git a/src/utils/index.js b/src/utils/index.js index 1dd35d7f..c4a70480 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -5,10 +5,11 @@ var fs = require('fs'); var defer = require('azk/utils/promises').defer; var Utils = { - get default() { return Utils; }, - get _ () { return _; }, - get net () { return require('azk/utils/net'); }, - get docker () { return require('azk/utils/docker'); }, + get default () { return Utils; }, + get _ () { return _; }, + get net () { return require('azk/utils/net'); }, + get docker () { return require('azk/utils/docker'); }, + get Versions() { return require('azk/utils/versions'); }, /** * `lazy_require` can postpone loading of external dependencies. diff --git a/src/utils/versions.js b/src/utils/versions.js new file mode 100644 index 00000000..f9abb89c --- /dev/null +++ b/src/utils/versions.js @@ -0,0 +1,58 @@ +import { lazy_require } from 'azk'; +var lazy = lazy_require({ + semver: 'semver', +}); + +// var last = require('lodash/array/last'); + +/** + * Versions: Abstraction above the [semver](https://www.npmjs.com/package/semver). + */ + +var Versions = { + /** + * compare version by regex + * @param {Regex} regex Regular expression to compare with content; + * @param {String} content String with version to parse; + * @return {Array} collection of versions; + */ + match(regex, content) { + if (!(regex instanceof RegExp)) { + throw new Error(`regex ${regex} is not instance of \`RegExp\``); + } + // fix cached regex + regex = new RegExp(regex); + var match = regex.exec(content) || []; + match = match.slice(1, match.length) + var versions = []; + match.forEach((version) =>{ + if (version) { + version = this.parse(version); + versions.push(version); + } + }); + return versions; + }, + + /** + * parse version from string + * @param {String} context context contents version + * @return {String|Null} parsed version + */ + parse(context, depth=3) { + var match = context.match(/([0-9.]+)/gm); + var version = (match && match[0]); + if (version) { + // force valid format: eg: 1.0 => 1.0.0 (with depth=3) + var splited = version.split('.'); + for(var i = 0; i < depth; i++) { + splited[i] = splited[i] || '0'; + } + version = splited.join('.'); + version = lazy.semver.clean(version); + } + return version; + } +}; + +export default Versions; From 50f303bb75d71308a7db3545c443468258c5e0bd Mon Sep 17 00:00:00 2001 From: Gullit Miranda Date: Tue, 9 Feb 2016 01:39:10 -0200 Subject: [PATCH 26/61] [generators] Adding elixir v1.2 and refactor elixir base --- spec/generator/rules/elixir_spec.js | 79 ++++++++++++------- src/generator/rules/elixir.js | 27 +++---- .../suggestions/{elixir.js => elixir-1.0.js} | 14 ++-- src/generator/suggestions/elixir-1.1.js | 21 +++++ src/generator/suggestions/elixir-1.2.js | 21 +++++ src/generator/suggestions/elixir_phoenix.js | 2 +- src/utils/versions.js | 2 - 7 files changed, 112 insertions(+), 54 deletions(-) rename src/generator/suggestions/{elixir.js => elixir-1.0.js} (82%) create mode 100644 src/generator/suggestions/elixir-1.1.js create mode 100644 src/generator/suggestions/elixir-1.2.js diff --git a/spec/generator/rules/elixir_spec.js b/spec/generator/rules/elixir_spec.js index 27c5b09e..6d93ab35 100644 --- a/spec/generator/rules/elixir_spec.js +++ b/spec/generator/rules/elixir_spec.js @@ -1,6 +1,7 @@ import h from 'spec/spec_helper'; import { Rule } from 'azk/generator/rules/elixir'; import { path, fs } from 'azk'; +var last = require('lodash/array/last'); describe('Azk generators, Elixir rule', function() { var outputs = []; @@ -27,40 +28,64 @@ describe('Azk generators, Elixir rule', function() { h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); h.expect(evidence).to.have.deep.property('name' , 'elixir'); - h.expect(evidence).to.have.deep.property('ruleName', 'elixir'); + h.expect(evidence).to.have.deep.property('ruleName', 'elixir-1.0'); h.expect(evidence).to.have.deep.property('version' , '1.0.0'); }); - it('should version 1.0.0 when set 1', () => { - content = content.replace(regex, 'elixir: "~> 1",'); - var evidence = rule.getEvidence(mixfilePath, content); + describe('should version is', function () { + it('1.0.0 from 1', () => { + content = content.replace(regex, 'elixir: "~> 1",'); + var evidence = rule.getEvidence(mixfilePath, content); - h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); - h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); - h.expect(evidence).to.have.deep.property('name' , 'elixir'); - h.expect(evidence).to.have.deep.property('ruleName', 'elixir'); - h.expect(evidence).to.have.deep.property('version' , '1.0.0'); - }); + h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); + h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); + h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('ruleName', 'elixir-1.0'); + h.expect(evidence).to.have.deep.property('version' , '1.0.0'); + }); - it('should version 0.8.0 when set 0.8', () => { - content = content.replace(regex, 'elixir: "~> 0.8",'); - var evidence = rule.getEvidence(mixfilePath, content); + it('0.8.0 from 0.8', () => { + content = content.replace(regex, 'elixir: "~> 0.8",'); + var evidence = rule.getEvidence(mixfilePath, content); - h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); - h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); - h.expect(evidence).to.have.deep.property('name' , 'elixir'); - h.expect(evidence).to.have.deep.property('ruleName', 'elixir'); - h.expect(evidence).to.have.deep.property('version' , '0.8.0'); - }); + h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); + h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); + h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('ruleName', 'elixir-1.0'); + h.expect(evidence).to.have.deep.property('version' , '0.8.0'); + }); - it('should get latest version when do not found engine', () => { - content = content.replace(regex, ''); - var evidence = rule.getEvidence(mixfilePath, content); + it('1.1.0 from 1.1', () => { + content = content.replace(regex, 'elixir: "~> 1.1",'); + var evidence = rule.getEvidence(mixfilePath, content); - h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); - h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); - h.expect(evidence).to.have.deep.property('name' , 'elixir'); - h.expect(evidence).to.have.deep.property('ruleName', 'elixir'); - h.expect(evidence).to.have.deep.property('version' , null); + h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); + h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); + h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('ruleName', 'elixir-1.1'); + h.expect(evidence).to.have.deep.property('version' , '1.1.0'); + }); + + it('1.2.0 from 1.2', () => { + content = content.replace(regex, 'elixir: "~> 1.2",'); + var evidence = rule.getEvidence(mixfilePath, content); + + h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); + h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); + h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('ruleName', 'elixir-1.2'); + h.expect(evidence).to.have.deep.property('version' , '1.2.0'); + }); + + it('latest from not found engine', () => { + content = content.replace(regex, ''); + var evidence = rule.getEvidence(mixfilePath, content); + + h.expect(evidence).to.have.deep.property('fullpath', mixfilePath); + h.expect(evidence).to.have.deep.property('ruleType', 'runtime'); + h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('ruleName', 'elixir-1.2'); + h.expect(evidence).to.have.deep.property('version' , undefined); + }); }); }); diff --git a/src/generator/rules/elixir.js b/src/generator/rules/elixir.js index 6f5527b8..a0115b6c 100644 --- a/src/generator/rules/elixir.js +++ b/src/generator/rules/elixir.js @@ -1,17 +1,22 @@ import { _ } from 'azk'; import { BaseRule } from 'azk/generator/rules'; +import { Versions } from 'azk/utils'; +import { last } from 'lodash/array'; export class Rule extends BaseRule { constructor(ui) { super(ui); this.type = "runtime"; this.name = "elixir"; - this.rule_name = "elixir"; + this.rule_name = "elixir-1.2"; // default version + this.replaces = ['node']; // Suggest a docker image // http://images.azk.io/#/elixir this.version_rules = { - // 'elixir0' : '>0 <1', + 'elixir-1.2' : '>=1.2', + 'elixir-1.1' : '>=1.1 <1.2', + 'elixir-1.0' : '<1.1', }; } @@ -20,19 +25,9 @@ export class Rule extends BaseRule { } getVersion(content) { - // https://regex101.com/r/jQ6eE8/1 - var regex = /elixir[\s]*\:[\s]*\"(.*)\"/gm; - var version = regex.exec(content); - version = version && version.length >= 1 && version[1]; - version = (version || '').replace(/[~> ]/gm, ''); - - if (_.isEmpty(version)) { return null; } - - // force valid format: eg: 1.0 => 1.0.0 - var split = version.split('.'); - _.map(_.range(3), (i) => { - split[i] = split[i] || '0'; - }); - return this.semver.clean(split.join('.')); + // https://regex101.com/r/jQ6eE8/4 + var regex = /elixir[\s]*\:[\s]*\"(?:[~> ]*)?([0-9.]+)(?:(?:[or ~>]*)?([0-9.]+))?(?:.*)?\".*/gm; + var versions = Versions.match(regex, content); + return versions && last(versions); } } diff --git a/src/generator/suggestions/elixir.js b/src/generator/suggestions/elixir-1.0.js similarity index 82% rename from src/generator/suggestions/elixir.js rename to src/generator/suggestions/elixir-1.0.js index 254b3b4a..5248de56 100644 --- a/src/generator/suggestions/elixir.js +++ b/src/generator/suggestions/elixir-1.0.js @@ -5,16 +5,17 @@ export class Suggestion extends DefaultSuggestion { super(...args); var name = `elixir`; + var version = '1.0'; + // Readable name for this suggestion - this.name = `${name}`; + this.name = `${name}-${version}`; // Which rules they suggestion is valid - this.ruleNamesList = [`${name}`]; - + this.ruleNamesList = [`${name}-${version}`]; // Initial Azkfile.js suggestion this.suggestion = this.extend(this.suggestion, { - __type : `${name}`, - image : { docker: `azukiapp/${name}` }, + __type: `${name} ${version}`, + image : { docker: `azukiapp/${name}:${version}` }, provision: [ "mix do deps.get, compile", ], @@ -30,9 +31,6 @@ export class Suggestion extends DefaultSuggestion { ports: { http: "4000", }, - envs : { - MIX_ENV: 'dev', - } }); } } diff --git a/src/generator/suggestions/elixir-1.1.js b/src/generator/suggestions/elixir-1.1.js new file mode 100644 index 00000000..d5c6b7ee --- /dev/null +++ b/src/generator/suggestions/elixir-1.1.js @@ -0,0 +1,21 @@ +import { Suggestion as DefaultSuggestion } from 'azk/generator/suggestions/elixir-1.0'; + +export class Suggestion extends DefaultSuggestion { + constructor(...args) { + super(...args); + + var name = `elixir`; + var version = '1.1'; + // Readable name for this suggestion + this.name = `${name}-${version}`; + + // Which rules they suggestion is valid + this.ruleNamesList = [`${name}-${version}`]; + + // Initial Azkfile.js suggestion + this.suggestion = this.extend({}, this.suggestion, { + __type: `${name} ${version}`, + image : { docker: `azukiapp/${name}:${version}` }, + }); + } +} diff --git a/src/generator/suggestions/elixir-1.2.js b/src/generator/suggestions/elixir-1.2.js new file mode 100644 index 00000000..2e2f0427 --- /dev/null +++ b/src/generator/suggestions/elixir-1.2.js @@ -0,0 +1,21 @@ +import { Suggestion as DefaultSuggestion } from 'azk/generator/suggestions/elixir-1.0'; + +export class Suggestion extends DefaultSuggestion { + constructor(...args) { + super(...args); + + var name = `elixir`; + var version = '1.2'; + // Readable name for this suggestion + this.name = `${name}-${version}`; + + // Which rules they suggestion is valid + this.ruleNamesList = [`${name}-${version}`]; + + // Initial Azkfile.js suggestion + this.suggestion = this.extend({}, this.suggestion, { + __type: `${name} ${version}`, + image : { docker: `azukiapp/${name}:${version}` }, + }); + } +} diff --git a/src/generator/suggestions/elixir_phoenix.js b/src/generator/suggestions/elixir_phoenix.js index 48ef34b3..46a43b95 100644 --- a/src/generator/suggestions/elixir_phoenix.js +++ b/src/generator/suggestions/elixir_phoenix.js @@ -1,4 +1,4 @@ -import { Suggestion as ElixirSuggestion } from 'azk/generator/suggestions/elixir'; +import { Suggestion as ElixirSuggestion } from 'azk/generator/suggestions/elixir-1.2'; export class Suggestion extends ElixirSuggestion { constructor(...args) { diff --git a/src/utils/versions.js b/src/utils/versions.js index f9abb89c..31a614eb 100644 --- a/src/utils/versions.js +++ b/src/utils/versions.js @@ -3,8 +3,6 @@ var lazy = lazy_require({ semver: 'semver', }); -// var last = require('lodash/array/last'); - /** * Versions: Abstraction above the [semver](https://www.npmjs.com/package/semver). */ From c6e4bee29b7cc75c8879f23323f6a564b1fb7af1 Mon Sep 17 00:00:00 2001 From: Gullit Miranda Date: Tue, 9 Feb 2016 01:52:41 -0200 Subject: [PATCH 27/61] [generators] Updating `elixir_phoenix` --- spec/fixtures/elixir_app/mix_phoenix.exs | 33 ++++++++++++++------- spec/generator/rules/elixir_phoenix_spec.js | 10 +++---- src/generator/rules/elixir_phoenix.js | 23 +++++--------- src/generator/suggestions/elixir_phoenix.js | 3 -- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/spec/fixtures/elixir_app/mix_phoenix.exs b/spec/fixtures/elixir_app/mix_phoenix.exs index 0abc1d15..aa8ded60 100644 --- a/spec/fixtures/elixir_app/mix_phoenix.exs +++ b/spec/fixtures/elixir_app/mix_phoenix.exs @@ -6,34 +6,47 @@ defmodule HelloPhoenix.Mixfile do version: "0.0.1", elixir: "~> 1.0", elixirc_paths: elixirc_paths(Mix.env), - compilers: [:phoenix] ++ Mix.compilers, + compilers: [:phoenix, :gettext] ++ Mix.compilers, build_embedded: Mix.env == :prod, start_permanent: Mix.env == :prod, + aliases: aliases, deps: deps] end - # Configuration for the OTP application + # Configuration for the OTP application. # - # Type `mix help compile.app` for more information + # Type `mix help compile.app` for more information. def application do [mod: {HelloPhoenix, []}, - applications: [:phoenix, :phoenix_html, :cowboy, :logger, + applications: [:phoenix, :phoenix_html, :cowboy, :logger, :gettext, :phoenix_ecto, :postgrex]] end - # Specifies which paths to compile per environment + # Specifies which paths to compile per environment. defp elixirc_paths(:test), do: ["lib", "web", "test/support"] defp elixirc_paths(_), do: ["lib", "web"] - # Specifies your project dependencies + # Specifies your project dependencies. # - # Type `mix help deps` for examples and options + # Type `mix help deps` for examples and options. defp deps do - [{:phoenix, "~> 0.13.1"}, - {:phoenix_ecto, "~> 0.4"}, - {:postgrex, ">= 0.0.0"}, + [{:phoenix, "~> 1.1.4"}, {:phoenix_html, "~> 1.0"}, {:phoenix_live_reload, "~> 0.4", only: :dev}, + {:postgrex, ">= 0.0.0"}, + {:phoenix_ecto, "~> 2.0"}, + {:gettext, "~> 0.9"}, {:cowboy, "~> 1.0"}] end + + # Aliases are shortcut or tasks specific to the current project. + # For example, to create, migrate and run the seeds file at once: + # + # $ mix ecto.setup + # + # See the documentation for `Mix` for more info on aliases. + defp aliases do + ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.reset": ["ecto.drop", "ecto.setup"]] + end end diff --git a/spec/generator/rules/elixir_phoenix_spec.js b/spec/generator/rules/elixir_phoenix_spec.js index acbe5944..ce6196e4 100644 --- a/spec/generator/rules/elixir_phoenix_spec.js +++ b/spec/generator/rules/elixir_phoenix_spec.js @@ -18,7 +18,7 @@ describe('Azk generators, Elixir Phoenix rule', function() { beforeEach(function () { content = mixfileContent; - regex = new RegExp(h.escapeRegExp('{:phoenix, "~> 0.13.1"},')); + regex = /\{:phoenix\,\ "(.*)"\}\,/gm; }); it('should return an evidence object', () => { @@ -26,10 +26,10 @@ describe('Azk generators, Elixir Phoenix rule', function() { h.expect(evidence).to.have.deep.property('fullpath' , mixfilePath); h.expect(evidence).to.have.deep.property('ruleType' , 'framework'); - h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('name' , 'elixir_phoenix'); h.expect(evidence).to.have.deep.property('ruleName' , 'elixir_phoenix'); h.expect(evidence).to.have.deep.property('version' , '1.0.0'); - h.expect(evidence).to.have.deep.property('framework' , '0.13.1'); + h.expect(evidence).to.have.deep.property('framework' , '1.1.4'); h.expect(evidence).to.have.deep.property('replaces').deep.eql(['elixir', 'node']); }); @@ -39,7 +39,7 @@ describe('Azk generators, Elixir Phoenix rule', function() { h.expect(evidence).to.have.deep.property('fullpath' , mixfilePath); h.expect(evidence).to.have.deep.property('ruleType' , 'framework'); - h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('name' , 'elixir_phoenix'); h.expect(evidence).to.have.deep.property('ruleName' , 'elixir_phoenix'); h.expect(evidence).to.have.deep.property('version' , '1.0.0'); h.expect(evidence).to.have.deep.property('framework' , '0.12.0'); @@ -52,7 +52,7 @@ describe('Azk generators, Elixir Phoenix rule', function() { h.expect(evidence).to.have.deep.property('fullpath' , mixfilePath); h.expect(evidence).to.have.deep.property('ruleType' , 'framework'); - h.expect(evidence).to.have.deep.property('name' , 'elixir'); + h.expect(evidence).to.have.deep.property('name' , 'elixir_phoenix'); h.expect(evidence).to.have.deep.property('ruleName' , 'elixir_phoenix'); h.expect(evidence).to.have.deep.property('version' , '1.0.0'); h.expect(evidence).to.have.deep.property('framework' , '0.0.0'); diff --git a/src/generator/rules/elixir_phoenix.js b/src/generator/rules/elixir_phoenix.js index 534eb497..ad5c1477 100644 --- a/src/generator/rules/elixir_phoenix.js +++ b/src/generator/rules/elixir_phoenix.js @@ -1,29 +1,22 @@ import { _ } from 'azk'; import { Rule as ElixirRule } from 'azk/generator/rules/elixir'; +import { Versions } from 'azk/utils'; +import { last } from 'lodash/array'; export class Rule extends ElixirRule { constructor(ui) { super(ui); this.type = 'framework'; - this.name = 'elixir'; + this.name = 'elixir_phoenix'; this.rule_name = 'elixir_phoenix'; this.replaces = ['elixir', 'node']; + this.version_rules = {}; } getFrameworkVersion(content) { - // https://regex101.com/r/yG4cG2/1 - var regex = /\:phoenix[\s]*\,[\s]*\"(.*)\"/gm; - var version = regex.exec(content); - version = version && version.length >= 1 && version[1]; - version = (version || '').replace(/[~> ]/gm, ''); - - if (_.isEmpty(version)) { return null; } - - // force valid format: eg: 1.0 => 1.0.0 - var split = version.split('.'); - _.map(_.range(3), (i) => { - split[i] = split[i] || '0'; - }); - return this.semver.clean(split.join('.')); + // https://regex101.com/r/yG4cG2/2 + var regex = /\:phoenix[\s]*\,[\s]*\"(?:[~> ]*)?([0-9.]+)(?:(?:[or ~>]*)?([0-9.]+))?(?:.*)?\".*/gm; + var versions = Versions.match(regex, content); + return versions && last(versions); } } diff --git a/src/generator/suggestions/elixir_phoenix.js b/src/generator/suggestions/elixir_phoenix.js index 46a43b95..675db9e0 100644 --- a/src/generator/suggestions/elixir_phoenix.js +++ b/src/generator/suggestions/elixir_phoenix.js @@ -30,9 +30,6 @@ export class Suggestion extends ElixirSuggestion { ports: { http: "4000", }, - envs : { - MIX_ENV: 'dev', - } }); } } From bff46e6701ea8f6376d013297f1c7fff81961a96 Mon Sep 17 00:00:00 2001 From: Gullit Miranda Date: Tue, 9 Feb 2016 02:19:52 -0200 Subject: [PATCH 28/61] [suggestions] Removing `shell` configuration to elixir suggetion --- src/generator/suggestions/elixir-1.0.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/generator/suggestions/elixir-1.0.js b/src/generator/suggestions/elixir-1.0.js index 5248de56..f7bdf6ef 100644 --- a/src/generator/suggestions/elixir-1.0.js +++ b/src/generator/suggestions/elixir-1.0.js @@ -20,6 +20,7 @@ export class Suggestion extends DefaultSuggestion { "mix do deps.get, compile", ], command: ["mix", "app.start"], + shell: false, mounts: { "/azk/#{app.dir}" : {type: 'sync', value: '.'}, "/azk/#{app.dir}/deps" : {type: 'persistent', value: "#{app.relative}/deps"}, From 5c5accdd5b299209721fd1ed323c62678a4eae76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Thu, 31 Mar 2016 17:35:21 -0300 Subject: [PATCH 29/61] [Suggestions] Fixing lint in elixir generator --- spec/generator/rules/elixir_spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/generator/rules/elixir_spec.js b/spec/generator/rules/elixir_spec.js index 6d93ab35..0bca267c 100644 --- a/spec/generator/rules/elixir_spec.js +++ b/spec/generator/rules/elixir_spec.js @@ -1,7 +1,6 @@ import h from 'spec/spec_helper'; import { Rule } from 'azk/generator/rules/elixir'; import { path, fs } from 'azk'; -var last = require('lodash/array/last'); describe('Azk generators, Elixir rule', function() { var outputs = []; From b43b4856e1686f17a00a9436f027dd76ed9d3929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Thu, 31 Mar 2016 17:47:43 -0300 Subject: [PATCH 30/61] [Suggestions] Fixing lint warnings in elixir and phoenix. --- src/generator/rules/elixir.js | 1 - src/generator/rules/elixir_phoenix.js | 1 - src/utils/versions.js | 6 +++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/generator/rules/elixir.js b/src/generator/rules/elixir.js index a0115b6c..118ddfee 100644 --- a/src/generator/rules/elixir.js +++ b/src/generator/rules/elixir.js @@ -1,4 +1,3 @@ -import { _ } from 'azk'; import { BaseRule } from 'azk/generator/rules'; import { Versions } from 'azk/utils'; import { last } from 'lodash/array'; diff --git a/src/generator/rules/elixir_phoenix.js b/src/generator/rules/elixir_phoenix.js index ad5c1477..92591ae0 100644 --- a/src/generator/rules/elixir_phoenix.js +++ b/src/generator/rules/elixir_phoenix.js @@ -1,4 +1,3 @@ -import { _ } from 'azk'; import { Rule as ElixirRule } from 'azk/generator/rules/elixir'; import { Versions } from 'azk/utils'; import { last } from 'lodash/array'; diff --git a/src/utils/versions.js b/src/utils/versions.js index 31a614eb..c0c1926d 100644 --- a/src/utils/versions.js +++ b/src/utils/versions.js @@ -21,9 +21,9 @@ var Versions = { // fix cached regex regex = new RegExp(regex); var match = regex.exec(content) || []; - match = match.slice(1, match.length) + match = match.slice(1, match.length); var versions = []; - match.forEach((version) =>{ + match.forEach((version) => { if (version) { version = this.parse(version); versions.push(version); @@ -43,7 +43,7 @@ var Versions = { if (version) { // force valid format: eg: 1.0 => 1.0.0 (with depth=3) var splited = version.split('.'); - for(var i = 0; i < depth; i++) { + for (var i = 0; i < depth; i++) { splited[i] = splited[i] || '0'; } version = splited.join('.'); From a1552487e90aabfbb67b2ce373db3088937fcb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Fri, 30 Oct 2015 12:18:35 -0200 Subject: [PATCH 31/61] [Manifest] Adding `resolve` to `path` options. --- CHANGELOG.md | 1 + .../pt-BR/reference/azkfilejs/mounts.md | 27 +++++++++++++++---- spec/spec_helpers/mock_manifest.js | 1 + spec/system/index_spec.js | 1 + src/system/index.js | 18 ++++++++----- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35ee2edf..0a301dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Enhancements * [Code] Adding github issue and PR templates; * [Docs] The instructions for use `azk` and `Wordpress` have been improved; + * [Manifest] Adding `resolve` options to `path`, with default `true` value; ## v0.17.0 - (2016-02-19) diff --git a/docs/content/pt-BR/reference/azkfilejs/mounts.md b/docs/content/pt-BR/reference/azkfilejs/mounts.md index cc1d582a..e67dd6f4 100644 --- a/docs/content/pt-BR/reference/azkfilejs/mounts.md +++ b/docs/content/pt-BR/reference/azkfilejs/mounts.md @@ -8,7 +8,16 @@ 'INTERNAL_FOLDER': path('LOCAL_PATH'), ``` -Monta a pasta localizada no sistema atual em `LOCAL_PATH`, relativo ao Azkfile.js, na pasta `INTERNAL_FOLDER` dentro do contêiner. Caso algum arquivo seja alterado a partir da máquina do usuário ou de dentro do contêiner, a informação também é atualizada do outro lado. +Monta a pasta localizada no sistema atual em `LOCAL_PATH`, relativo ao Azkfile.js, na pasta `INTERNAL_FOLDER` dentro do container. Caso algum arquivo seja alterado a partir da máquina do usuário ou de dentro do container, a informação também é atualizada do outro lado. + +Importante notar que o `azk` executa uma especie de "resolução" do caminho passado para o `path` (veja a opção `resolve` a baixo). Isso tem duas implicações: + + * Se o caminho não existir ele não sera montado no container; + * O caminho é sempre relativo ao host onde o `azk` esta rodando, ou seja nos casos onde estamos rodando o sistema de containers dentro de uma máquina virtual o caminho da máquina host sera resolvido; + +##### OPTS (opcional) + +* `resolve`: um valor `boolean` que orienta o `azk` para não "resolver" o endereço da pasta. Seu valor padrão é `true`, se ele for `false` o `azk` não vai verificar se o caminho passado, tornando possível montar caminhos da máquina onde o sistema de containers esta rodando, ignorando se esta localmente ou dentro de uma máquina virtual. #### persistent @@ -16,7 +25,7 @@ Monta a pasta localizada no sistema atual em `LOCAL_PATH`, relativo ao Azkfile.j 'INTERNAL_FOLDER': persistent('LOCAL_PATH'), ``` -Persiste os arquivos dentro do contêiner no caminho `INTERNAL_FOLDER` para uma pasta persistente do `azk` dentro da máquina do usuário. O local dessa pasta varia entre Mac e Linux: +Persiste os arquivos dentro do container no caminho `INTERNAL_FOLDER` para uma pasta persistente do `azk` dentro da máquina do usuário. O local dessa pasta varia entre Mac e Linux: ###### Mac @@ -89,13 +98,13 @@ A pasta encontra-se no disco virtual (`~/.azk/data/vm/azk-agent.vmdk`), no diret `~/.azk/data/sync_folders/#{manifest.id}/LOCAL_PATH`. -Note que utilizar o mesmo 'LOCAL_PATH' no mesmo Azkfile.js, mas em contêiners diferentes, significa que eles irão compartilhar os dados sincronizados. +Note que utilizar o mesmo 'LOCAL_PATH' no mesmo Azkfile.js, mas em containers diferentes, significa que eles irão compartilhar os dados sincronizados. > **NOTA IMPORTANTE:** Se você estiver enfrentando problemas de performance ao usar o `azk` com sua aplicação, você deve utilizar a opção de `sync` quando for montar a pasta onde está seu código fonte. Vale lembrar que a sincronização é somente em um sentido, portanto é preciso adicionar o `mounts` as entradas com as pastas que devem utilizar a opção de compartilhamento de arquivos (usando as opções `path` ou `persistent`). ### Exemplos -* __path__: Monta a pasta atual do projeto (`'.'`) dentro do contêiner na pasta `/azk/azkdemo` (considerando que `azkdemo` é o nome da pasta onde está o `Azkfile.js`). +* __path__: Monta a pasta atual do projeto (`'.'`) dentro do container na pasta `/azk/azkdemo` (considerando que `azkdemo` é o nome da pasta onde está o `Azkfile.js`). ```js mounts: { @@ -103,6 +112,14 @@ Note que utilizar o mesmo 'LOCAL_PATH' no mesmo Azkfile.js, mas em contêiners d }, ``` +* __path (resolve: false)__: Monta o caminho `/var/run/docker.sock` para o mesmo endereço. Neste caso o caminho da máquina onde o sistema de containers esta rodando é usado e nenhum tipo de "resolução" é feita; + + ```js + mounts: { + '/var/run/docker.sock': path('/var/run/docker.sock', { resolve: false }) + }, + ``` + * __persistent__: Persiste os arquivos de dentro do container que estão no caminho `/azk/bundler`. Estes arquivos, nesse caso, ficarão guardados na _máquina host_ na pasta `~/.azk/data/persistent_folders/_ALGUM_ID_`. ```js @@ -111,7 +128,7 @@ Note que utilizar o mesmo 'LOCAL_PATH' no mesmo Azkfile.js, mas em contêiners d }, ``` -* __sync__: Sincroniza os arquivos do projeto dentro do contêiner na pasta `/azk/azkdemo` (considerando que `azkdemo` é o nome da pasta onde está o `Azkfile.js`), excluindo arquivos CSS e a pasta `config`. Além disso, usa compartilhamento de arquivos para as pastas `tmp` e `log`. +* __sync__: Sincroniza os arquivos do projeto dentro do container na pasta `/azk/azkdemo` (considerando que `azkdemo` é o nome da pasta onde está o `Azkfile.js`), excluindo arquivos CSS e a pasta `config`. Além disso, usa compartilhamento de arquivos para as pastas `tmp` e `log`. ```js mounts: { diff --git a/spec/spec_helpers/mock_manifest.js b/spec/spec_helpers/mock_manifest.js index aafaa2f5..e2354b65 100644 --- a/spec/spec_helpers/mock_manifest.js +++ b/spec/spec_helpers/mock_manifest.js @@ -171,6 +171,7 @@ export function extend(h) { "/azk/#{system.name}": '.', "/azk/root": '/', "/azk/not-exists": { type: 'path', value: '../not-exists', required: false }, + "/azk/not-resolve": { type: 'path', value: "/azk/not-resolve", resolve: false }, }, docker_extra: { HostConfig: { Privileged: true } diff --git a/spec/system/index_spec.js b/spec/system/index_spec.js index 7dbf08c0..04223874 100644 --- a/spec/system/index_spec.js +++ b/spec/system/index_spec.js @@ -98,6 +98,7 @@ describe("Azk system class, main set", function() { h.expect(mounts).to.have.property( "/azk/root", utils.docker.resolvePath(path.resolve(manifest.manifestPath, "/")) ); + h.expect(mounts).to.have.property("/azk/not-resolve", "/azk/not-resolve"); h.expect(mounts).to.not.have.property('/azk/not-exists'); }); diff --git a/src/system/index.js b/src/system/index.js index 60807953..de278e35 100644 --- a/src/system/index.js +++ b/src/system/index.js @@ -404,9 +404,8 @@ export class System { ports[data.name] = [data.config]; }); - var type = daemon ? "daemon" : "shell"; - // Make mounts options + var type = daemon ? "daemon" : "shell"; var mounts = _.merge( {}, this._mounts_to_volumes(this.options.mounts || {}, daemon), this._mounts_to_volumes(options.mounts, daemon) @@ -551,6 +550,7 @@ export class System { version : version, default_domain: config('agent:balancer:host'), default_dns : net.nameServers(), + dns_port : config('agent:dns:port'), balancer_port : config('agent:balancer:port'), balancer_ip : config('agent:balancer:ip'), }, @@ -591,17 +591,21 @@ export class System { mount = { type: 'path', value: mount }; } + mount.options = _.defaults(mount.options || {}, {resolve: true}); + var target = null; switch (mount.type) { case 'path': target = mount.value; - if (!target.match(/^\//)) { - target = this._resolved_path(target); - } + if (mount.options.resolve) { + if (!target.match(/^\//)) { + target = this._resolved_path(target); + } - target = (fs.existsSync(target)) ? - utils.docker.resolvePath(target) : null; + target = (fs.existsSync(target)) ? + utils.docker.resolvePath(target) : null; + } break; case 'persistent': From a5cf7151b356e7eeb16f83aa242cf7166de9afbf Mon Sep 17 00:00:00 2001 From: Julio Makdisse Saito Date: Fri, 30 Oct 2015 13:25:16 -0200 Subject: [PATCH 32/61] [Manifest] Translating docs to english. --- docs/content/en/reference/azkfilejs/mounts.md | 25 ++++++++++++++++--- .../pt-BR/reference/azkfilejs/mounts.md | 6 ++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/content/en/reference/azkfilejs/mounts.md b/docs/content/en/reference/azkfilejs/mounts.md index 2a483955..060d16e5 100644 --- a/docs/content/en/reference/azkfilejs/mounts.md +++ b/docs/content/en/reference/azkfilejs/mounts.md @@ -1,6 +1,6 @@ ## mounts -Mounts has three different usage options: `path`, `persistent` and `sync`. They're used to configure which folders will be internalized to the container or persisted internally by `azk`. +`mounts` has three different usage options: `path`, `persistent` and `sync`. They're used to configure which folders will be internalized to the container or persisted internally by `azk`. #### path @@ -8,8 +8,17 @@ Mounts has three different usage options: `path`, `persistent` and `sync`. They' 'INTERNAL_FOLDER': path('LOCAL_PATH'), ``` -Mount the folder located in the current system machine `LOCAL_PATH`, relative to the Azkfile.js, to the path `INTERNAL_FOLDER` inside the system container. If any of the files are changed, from the user machine or from inside the container, the information is also updated on the other end. +Mount folder for current system machine in `LOCAL_PATH`, relatively to Azkfile.js, to `INTERNAL_FOLDER` path inside system container. If any files are modified on user machine or inside the container the file is also updated on the other end. +Note that `azk` "resolves" the path set on `path` option. This leads to two things: + + * If path does not exists it will not be mounted on container; + + * The path is always relative to host where `azk` is running, i.e. when running the containers system inside a virtual machine the host machine path will be resolved; + +##### OPTS (optional) + +* `resolve`: `boolean` value that drives `azk` to not resolve folder path. Its default value is `true`, if it is `false` azk will not check the path, making possible to create paths of host machine where the system of containers is running, ignoring if it locally or within a virtual machine. #### persistent @@ -49,7 +58,7 @@ Syncs the files in `LOCAL_PATH` with a remote destination, which is mounted in t * `daemon`: a `boolean` value that indicates if, when running `azk` in daemon mode (e.g. `azk start`), `azk` should either use or not use the `sync` scheme (in the negative case, the `path` scheme is used) (default: `true`); * `shell`: similarly to `daemon` option, it's a `boolean` value that indicates if, when running `azk` in shell mode (e.g. `azk shell`), `azk` should either use or not use the `sync` scheme (in the negative case, the `path` scheme is used) (default: `false`). Setting as `false` is particularly useful to keep a two-way sync, allowing created files in the shell (e.g. via `$ rails generate scaffold User name:string`) to be persisted back in the original project folder; -##### Data persistency +##### Data persistence When using `sync`, we need to ensure that the all data generated by [`provision`](/en/reference/azkfilejs/provision.html) will be persisted. To do this, we have to add to `mounts` a `persistent` entry with correspondent folder. Those folder may vary between languages and frameworks, but follow some examples: * __Ruby/Rails__: @@ -96,7 +105,7 @@ Note that using the same 'LOCAL_PATH' in the same Azkfile.js, but in different c ### Examples -* __path__: Mount the current project folder (`'.'`) in the container on the path `/azk/azkdemo` (considering `azkdemo` is the name of the folder where the `Azkfile.js` is located). +* __path__: Mount current project folder (`'.'`) in the container on the path `/azk/azkdemo` (considering `azkdemo` is the name of the folder where the `Azkfile.js` is located). ```js mounts: { @@ -104,6 +113,14 @@ Note that using the same 'LOCAL_PATH' in the same Azkfile.js, but in different c }, ``` +* __path (resolve: false)__: Mount `/var/run/docker.sock` to this exactly path. This avoid any `azk`'s path resolution; + + ```js + mounts: { + '/var/run/docker.sock': path('/var/run/docker.sock', { resolve: false }) + }, + ``` + * __persistent__: Persists the files within the container that are on the path `/azk/bundler`. The files, in this case, will be stored in the _guest machine_ inside the folder `~/.azk/data/persistent_folders/#{manifest.id}/`. ```js diff --git a/docs/content/pt-BR/reference/azkfilejs/mounts.md b/docs/content/pt-BR/reference/azkfilejs/mounts.md index e67dd6f4..8c62425f 100644 --- a/docs/content/pt-BR/reference/azkfilejs/mounts.md +++ b/docs/content/pt-BR/reference/azkfilejs/mounts.md @@ -8,12 +8,12 @@ 'INTERNAL_FOLDER': path('LOCAL_PATH'), ``` -Monta a pasta localizada no sistema atual em `LOCAL_PATH`, relativo ao Azkfile.js, na pasta `INTERNAL_FOLDER` dentro do container. Caso algum arquivo seja alterado a partir da máquina do usuário ou de dentro do container, a informação também é atualizada do outro lado. +Monta a pasta localizada no sistema atual em `LOCAL_PATH`, relativo ao Azkfile.js, na pasta `INTERNAL_FOLDER` dentro do container. Caso algum arquivo seja alterado a partir da máquina do usuário ou de dentro do container, o arquivo também é atualizada do outro lado. Importante notar que o `azk` executa uma especie de "resolução" do caminho passado para o `path` (veja a opção `resolve` a baixo). Isso tem duas implicações: - * Se o caminho não existir ele não sera montado no container; - * O caminho é sempre relativo ao host onde o `azk` esta rodando, ou seja nos casos onde estamos rodando o sistema de containers dentro de uma máquina virtual o caminho da máquina host sera resolvido; + * Se o caminho não existir ele não será montado no container; + * O caminho é sempre relativo ao host onde o `azk` está rodando, ou seja nos casos onde estamos rodando o sistema de containers dentro de uma máquina virtual o caminho da máquina host será resolvido; ##### OPTS (opcional) From d00894861f9c141b2e56b72a74a3ecca936a5b44 Mon Sep 17 00:00:00 2001 From: Marcus Gadbem Date: Thu, 21 Jan 2016 21:10:49 -0200 Subject: [PATCH 33/61] [docs] Removing old and unsued files --- docs/content/.htpasswd | 2 -- docs/content/README.md | 1 - docs/content/cover.jpg | Bin 238900 -> 0 bytes docs/content/cover_small.jpg | Bin 13622 -> 0 bytes docs/style.css | 1 - 5 files changed, 4 deletions(-) delete mode 100644 docs/content/.htpasswd delete mode 100644 docs/content/README.md delete mode 100644 docs/content/cover.jpg delete mode 100644 docs/content/cover_small.jpg delete mode 100644 docs/style.css diff --git a/docs/content/.htpasswd b/docs/content/.htpasswd deleted file mode 100644 index 8b0729cf..00000000 --- a/docs/content/.htpasswd +++ /dev/null @@ -1,2 +0,0 @@ -codeup-staff:$apr1$cMGeYlvd$gcGq1wTbu3gDAbJmNIm/u. -codeup:$apr1$DUF5T8tU$sqXoymXsvTMOAYciczBhA0 diff --git a/docs/content/README.md b/docs/content/README.md deleted file mode 100644 index e10b99d0..00000000 --- a/docs/content/README.md +++ /dev/null @@ -1 +0,0 @@ -# Introduction diff --git a/docs/content/cover.jpg b/docs/content/cover.jpg deleted file mode 100644 index cd391b9a6fc0d1c9d4906d4d76beb6ff3938ee78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238900 zcmbTe2V7HG*EhOPLa!o0=~5I3Dm9cKU;t@>Ku8c36-8PS5h)^qEsAB#Pz3~zF-A#3 zK+#dbj&&loNK7J%A|hxoGK>YVV41s_=Y8jS?)$sncklgQ}VyBPdxxW2>`|XD5{;^zwiHll1$Qa z^EUv1P+@ED^o)WuDDQ%DcF~61sr&Dtj9asIO2%kR$@y@AP&S{Ev;HN|`@7A*WYUzJ zzAh&nwwW4pZu+|PDfxFO|FW?#1IpM~DDTp$c*{~>S4*aXK3K-l`+;(TTnV}qSb8s5&6Nc6Xh z$|%at*sx(fH8pK*YJR$1*!p$3sX4^}{Awo*< zKdt`v!qndWtHoHt-+PA4xc~2a|L*(md+Uz^;6DM^rt07K)-(ceZZ`ntPyc;yHXi`v zT>zZz{*U?0m^v?v4I6R;+}(?cirknPX>L;s`p@N zYGL*UyQxJ@Tc5qYFyF2qH#IH8ZvOu~#Q(<~|6^GHF^+k(jLeMuj2!r?BzVf0IazSK zbJCd`nCo-wm^uHe9sWP;_8()I6909sp|2z_e~0z>eMr8m9UHyMhNlLH^md zSluKrrFlzc4gc%hLm7Vl_xJzXH%uk`5mUg-vYWacMoY9yE6m?GB}1K<`lbo=fHA-U z3t$TzfeV-qynr8A07xJlkU>011j|4QNCOP84&;L(um$V@d%&-t3e*7<90mf=0#1O_ z;2gLNu7g|P9*}`Y;29VIufYg-4?cl!(2!{(hKMO*fy_eYAafBf#2*Pp!jV{nhAcx? zBUwleQiyCpN|9fY{RoO2L0XVg$OYs&atC>c^dc`2HS!Ue#9%Q77#zk1SJeM?XmN*{@5^VJT@7d ziOt7u!*Z|(ut%{cuotmiSOs-`hL(n@hMmTIjRhJ}8jCg3H1agIX_RT88e)yJ z8oz5OG+t`F*Z8SvsA;V^S2I8}T63vpmS&OW9?b)qBFzp>iRL5C*P5TSw6tbuIcxc8 zMQJV5VrgyGD$_cobyDk^)FHw)Bw?nT%?}VO2uTO7GUtixrpQxXp&(vq@H|YPSe_Q{h{#S$P2CfES1}h8- z4Jr(p4XzpV8jKkl8af%043iBv7*-gz7~VAOH~eB`YUE}VZIo`b-RPjvStGg8pT@ez zbBsfbQ;bWD4;Y^@?l%5on(j2GX<^e+r)`^daN31wkEczTn3{N+#G9-$DKlv`xnuHX zy7qLZ>EYAUr|+8HH2voEAycfWqiL9FnrW%2z*J)T8mEPG#zo^;xL^ktNSkV)=&^&T4^Gnia?DwAE89f7s5jCE2oUYi+OCs%M$b3ZBKBRXgkIthaV%b|H4zb`5sF z+r789wvVzev_E41!2X+qv%_MCoen1*o;&I}`a5Pi);ivBd_Q~EY|88{vyaVwI!AYo z{~X4g`Z-;5K07%(EpgiGbk6CGv$=D$bBXgY=ROxhmxV5QE=OIIcrCm?el7kGUgnB% z^>Jmm9(3)Vi<#>?mpS*)+=uft=Mm@S%sV>o@qB~%A@euRKQ{lR+YGl@x1Db1-2QZ* z?Vjvj?cU}7)5FIj+e6^-%yYUY*>k7oMbD33u3l+g2fdWuhTak0+r7_we;~{yWDt0S zCqAY=6ra65H++8h`uXPjp72%s&GuXE$Mt*ckMpPcm-*i&Y7#?<+liNnUjuvtHUyjw zc(-8wg0%};7rY5{4$KG?1P%o`1f>QY2^t8t3tk<}4<1-(zc6*-(S?H~M^ZXTL>dmk zhp+RCUzjXq)J?=$7dBWIys&^6xPQG4vQTW-!(@Hb3?v1w$cIYAAhibK-L1&c%cH z==hrW=Tv8E9`#a!RsuEQV1kO~N!vo}qMOoJ(Oc=C5K3O|CDOr>}u{?Zv-SXEfd{*pP(X-Nd<))Q)QY=%l zQ#w}}uS#8YX0_((C97Lke@~51J(@bPCVCBb&HJ>lwEDD>bW(b4x;i5$V}HhQW>Dt- z%r{vCwt4Me*-NrduG3k!dfkPb={ak2eqV39 ze$)DgxpQ;(50^xg2sdkNPTPEAi~W`zTV8Gr*?MFfvTe<_8`~YX@7n&F9my8&(A%+g zhjgdM&iy;bOP7>h+-0+iy=!Q90=PVIQz zs=7M|2nP<=>(v+54>ZI!oNt`nSk?F)WuT7^h8{f0wdU^Qem<0bNX{eiP8_y5{OjRK zK9m3CNaT^TN1cuyXwquh*rXC93A%*-!WNOG=vUE?=A7mMF-`n?i(gAit95JTG0d^T zW9s88j?3B>wVgjP|HP4#Gfw{c8~Cm8x3{NOpXxasbNa>^|1&4r=d^P>OglJdk+a2T z-=AZg8#ur0{KE_63pXw)zh%Xb3qT)69h_uM_-duQ+a-0zV3NIM?* zJ~-R$*L_|_lwEun`0$E+q5QgHk>ZvzT6ynL+@ps*i+i3tUh#O~N&1sFz1h7Vo^E{l z?HRjIvybz9+Vcbb*8N9c;9r~^@Ez!Ux#;EnLHgjcp|qiqR|T)Wzuu)XR2>+eHQe&X z>&<0#gj)7?`P_~2i-Vhv*TkIX_`{;LfJe&`8f4ggHxS3ctPw~T}GuD#5^ z0mc+2X@Fl-7mQ{8#wdUPOJ#1#$X=JaVST=xx7**4*~`O|5a2-!@ba?r^q3O-i4ef@ zk$;bekNNLsYU}W^|1InKPd)svm&mdI!mK$3ZmH?(*JQY*tzYN9DK*#K)6K&j1a1o0 zv@UmD#)ee8P3y9A3Ia9-IsDx$0LoLh-5u=yZn9x*ki$|66=J{q3_Bk;f}4j!u%4Zr zU0{BCW&mwb#D8^%KLt7b2TCsTc3YpHuBjf==1;c`s*4n(b4tS z*1;kU0Aqq+O_0BS2eaTo)r7beUQ=gl>OwGB4NW*AI1bpLV;USj7K?#{)zpAM8rcdz z2O1`t)9pP&v`iCHwH@+sUOTFf=s1R+x^6~$_sBdUQ zk2VQ}qGoYR>*+J?9cRy-zi{!!&EF-rx^CaOD_1BV^*nyk`}F1D(5u&~;Wz5HA3lza zPkj3P-=uLZXwkH3f-i+CVmvcT$P3`ZQ{ePR-&i|vC{WGzD&+92Lz#?$-uqI$3 z`2MXTa0l&|Gtp)-Jb%8<{bhtbu631dgldvWgSw*5+b7)dg9I!JcUhrW_O*HLPXc)7 z&I!DjfA|*H5JbMFC%Yc@7cE>t)hvol%s$`Jm(_nXLkc|Hh5jyty=}-Tl94XDawl6^ z?kogWnzk$LYY4hq8OW9+6}9Fqi)xQ_ZT}0b1Q8Eu=B*JWY5shzuZg@A=9UM{#n<^- z4J{{UxNiAVH;6y!p((DWpT)@CM#7bp64@@>_Q6eqY^&4tSwhXPg0Ub8aO{#q zjL>FEJs&VIGtJcrzqKsQY|0oV84!*|74+{OkpS|qXmy$>f<U8F&^%zg|7w;*Eyp^{^d3 zUTHU1tH@zaE*Rg6hC4f?4Y$}1bEQ^{%&z2k?;f_bsZ(uN7vZ|IwfyT*Hm3Fl zUs1+ZEIUQgp*yj;GAk!;lM@>;bFYg}sFBXK-Y^1RxK;ddw?CI^OaR@pX@tdYe-cG! zs2YYg5leWiWtRLbzUEV7mM;hCUO|&#H!60@O-m)n8>D}gwJdFzOFK3e<{J(o=(t-e zlx?@g-N}Gb`?``)+D7KeFgjKmG z&bRMj8C7FV;3s}~{CrNlHwxC*MLqnON86)?N~__4O;a6wtt{Il&_+S#-Nl^49I6X) zWu^VEJ|5Q8B=+2f3r%-hIGN=Q1jL+Ee{Z72vobk?1+wZRYwQE=}yE|AqO%i2J z`{9DY&g$^o-|SxFekQA-+%3g~-<`b|^KBYVN5o`#_r3P|Uw6B}WOK zlS~LJ3Mha4#ZLidaZ`Lb% zR{2}&wyaLASRMoOY?bvV!MD_Lv-s_}BVx>pr~Agm!t>5VNhG=!1(s=XG5dpDF@16^ z729ooFSd-n`{lS9HOT4*1M}k9^7FXEtl&q>w+mfL0QW)kD5W@_FaG>NYM(6w3fnKD z#@pkf3*F>Y7N{AWT|!TJ^DS}R9aY2cGL1cNzR~n=@{aPId#VnXfYa_qo9VmNpITE} zLZo21pP=HARln^MakR)&3S!qAXVc~5pCz>CaZC7y8lBF3t-2HY{^+3C3Jp=PENk{B zajd{rsWxJ1ocN9;Lx~#$6L}$6smR~j>Ni4>fH*hZ@n+qE#XSN;()1bg zh$7$kWNNRFfS4K!J+1^OkGop)Ue&jbF1t~N|QSMWMJ;~)u^d=e7E8+wH#*v zZ)?SYH!Xj#wEW@0pV2e^jk><_-0Q_frae`iAla|$ip$Jhyhl`D{NhdqX5f;uy!xbZ z$CBq!UH$AiYi|VMuVx27TG_y1t|x!dbA`-X#+C%jmGIAB#e zO1eN4A3VWy!CX5Zbc*ccvyu{M32I{t9^mighKy1(%b~{3*wz!df}S$|33IGoaJr%0wBM^oGI1 z!?RdB;t3z25}hR4)>*AEiMAKtk$_mY=pjx?U-e_NBp$RG%S>z3Q5nwirik0Z`PC%- z;c7J*E>Y-c?EDeZjPeg%_Ds?Eq~7u6Jf`sLAbY<4jd<3ocGJgZu?tU{Ib6jd-m%f6 z34vW#@RUX4U??|I=nl_X>u{S}u@o$wxo9J9tGMF4*c*+7HY_=&X*B*@$fs7;_c7YM zT4eQu_|#ZtWi~xf`dlsi;evD%yX|OO`K3>rGABf)Z~&SQZELswR!Kf~rRF|7s=p5L zlvi_tcZ&B;2*+G7L#xi^;;*iAR(xy~RxtV)m^+teZxr|cF{!ufEYsML1B%b5QO5R- z#LiWdO@kQ7vpd3NQG|nV?Kg10(aWzp@>P6#{LgkUM1yXf7p9 zKUm6R%iIhX#kbk90Jm=zOY{0CaX|zFbN9LJm*%bfPm(0V9r1<|Kp8&F3d@fahNhsa zf?P037m1?@op*w3Zzy}-k&OJDgTyl;73|pnY!bAUGzg|OYoDBA+u!%x%2`xY37VC z-{?R4@}7#1(LYl)4_qORMt_pWo45WhGX%^0(v|Cp!L^bjdkN4dKHWk5B=WISOW?k1 z+q9g_Wd+J-u|N@b^Uw!=T*1>OhkBdNH<91;HR({0^nPo1> zBEhVZ>N(ExBMGI_s6#H81blGqp8S9{2e}i1918I7t5!!oJ^1u!8ihDYGW{X0ep>b1 zb6mWcy2)CmahMsbdl&rcq8D$KAwt5qj45pif3g z#-`B5$+l`nFaGgerlQi<}kUrUy#JtKRV=yuDyfqwmp2w zfo(ZD5`ERiGt9C~rg4M*#)a9P>uT5EUZzAGSMa`w4xV{oW|FNiA7szdKl1`Ne%bC* z>*}$1`Uq*J=F8wZ>-yQB%*=^`ZMEQW!*BO<$4Gih)CvASSTq*V zn1Gnz`Z9UX8Tu1^t=&@pIsR_Um*HR$4IQCZCv<`(8KPk=VO3)Mb-s4f8d2nTmUtU0 z>eU2a_i(i&bck2yl3g+0V*vr}sH)zYR^5$j%@SekG~VHsaQ znxWA@er0=0>z>$$DioZ}6hD7E%THc92h8Plm}hS|a@gtMMfM!SnwT;rqa^VI2b9;K zsjCu_^i&P^NYm=NlPMi;PeXUI?Hy|Ahp}yb1n?o|Z1wUW{1?8?O8jo3>bQr6$P2=> z$$~gKIouA1*jb1g)b|uMG~j|c6Fw5_In>j3I*)q$GF3yHgI%-FU;LS>u^SpmwXj8a zie&uaT?kQ>BPDLjsRa#9>&VAyLLmJ096@7O)1?HsLWfJ3+MQ7|9jbQ&f^BiLmT1F7 zGZjo&yi(lmPEO+hCSh(`oBfE?;}l=__gc}4@z$3=7-QZfQ*Tw{#fB7i-x$ewM83zi z9X}xqTBrxotZBz4=_ys#mY~58_n0V1nh+FlK8d`EOZ#Vscp-(r?|(zfDHTY|C< zXEfZAM~Q$4Cw0DiBX2_MEw!rykXHp!Jh(EV@D>9ROi1lNHIE7;z%Gzh8ZDg<+?XzW zE){v)7U;(E=xs?HZUdM=G`F&ci|;ju7=3kO_LVwMRns;@;Imm#O4q$k(1O-_iI3Ge zYx+KM!>a<4-twMWq9{w@&o?H6ZSRs1sb3p0GEjz;$-izGU!gcVEN7dWqAO@)aa}mX z)Ij07Bza$_7zv}7X0?T1>Z$b;g6K>ArOt%=uV^#)+FyCK+TwkxTI&i1hNA3bIXrm> zv5I!B)V_jOjwZeOKr%6tAiv?4(kUP)rS5{6CAw+&WC;N*1RKd<}nvv;A^B$h@ZP&vk+aBX|6dhOd337*F`R`aKTXe$9K2RAMPn1 z2Osm9c%j-4ZoIIo(`R`QvloV3OxzCU7pXbiC3Or}&v%l(REGKU4*x96tM4Hk;*$^8 zMW!Ly@*Er(@Q%1IYv!>+Zu7OWB}gvw)wT`&d)U^^Jm$cMgwi-;lHM?G69i)<1AH77jTj02@I|K4>auMw0YA7mkg8d%wf&I8M02#% zbCVO>i5{0VF0Sju$Ees=j%jV)+l6z-D17bX+)4VQ)5YTO7>hp{$N-IpM+U2TY%{bW zj%m?y5Zc8rQu`J1lY}1^aEQ6NI&PGF;~+^-i)rB<9}@SB*N&{L$FbAeVn;Zs=6uZ* z{MR2aw-b{k1b7xjc}T4*0Sk%(m1l>&43x-e*7EHWJK3|j4;TG#5nP)vpm`}d{;KMp z29t^a<-P>oOd%AV-iQ~TOU!5qD_3k~({+4ZlTc z_2vZ%YCC**2QOB20E4{PVC5gfLbpDdMl8OZele|W(GtibI8>EBoo` z;ySqV4&4(<`v!LgUx07jq>$V*4eKopO`b;<{u5$5lT>le zxnLRV>|Y=`!6`tdSuCX~Omkg3 zzeFju@hB_A0VC%1Gd)M0waUj4w|8VFE43ivE!YSrot(L;8QLmYxIZY7C9xfCm~Wqm zswvJRBvW1HmcGwozmkN@G7SlgE|2yz!(`Y=o{K&jPW;XahhgGfMQmYuBpP^Grnzb! zO07$~Yauj|X>OlaEk*L^4POQuXl!$%0NMe806Kt;-zS8|2{Wz>jk~Apl33_Z7@V*| zK~@QsNff5kB6I95j+D@#mYtK=ug5jj#Ot|WW|)se85xpC5`L2aVtVsF*J%tx7=8E_ zDB7I7DM`|(04f%YH;ba3*;q4_j9|ug5LVFz&aufBKEfFF)%BJ+cfA@)ggG+ zQp1V$@){f%CwzQjtxVf7A;^T0S~33MN9C>U?*-3M(71}ne0hqqV|kB&E&)LYgU1_1 z2fCAyI}?2E8eRvEt7K^Tw{R}1(9iZe9{HJtnou)xAvv5YyOsye~IO!w^BFZ5I_%O&!4x^ew`xXPQ5 zw6o-$}VFNV>p0pOt4&Qy^R2NpNUHn-=men;A_ud zgQ_kb*Fz)#`SyTZ!YzM-wgkkWIMvZHPnmq53nsbztMCQ7)(1q~Bq$zp3VaPCZn1F> zQRWs)zcp+du3kho&9vf>y6g*IH{o<~%M!HpbOAlm=`Gl7@8O;=m&DmhW)4wwC}1ZErlXIDpDC*>j9X z;=0>HXHPWm^Ke0iIce6mhK@13JxwqMri_<6l_-~gZMuJFkUb;v&LC>8D!tQ$0~4%G z`;V%7%Ha~k45CDbCNJ7??ik5bv&mev4{uNljp#~mkDvU5sJCJOlBOH8?1`wcfMmRl z<(EwTIzrZBU}mf$=NM-zU*iKWf?so(p{tcgUFkL;tc*6S#iOH4s7X7T3(aiv$X5Mq ztrUEjYpklFe;nK|2*(}kVNWZwj61}l)b^~PVp>|7SSPub`B$J9Y3rjV^T#^XqHLMw zp__ywt-sax^G>f}J3ISWar`D_%NLSC`Cg$%S>o>{bPLEdT2*v1FMMZ3CRf5YN*oR3 zt%^?&2#}T#_yy7^s*2qX*a>lfW->xDd`GjN5UrEALMrlAdLnH;8E;WQGV4ogvl|p- z40Cc`kilYqA1|L!?KXeEHW0NOW#%x2YT+4@o)l#gk8(h4y8L1EMYgqwXT-8o4vd3O zghNa}`A%<=K4)!(YxpZ#0CdDjc~U#;G1^6_yXX0uO4#C;VQlcuZCE|k=lz@6Z02nYg&g&3sO#IiOPMD)8LoZ^{0@f9MaR|j2;N7NwAG40KKZj)dPx0VhqWD z5KZD*){gUamKIQ2mTeM^s@Rs=BT$$BtnKk9z@xYEE!&SUpaiN(WvnZGj8T83CPS-XIfrp~L&8k=( z@uG_WcG0)hIrZa(xeSb#QL(6oj)M&6LzxD#iSQ{_o(Wa1*182KqJ;Bnr1m9Xtr5%5zxhla$_XPG z%$K_Im~B0524;0$l$2r^1p_w2B2`2BgrG{*paf%cTx!r?h6&g%0upd1?g0z8#x+5K zf(Ws|UmAPUvHu7}h`!s&eq$8qj$o8rM7Mc^vt=NcT9+Q~IpW+?w+e@3`eji;ha%=hN%p)8!H#i)G z%q}$5>u`~Vj=-Qh>s*Ih^eft^#6AV4rwFp*W`EI+Qb?Z0wZGrWBH(^1fi1I{v&6Q& zCvc3Uf3ZOt@pkR3F%*RM5?%+lKNx>CP$t9Pxh8%Un7Fq(;kGx)#Ct6ID&ctha}j&y z{if|gr#=XoFufyDag1BcjWS?aztyUkF9i16@WPg56pt1@wy4u38>L(xizI_8`o#(H zO?v4!8K!O!Rle^P%&nG!&|AA%1x^9vNdZmE-;AD}jP7YBV*wZrW4?n2Y z(}8@2s%|+N$F=k>g;;kk{i7kNjZ?jf0FHN2a_qoxIJxN(^)1BOwS zP5SXRa6p+st$wVk%H(}-kW1x6E5hByfiewoZ*wB6fBgNka^PrtLFDtQFhZZzY3WyiLL+Rb34_#fi<89~kP0Fft5aV+xcC^H=h73zOFY z9ACbsVjNr*+7pE~3`c0~1*5_x636-8{(SwsE8#&E#jSUoh2)bcNN;Kj9YTK_^3fN9 zMbT21ZA|k5N;lNPg+2~v!Gmn&ax{|(Av1iNZ&oTL`yf%oD^t>M`m zP*uiB`w;g+5-|!5m>fp)=06EL-XOOc;)VF^@plv_7>rM}q!C9JUvEixgSke;b(ssM zZ)IA%L98%nvA~@IQ-nrunaqP9nb*&&*f?%s6H(;7S!THo+Rx?HTiVnvac`U-crxz@4^9g3z6_~vn6oe(1mc}hnv7K&(M>=G+`TILmKXYKuy*QxA zlSch078UWeU1j$qI=w9(h9IdrmsyrazkNY$$?Pja5m zRc`KN-Yp>Mlbg}lhdc&`9I;|Tkkpk-`#BD};+V&;)PBm@B&v}D!bzb$TL9!?=IeSzA4Qwh zWN3JV0wHxX6;sK|VUaH9QPN>0Mu!t+VHTmR9Xx*$C_hHGs=UF5F1An|*Nay8N~6rp z_*&Ja=-Qy5O5vhg>}hu>?pHS$FG_BjJWA-hzA4odi zm2Oj{>cqPQ4;9D3DWeB z*KId+F6(3dr>Pf@C3+kRJ=XL4uB%XUJ*a9-r`u3zM+q_2d*A=5iPT{Jq30;H)> zBdZN&0WS=rD-=sk;$FUGAkV{zOPo>0+k*I%l0o*&44-;4cUDCh$z-^vE-s!RXVTk1 zVA+J|Hx(~{K1&Fk7|xDpXtBUqCIiZ+RBy5-u=6LZhgO07GT84kQqxEiYpt8CX zkRgy*LBE+7rD8ii`=Z#J%p?-D_}YHta!KIfu70|E!&x9oJ_kQJ`kV0?T^YfU~oZvyDPak&@zp8 zWgN(vekF3xVR;P!{GqB4?I3GI9_H!(x|ZYdERg$ZEM8xo(9%*R)5zh~nNFzMJZ`J_ zRaA}D17kGDN%WTrX2Jy(d%l}_5#ezhQLyh+kKiqluh(r8L2J7>{6XAE9ArC4{4?t_ z@prF4(tCn`g86b2^zY&4MncS-`sifH3Z+T?h=k9!9kyPOPFtV6ZH%*TdtIQBKVS2C zSF(2qZdVBvWBQdws~cnoxquYr%a-;Fd|mv>C_{}`a9|~hq7ubHPO3&G+XgibVruss zDl6n`jYEREo3OiuKSr9qSCBO!YEf11VF0&+>!LqD3(_Qh!R?6axj+bor(4#=g+q7G zyj0mnZ1o)>8BH%y>}njL)<}tv3lHt1+q94-1P*MgtLVx;MZ8uO#IoYzW4_XF*B6_# zN?U$E%AUjJ=7rB5_Ta=qUip^Lg*BK*H5Xs54meX3G=G1<&z)1*W3djJEU1ts`KkDti6%V6c zdaVL~4Z@2Z>g%0`5&l9$DTuj{pRg?R>xSEqNh?Zd=eQ7?X;y=J& z{|HUc8D4vtzaUJy00(Xm2Y1BJrF?4#qN?}!NC#92E`4pbnHKmZR_oa*FH+TR>KQO6ZBONYNlx7J-jY{Yb#hBIJA(cVQG#@() z+i_%BlL&V#uFg~lLbdR^lAwAtO%EV~6)XJ*_j|vi z!3w%c8~m=-V@WownQD@b6>efnVj{L1hq!%LB(t|NCSkns4E3eo7$XCio2$h2tr4Z7 zWo0l6Rne|>U6?W05a)vY%+QKeW^^*9xX^8HHEI=<*^v6$@+b2~3hPR-s@B&9b9*~I z=2HKWq~^U4;to00Sn&PYEV^LmW7vN4ddnH71(cbh&UDGP9{F&Iy_NCTp3XagBgs2F)KE$BPyaq5d{q}<-!Qrh#^pu z^kAkc?m4Or8M2g3&CNI=9@?t+%Cg=jqJXb8(d+{GOMiD9P+Sw4b`w6ETPxlAK9G#& zkHk6#$}@STL1JV#Q4N_!l>o*Y5Lb$sOFvK^;JrzP+bG^#WqGUwxN>dr0}4onZrcFu zA$l{28JWDY9kIam70~J?L>|}q8bZ3%gU5u-zVTl`kJ+sC9dAAXU3OuM$yZsh zfvVB&_L1eBB}6Uu+^8Vt%u>$&kc1%{Ll9L-J6@N<4tGD@2=rp5)FDnmzbOAN!p70Tx zX$>V626nnaT${x6AP$TwwNwqOri@#_Rf0FPJ}2W1dF`O$g1Fa#<7#Gb9q(UnHn9)^^G1@3;PAP^#Bpof2v z9CDoxk-yc`n(c(1YpQIAg!YOxcAvBPhr_7z zE-P-r;M8YQxoC|rXL~?s5G0`k=#5@X0TpwM{%fAptM(iYc&e&=2xm(ybN(oTRU1yO zi}$x2uXBys%D`;QR?-TE#u7I_s>Y&%&>Gv zeD8^43RI1kP`@mg6+T`N8_3C1!88a=jbnrGFY(tQtFJ3y{9vtv0Z}nu@0&|+l0ix> zZ%GFT)%2&|-R?>`uO@*{LcRgLHjiYO!*Vtwu2AMQ@^$uh1X`{&pznZ%ueig>>q}OA zh*th^0S^M?6QVsT*jSHQv}Qld7@u2HK4IlcA#?)Dr67hh;^Y?v^w_#0&tYEsM|_zJ zf=Gi(=)vu+4CDuM50|-zJ#(9(+h2hE3y<*}mZVwvII%7+3{;AH`5JkW(2*nTd7F%F+MfxIq@s2u^aO4e!_SITp zxts6#CO}GSWuXH8^U2O|XNh>&jk(}S=?`G&?K0?yFO!FgXN!$}80CHupNQQp;RbsXa zGP9z?!Ov_adF2;1$y8gb>Z;GoMv46vnTCiQcz0I^@&*H8hzW|#lP+ij-nQ*;_CCOe zst#FhxxqGh(;RgXLm^~4D{|aw{($zB{m? z9`+n<0^N_Ba1{khMiOACw{JpJt?p-=#6!}^)l(V>!AxQr&yZ|Y!MfmcL0+_hujOqmYm*vy%MZ#XsTv>7wZ(8bJ1m8Uym8QLVjBu+1z5N#fc9n)?t5dsu#S4U2Y}4p$6mY;G!$daonGUm$NH@Uw(I%Q0Ll0w}v6 z?n@L*Dsx~aVAmjIl!zBZrllmEu0XxG{`O{++6ZkOvH1u;5|XQRh6}fbTGqnyQ2T9- zf-z5Odo3@k6D%F`V#Z`1+vmCn9>ydKGpqS=GmFS;ofw!ahOnCJwFg52eCp*@g~Eb_ zddO{QS1_TUXl$M<(|CEBIZ%0*zvND{t|!TK`hqdagMh?!?k%E51}1{2j&DHz(Xr6b9jUGXNc}j$^XvFm0RnDlbZTL3DwyRTIsXdtMi4v1eOP9rINs z8@*sw+*-Lw)m(U6{G@=S(#s zAT(h-3t~|V7C-#jl0*i@DMXQ3^ZITw6S5v9a+5e`E>%kt<~Bcwl*ccBh|L(sfzme3 zGZCIq6ksvYj2Ek~!7BVy-Q?YD%*}Uog8#!oX?(3q>7bW*^7sg~YRs%M^t1j7>!WmM-J`8r;cT z{GQ0y{k)nUv!-b}ZRQ=mZkVdpmgP}!yScd&;D_5hzqA;Kz%svx=aAQ$m`eNXqGI#N zO}O*Jo&K;^1PhZ`<|^UGFQU`a;CnyBEs?^ed_Mx>uM$+g(>pQCXP&d03-WPX#hF9I zQMLI^iLH+?XqB~0Bh+bA65YEhCB-v_7R2d zXk}5LB_ztRpT}YeGbTjd>Co`49*bJe$tf0g!7u||t5=iZ2(9j-cp>tUxiYs{RB>e- zaqaMj6UV>Gf+UB%_+DCDw96pB1QgeBnA@1_F_y%Gp$Zk z4JoTxJXU)4ql`g8BqVb{CVG!Dl`CfOHRqywC5kN%Ho*dF#2leG+qh&6p=mx~!F_>y zth>|>W;xDeHf5@K;hyz!=yDr{PYXimB!e{G_`#NCOkYcYg?d4Wn87;=;eAilxj=eB zh(BMuG$z)N3>kZq?hZdf*;h5?#ZQ<;Ddm>XCd73=n1it}E%|OFHjs8e>Ie%Wvv^Eh zxI4DB;FiD0l;)M$c$z&OhJ(6Nx4R$~mJ-?AD8JHv#E6IkhnRUWXQxn(#C#ad3qCJW zvGrD}8sdW(^I^(HQ+*+*WocTQ(-h02f#xQ&PPi_zh5RCyZsFN;d`(jx3g986OPz?q zxXUuEGOJA!pEyZ7L&ai-g+WWkM8oJNm`CXCCX_WcLu%bx50o_zQRYgbmX;_Cfn9Jr zf0FJ!LN){o1E~?=x>8vBA=B`3|0u3Z=7oz_7V|X{7qpCcJDySz@%^*l0RuT* zkl-mHfB)NkdGwCN(MKZsnH#G~#@h0oTh%il4A!b27v$sn}F5 zhg{O6Ohp1^(VD1f(+n81%oe3(DAOj!HL&zZgvnabsx4F2)I4Zbqyf=xJzAJiW2M&U z)O>#*p5MRonk?a*`@TQd^}eqADt9~cucmEC%3ZADKtC&*=u-eJmutcbVn9ky?do8` z%n$mY@m)R%Tz4>RV!2{)qrVl<)?##sXa%kn>8E+3pOb^Z|59PLCommQD@Zel(zg!* z$Os@R{^Y=tav`#wSQ*EvYW3mZt^}9Vq(5uNm)eqX66Vye3W8K@062Im7-RQ3l-{tp z{x)a^e&uqoL_>00#Un{rjADxdzJ;|_Xq%^IRTD@(;WrZAE+>>1_f*3^cxN7Wc>G2y zvjmQ-2|q_s_v#|87x3a_O?@gHk3h}LJ*XV(Vn1_ zMP&B^lG^~SAcLg~azPGifC3{=5ja}ltLL3jP=0F%6eW3aZ^=?m;CA8Y5ne!XMa#XT zNRx1)jI5p}7SqX7hMZ9(Pt$n9J_@+QNCv!J39#u+M_o2oRvc(W!ZLs53f4*7LUFH6 z`MYT`Eh!&JV>03&U~6NCRW3*o{*9IPf#QQl{x!AZ8^05L`#^qL)sV3k%MZof=5gHb zN6_7_nYyCy!}OAtfoD=UGPe=7B;b*--1os%JT81s=u0cZNtJ(F=6nP|V1GhXjea`( z;Zu*{1d2+zQJt`HE^t=3jVNcW+gC`kPh`W+8iY20Go;7Y4J%wp<0^~gXntoenJt(& zY9w@;8zeQPF)RECSHHgvG1P%{m$<_$+M$R->Gt)5v<-b&(g$jYSun$0+7dXAcRAr* z=AKZy%~1anQ8h(?V!knCBj^B{su9!gETHwzpQ|DA%}0Gf=~sY z;^IzrvajhNeD91YT+MG__6cDa9wxhYV{pCe6=d#Y;lqjmRS?&x*V7<+FyquF_!Dz^JxC5fuMU!sm8^%{eN@)r)qI5ImpY?Jv*Ryi zqTT=!Rs>cRZ!)-){@!IbpqiE2fT{MF0K4MOTD-F{FXv*sVu8e=W+`fZ$97#(1LK-eOT)a4#AjipFt(I<1= zWa-1)#oyjVO2rT=H0_IS^T0{D}7cW6G+$s-v@2Qz`#ux36VXUfppiA*2h{Y=l|() zl$egdxZWb~3IP3~vJ_6qkDDHCI1eoIfM zMO!Hq08l&P%P%*sD%&+=GX;|!M!P~09Z^Si#+NtaaO^FH^Mh%maZ*AQMwkyX+OWb4 zePBLTX+oYevkj7TcPFe(F!8eLKYI$`RJV0?Q8tSEp-h-3fy7D|A`im9SrxjLXXdTPCRHbCgmR4!@*^=PWNvv?Jplf zEO{*NbnfX9WmK+U#$c>vZ-_QYLWjff+!WrlzZ1B(D9BPc6rvwSdO+L}~Ga%J<0$I5g5tpvqeyKN%8*9q6YmY_5kDT_yxUOT)? zr5GswA+vw4>({xAsQhhN0<@9auC?zL_*@iijF$+xVD7|?8Y_UZ#{ou?=r^CWBfCH2 zP!lZ235mb>o$x@ffNFo|r*DJw17B77Lo(-PamAdt%sdN7Jfuk@;s#B_75QwN#=k|< z)A%Ai6=w2=jYN?)Hl4fTsbE@;VxsYJZ$H@w3~YzY>lq$_HQh(A3K7Adm2Gto9+vrw z+aLie2#IGs5;74~&?5-2tgF_G^eK92^R4H@Ab=d?Qd?F==wkxdNOeblnuTv#e~y$* za?9;xbA5hie&=T9Kmgnuuu;|6^oig%Idt+iXRsUYDr@Zo-fpf0J~u{v7gnm$w?t1q zA47JS_dPyd_W^bZjI-RGzyBC}i)~!^Di0C{#iCzO+}KqdJBi7#8?2$fjWmMgkYF;t z6bQ|fJIKK@Zj_$fVSD9$VcZMd6NWq)H4G$xy7WZZ5}jD10?zx-4{2AOYRd>^@XP$u zwZ2cg7&d(P9AEOYj$r$btuo1^L%8)bI{9K>;fVu=Q@Z~{9?6_;*Q-}8;C0^0n#`rWUwfv8*hdNCv!yGcuV7V zbK%v=2dm%zFYcXgur!6+pYnW7$8Bly=#^PYI*$NRbx>)qG`0~MDiCUdW?qhAx*l@; z4tti%+NHHIE4w;K9$5Tv)5i2aiC%c*9^k6gLL5Z0hLz`y;=v_=7SBxJ%wk3k0{#;u z^IjGJfCmT$If_A^U-!@^*r$6|ac@x{{`AdQ`_`^weno^X@FL+yK%NSh+z@hOlzlr; zq~~9WaU6r14J(f*xZ=5~*di$j7T_ym<*FJQ9A=gsqtpNtKeJa$E)&dnCav|Y0qTak zT7fLhxjC??JhO_{7K*!BNIfLC4-%$1BBUbpbsjTDj}nm|Q#<*^yM|BP`M3Gw2pG8H zkG!#A%kqZQGxDT>P@L!9Jj;tKO1uLCc>-sF$@JO?$pVaUuena$& z82rSlJJr4xfm32diasMA<_-`+vkukq4LdA?kmZD3w`hyTqMslqM+@3EtPl0;-~JU& zIi2V*-q9jC36sDJ0z6r(SBZhJhj7aC9(fHfMCLdcvG8zHrapk> zU|SWxu$T7Jn+lmzdj#IYUJIH}ch`EM$aj;>(xf7Ez=&4K&7kCS{SL}-QfxI^fh~lg zqB4xUtWO!LSsM@|vtuOxaLpdv6g#3@k)y;Ni>5=){9N$Ggwvtx~{5vJa9g}t%cjQW%%mRDbtWncm0(C3Ssk^vo zla`uoscDjpiB+)rnV#PXcJP@@Q>lmL_&cJpYLqW|3symbsiFT(+Ew|Irs5X%3RKcUTp1+jD^2Bqiu! z;i{;zg*-VTZA62!7D>D(pFLuXu1ULQ!{RMccMliv`X8!stFv|HQa9<=#?K zO}H6Jpg;nmFIepDjyF7!IcCE-b3>4jDjh>b@p^XK7cE+Fxnr7Ni*^E4RgcSjjDWWvvW=Ag6ky{Bf@wnyvOKXqd_X&b4&Sl(B z=iy^CxE_I1>{s>U9!cBrTsTQ>i}C0AnTGRCpmd;r4gFzy12eGY_|}`6_5|*!`hJqf zV>;xRQveW(l{xr96pUtXJ`!Uk*}E%J?n-$pKarQg3_}r-mo2CT{>=k?{qusB;7+#7 zt1mK#^f1n|0Lp#|eTEKM=}yb-qr%RWx>(~JZ3Y_zmt84D4&*F3+S|CYM8NW zlzH~{XqO{lGFFxE$}SqDHy5FZupr|Fqeci;1oGuIYX<)PCo1R(+5S5XjqM7ehA9ZC z(EN8qopse9B2C_;5Au6B>C%GXFyJZUa*xl#E_PtT#j-J`SEgTwy?R7@AT&LRo)d6% z83bOT;sQ4-5$$fSSHQdO$#Ml*nYu@e2>Qhg0qYMXBQ?0A1lOTvUd44 zbq*dk)w<+ScljD)4GbPnJ}fEs#YDN>Q*199Vn47ZuX@l^Fq`g>DrAX0ATf!tl zWYP6npEG<(Wmq&cTIdo8QTi~teU(#Pdk9txA^^#W`|gRAG4-a(9>7EIBRf1?F7l0l zsJ91$zGH}5*R%n0O@9vR$B%fdOw-3NC?GjVhbL;%UNGP@o-og*AWU)NzActHTbj0M zoKcV4vN0|_d-Vx6!^a(jh~&j8tj1e_Kh2#tnBk0?Uw0u+ao@S#M_S;x>1!;cv=hC8 zVXR3S^yaK1^az4-bkXb6C&xpY$s74K*fn9PWU5)DKtki0ecg4du=M}|x@0YAedi$x zmiP;TU$mp8=XnX;;tdHLWD|CL+ZWAQ8c_rzM@@f97|WmL57@DvT*@Ws(k|c)fY7r_ z2GVC&Dw|{&QaF!WXW91zBDGF}@m+3&Q-vgWJ0*q>`p75|3)ha~d{RZUCstN?Pu+{1 zbR>+t=c76avVeVfWMtGkw4o*$*)dTXmULU)``Xl01Gw|;1YU?|tmdVNeilx&*tj0? z8SV6yfZ*q%`z=Q=7^>z53%pXz((FGh)e2;qHKVdgb&~GT)Gh8s^+V7Mf&-&1i8@%^ zvVJ{p;oGP0i_CJ-l$I6`Dnn4{~Z5F|=g*^1~ zm^poAm&4*c_&SRsVw9ODb2`%+n~FMEBr&4^URNfE!11w$264457ailH6-ih9`ja5b z55XrXcgZK(Y|@y8a`4cTaty#LDtwt|hvaWz{w-miTvI?V>6U0qAl9_1DYv5$!`cmk zsoUn(?`4QU=zw7IT*cgmtgo+!(c&_}4^a@4AWqn2EP)fX^cR;!5fsoOn+h32cG8sQzUa@6&`DouUC4&IbYjJL^p~UIN`1h;OBi{ z{61dtL&ZOoOErhCsx}UM)i;U(9iKh_ zmv~k|GLra;(8 zj1SJ~GU3lEU(;Uh@)((mt^h!QrRhaVk6~DH(m&7?dW85Wfv|ra02Q7)WDcW-lk2Mg z*WWc`5~M$Nnr6=osmG9WSGyiOQY@|#Wq`2nSFFBc`XkqNaw2tTCh)N_Is`l`a=0_P zUFke5>(C>!CO%3Ci{7+qD861Pf(=T-@`U`6J{-AwnQ6#1D);9YChpzx<5pjNEZ4IN*V@CN~Ca_uA%ukPwOnX4?B`G;(* zBJTn1hyss$(Z-_}se@uP3!z;?V#>R$iugl0rIU}i@E=@o63L_by6 zsE|3(GS>cty2b5~j#V4QvT zyz56ph(6<4I}a%x1+8S7$q+&uH{Ks~n*1USt`|G4t>V`P0ClN8CV~-9!#8?y>=LeM zropE@ZN2RN*0VhJq>L-?17VdW3E#hWxUktLUe zi@;4)`1~Idhj9JH^@?0wG*}-Yfla(UyZa$V58fEFSITKuAaiz>8{z$zuZIH$K#tCp zoG0nyAYi!jtJ3(Z(*IhQ-5GXKzaFZq1YLGD?KXM0^E4-(ZchOh<9{-J(5z1UfxS$R zc6PQ4LhTB`i|O`%+CZ5_gm*v~9b>Z?O{}juJ27O8e)=lHk{mD(V8qUAndp#J_S!@Z z#i<`_Jl_4V#whM`P^r+i*AITj^qna3#>ymks-t@V3qq)?nznzSak&*CC~~F4O1VDl z|6a;{_k57#lEN*MTWR1uo?ItALk+^0o+C{@RrihX>;m0+NCn~~u+f0ini?+m2<#c6 zy}H=WK3YBpdC^bK;1OWPrE|F@+L)~}Tw8KfYr^lDV74SUQm5KAR%xj!v%vdZ*gVY& zMauV3oY?7RPtEUzY4ba#C19i%-X>>{h?O{wBU*-@PvV1cmV(2_#@0@A(X8fsUi5W z;oz7g;s>HC!_MDr$9uuAw~SYQJ;PjQDAb2vry*4DqY_h?>4OP5!w7Gu)HD!b*h4~L zp&5kM23UTM*u=+%Ph`EM%RWFvb|cVcA~D`^EhrN<*EvzNz@rifvY<%asBs@iz_G+> z76AfU`&vdcOo;t2leh~7OUxM_oI?8EQe9hta6YsMtHeFID{25|=sWFtzHaFGcL z3_Kbt339{aWW*W!d8_3Z4bIvJE27apWtaVf3uZ;C8gGkQ+`*Q{s+Uq&kjnKOq;6NGzyA4p6d z?cbqDj0nE$xZ6nHv%<~ z{qtNl^T&M+76jFVOmR4V26agyHNiG@U8~n?8^MEeeL4B@+PEST6e(!QequHcl+1OO{!?nzUW9;f^KZt12o60dyh(UXlxqJA`B~G^) z^e{B3oH|C0{0A5!+U9eEy(ZqML!4Fkm)aNut^*KS;m_`rL)f3gvN5#KRoowQ#V*La z2i-^0NG{$b!Op0mz+4CC^MBXJuJ(HumIaD!9-P#=cC!8ffji0!4LLivshY*)r$(gDM`fX_F$B7W|SO2MeZ1!y_Iq)9{k)KSZ7tGaC+Vr=;T3v>$b}Qjs1SEYo2JaJ(GDGbqNLc&U zNmICE;3(27EFabcs33B>PT-M2jN5wRZLx+9&+y}qylB4y<`m^w2s```_n?xi?@ue= zDs!|)KMhbBoy2LfF>h8@hK-$yoqrJqC54Dkq7p8OFqgjqCd5hS`GUr3M~A$ir>_so zztfPL42T^f49oQJv}U~63yJ0$#sx-D=6X0y+ITz0fewZL5OQi5RyvDs<|ZiziQH*8 z@KxQvObhQ9aoTEjWduvsBk;_YKPk~?T&y$UCaz>8&5KnY8G`l$UUZ=z#c;B*_5e%h zF%DYJ!FiqMxax_Op-W|BqmC7l+cCT$T#rQDCtOj54=Vw%HT)GUxT1Ih4V>oc0I^Y6 zFx<2raVb^}XRpO0C(V*pVE;1~g0tzzB>=m#4Bbn3avUmLOWL^q z0e+o>gKE{s)|BE8d|)YpBRkjzwnSy>yo`f=T)J&g1z0?HwkQ>$tv7#RanbT#CP{wka7BhZ$Abp$^maIx_nMZ`JzUj9OXj6BI89)zj zxi41AR-}R!lgq7`vNLnxcV+&2+SSs( z1U-@&f2e<3)8&I94fmSv4HGBwX(0PXh%oMAgtC;9#Oj0!CvguVLE z)!Kx^)#dPVbh4BDxxFH0s2*9$IY;G;ShV6HC0ZnN=}BbswGc$x->YIHDM)TzyLQ&~ zuz7omWR9{znme}T5_nXE)6K)NjcqXA45Kfu5NDNR8Jeg3sKpEK@n%mf3G3KEp>0FfP~<&BKbN`cx_RF1X7nQ6!ZbkLZ;c^VCLA z?uT{A!i&P4<5}LYjVx$>_1MbDw}=yvgo+dZ`VI+j0}$WCXYK`RIo4898yo?UWkPeg zv=)A$7HDfm{}q4zqOR+M;V3;UlPB;7DQIS=uVFHlKMILf#|5Se@(L+;To=-gH#|2) zi(ofLGb`9!fEhh#nQV7@{15&9*pL|{HjV#SGj#2H!Jn8n%_&0eh zvOL6=PuZck&zmp=lDK^xbKOxhG%1W}!vEJ$&Uh-YA7I?Rq5=&i7od_#?tXWEjYcJH zMvnfinP5tNgT8^%#%R!e@m)iuFpA&2Kw;OLkDM+bjjuMXzoMDp1$eHA<>;zSJ&F@8M9Jl3CJ(#U%niwRyg;wqjBJmeh~ zEI`WLKWf{5;tiHMAHV*a7f9~DE-CUC!YB;+*`1AFGx$_oZs-wsr1nvgUEtx8z?nLM zEAAtDK>*PomX!TaWAyN?9hI5g>T|(XAK^rcflT#M)dqNO{EE~sc)6A)_-Er^0Uw`% z%76yH$c;*#4mo}&9f6-)EC)%?dA@O+rLGgI@F3~6utV{OG6?iek}G)a3AV+{SqnY1 zzF!rPQuZvC`^Bm}lxG74z6Z?mRlJF2MLB$-G0Nl!k(9UIOTHd9%tfyTDd};Bvk*as z=lGfC=9Qk~FW+(Rm~fF8Gs;}2&2SQUFRkkkvHZDb41#Ivhk=>Y(`Kf?dUW$ zFEZy%3IJ0A&?eZEv>6};0DZ)QmbG@p^gRC|xhW4ADf|sBu>mze2nYZ-S{QFl6uARK zDpJBA>ikMwz`S`^h)7vFd^<{a<{ZF^cDWoZR#hf?BL#Uz_uwP3Xlp3$fH1lcbN{S7 z3~tx~#Y-z4l{TLY4z;@@`4_!B>@lfzZav8@2LI@*983#7#4e3vb>#11!9ieneRyvC z)1_paT?Vwvo?mz5-ihP+^C4FV#AM%7Y=t&zJ4X+#QssX)V#mStn7K^Dq&|!YAqJ)m zkf!{W1EIs?ls)$@F5*CZmk%DdHb7Xg z>z~ZAGw1)JMP^}}C{&9ODTT8K;l)3zeS?Np+O5RHi zauWDzGXUUlh48KZwz`6x&fO0@CT>`zu{2cn99ba71-2xtc52l7jL61K{~QbB#iSdp z^Y^6Uktgo`jHt{iLlYm#1&@@AsKQ=#AzQeG`Z)xx39^qYGj%J`>770J2vt$SXw&?+ z0H$5v;Kv>Qyy4AD+brVKsVH)9o!Hl#DX!}SJ^BTC1-jp`!BdZfE)ZYt z>|Hcx_q&Dua5P-ZoXCQaW1V2yEU3VFSO!#_P%sVu3B==@vN_N|1*epBm!_%bMf*1+4cD7G^rNCjMx_pq_aZ@#2 zwZ9ibPP6caD<@|M_?kBQA*tO*T4Lt<8urVa4i!?|xCPW7K=s?vIvvUe;vN9=e&?}b zwr0qkhHodx3?4)PuX6lO_@h7{b4Cl1Uu5E2LS;n8Y!Sq+KsIZBoevQi=;04p)+26N zyLvM&^6ELCo>EyOdI4y#UPPS?N!wb^9OGbU{?l)(V>lcq`9 zTv@CffC99ja>Hbph~c~of#fdJa#axB;TfbG8~*MQ4B?<^t$^ZP^Xw%c29Jah3(*aM zf{CSzEsAX2Vz4Aa*!&^2X;4oQBjJB2`-D4LQMMz93hIR(20}a!Emq%KzMy5z_%1r# zanlbE-VDNV;wPwt4`hH`nosZ%=D(*ql%8ME;$wgld>cH2GtX}UI%)L54J~L95;q?51llQZdPX9<&R3Y7dW?;n ze!-QS+QPC^g-AYl{mSYys6G6_^hEDyR&?%Rf%7JIX(w^kcnCm2q!UsaEb`5M5+Ifv zzp?y2a$K>WtIUGQ#l#sK%*XpPQglehUSW>L44T}jEzq}G$&2Lepkfua02bK$gn8bZ zA#yDlQdmh|Xk#P<9-zbBb^b=ElW~A1PzChzxgz8nM`yX24l=VVId<0|j$3pxa^@mv zeuyaY2=vYk=Ps0ub1{ty66W87sLBiYEV1!o!xK<{D{0ZFoC%QnonrK3WLWrS?#;J2 zXrAIitA&@$Aq1liQZn%Oy6o>3m}{y4$1_)$kSjy-V9{n2mfzEaa^BLFru9)IJKrJA zam(>6i#$n)oD0b7IX0wbe4?y}iZ;}Y45_dE%U%2glGc-ECI#}d%KuWH&lQB#kgTk_ zL25*o{aBgtOTfP^(<#VWi^#wEbF)LjTikx8f7l|iArx79Q-f3gV=RFsH9vXrpTyuo z@-h_pBUXbipekO%nKlT3f7Mj^eHtK&qO>ZuD3|l(U8r5$IOcii(?RT|S7dG+yTbyc z7=F<25J5$8(=Avre(!}PAiHrLV{CcCv;g)|{*DuGdGv2|`}>6X#_{5P8EoVTV*H7F zeS;Q~5GnXJ-@%O8dT5!ji;g#rwaC}%!XcLBqGh&yD`c#bCma*=16E zr=cwft{~m6F2Ii#`U@NJ%#7TosV^f`!Yu}~F0r}srYirPtnJz04Q^udThI__siHgl zT9{7sL^pwUJ}hI6g9b1lwqe5qt&3R$M*F(@Va zS&klk5o#Bi7)GfsA5y|Al?B=Y0B(ZU1^_U9#=~VP)}`}a8=>d(bf)3Ns7V_hA{&Jg}Njq0lHUEv+t*L9`*Hi4zk)Ty+Pr4Ov57vx3CcF zQZF81l3@CRp$)OB$U%LyCyKm+YdNrH*&u0>y_ND@$cgba{3UaWRd;ASH(=Z(P}1v< ziyLKU%z{a^e8W0*A-#&zs?IVqR7s z(;|UV9(>8;1~OQrNryWr*)HrcU_lRNq*$s|ict6tYW}tyz22xF|E-WTskpiAP!qtk zQYG}VVryl-hSCn1BSTMV3KFg}{<#L~c0MuA4y&g2U>UG5kCiL(n+-Mrz@oa8c+F#_ zujz{_ay;EZv=wF2j6f5fP3dG&`h@Fzhagphq^3|{;8J}t4DS!^;N7I^kL z3o9D@xPOfheWI;4-to>u|6<4&b>eYh6{gB-g}T5*Ut_%*mLclfhd)Tb&K1m9#eH4C zIGI@w;>um&rletwXAWx&_#RuXhb3=(!Sk+hMG~R!f*n@0iQ*1$f5hOxk_&#^ktVq4 zxq%0zQzakF-8K=!(Y&1*s!irns|}~>2dHb)Bzy3<+}J4Q$(+J0e2tZP zN{m|&BcIide=rj&?iPtlfhYbQP6GGWZsjo`7qcPC-6L&lf<~BVL)ox$vJiwS@t|;9irhG8lkrOROQu6M0z^8s zYO9=){sf+kb}LPv08RuPLRCYmGuo6UtOgkLdy!%DVu;g;zGVXBiON~6d3@U<@Li2> z7$g_r-7;+51RH`eSFdl-jed@tp3aSCz}-1L)v9>IU&C^^sljy??Jf5|78vx9d|gPr zhzwxIMr8h;xUN+V@bh=R{UgKDWHP z*%=yS0f5AE3A3MAf_^=Iai3}=HN;CE5% z0NDqF7^mIirh7_}#JKz{$1dy8(W4I$9{+l%-J*K9`N^aIAGKZLoEnkGEbr_=14K}S zYpu+1Ww^}gFI_1AXBX0A6c@jdSz^sPoIxoPbqi+X^C6mGgd{uX^9D)SoW98Jw2CM@1d-{mV9xFj#4dH@(gll@X^FsM zjy2^Jrho1~N2J8j9X9$!99AX7swKU!>^YK4gyk3`7_5dxG0KU;@nYjlB$hu2w@D;j z7XYg`**`hjDeKRM4TzDf!H9P#P9s=>drH|`O(42fEgnD$u z#Yoo0T#qv@q1(Y!eAHY*n&?kBAgnzM6VB8@i?mr6|LG}Uk7+u`3RfG9geWeMR}1Z4 z8$Li55}2I@EkWb|MQ=rr>@E=#&W-4n*<;dY-`B{-D&rZ8O?2l~l7ET8&rJ-he)n5k zS-jCZKKB#=HBeZPby@z>+AXgs9-yBGC-=5RG`|Y1Q#tkskn7DK#3w&MffTN^z@|DD znz3SNYzx7^46hWQBJ2kmWzcnQy{L^_r^h4TpTHqxnhNa6Tv5F7_OLQBpcDxeDY@Ob zsq9~YkoG~5zPx`j^NLkcZfUU$2Sx~SacsQN1ft3fwpV1VoP_}+k*5oNWfYbeL@h`t z^cv(ar|804khI_TbxTZZZ@8>H6pC9y*NSKgJo3#jr4NfPpk;$vbBXA+%Lw;62?aBj zY8WSe|IEvQUd((jC7(m80ih|ykRtBG=$kpnGuk%Qr(saU7WD=<1$q1+F-DMFl+tUJ z1VLtr7V#;BYo(lH^>ae)0_I};5DGK4r}oEO!PE^N_;Wp{rVOhgpd5DomBF!-QgZ~$ zq97a!Um0gSC!|9C%{lUbx&Ep)2t)EJ^yoznpMvlQ)5lTS_0AwO!-I6_)cc+0 zEb0N?E6y+?8N|3&+H6Ql4JgkkOZwB>c5`qlj6~3JoYG^}LCqgRN?Qq)h3N}Sf0D)ui}0bU zbpjvgg@~$R7w)uGIE=d*T-^dWc@!3d@jc?`J~G&#sZIjFt9Wx>S27XK$Kwjqu9~Y2 zFdzKFp0`2a!pj0;?ub<%T4k0$1O2}|xqr!KmW{pSm&QZc@4*Oj*`X$9LrDh^%+;gD zX!ta!U`vCdox-t`B5)HOkadvtI165iFF5t?=&+a4EiUsn$V<;Fpsnvw>QcVHwM%#` zTTAuFdn-?PYIPN#-X@s+gLra3plh#f4HC|*l{-|z6u3Nw^quP%J8I#~V{7_cU5ZKwZI-i^ zkam4lcj&>!30c<3-b{3nm`-*=G~S(3aUh!&P01bt&!G(-wpGwT2(5~CYYI~$7zZP5 zE$o9r$EY2Om;_|LhmRX?Q4zFI3p`H(0dvDGiayAECb=DImzM5~XRY5v3AH~*sa^4? zWl=>{n>0PtZhhW1#SgBe04K=|nd3J7;s>UygJx*{ghC}&U9SSJ@^EyC ziskYTHW;ozDdo(T*25!6!m(Xm6{hBiY>J?B+ZQ7P4^YjugJy-H+)iz zW&S#%I#EEHSiF+ydG%q1)Km(a$TI8A&`JV}6d+I7` z;NZ~~&&mjv&yw`iEfsh-iLfF$!%B&TvmL(vt0Dwj1jQoB^S$s-`Bmvdh?#22UBJ74 zpWq#boStAjfd7)d(@2Ide!@jppH{A_WZ?oa^5Ng16E91n&QLbX#^OKGa`kf;>LWTq z7KWh5{$b^XLXZ{_azg*JbBwb7?usE?xX7=6#~{6INb!Q?KDVDdqYtiWVc9Nexj|t< zKY+ye@B%a;r*LGB_b3)za`zuYG2_`Fau6f^=H@}SwB4nk3>`A zu>AMFhB6R;%5(lPHSS@T&C~1!%(W)WX=PTu8*SlC1R6s6wLmmZIvA^RKJnkP-0t-V zr4y2$faIP0_7+E$Khr1XCr`Ig)OsVvfn7z2{48-Pm$15z=U?k``0a1j7ZdqmBiUiz>2sh0oGK*`F!s+~xEoT!-I|eE|Le?| zO$Jy$e+%HA+xi#2X1bY9LB&p&8;z)kMnqg(w1RROZktdngYD>YFLowWbGJaB1Z%)g zBaP|qXu`C35Q(?uW!{~+G{r^|{W!?22u@zS3i}a=-~==wD1e<3{|VY?4KYK?pmmZJ z5WuVCHfdu=0nhQjX1q8oU&S|_fEJmQxQtoYxr;Soy5sK4=mxB&Emr+Zh@>CKQnjJ6 zy=HOwu*}I=)4%^!mya~m?vFe}_zV_(NK*hqyL@FK{p`st+GXo+%Eo*#^cs!|$H*L< zmAu8oiK@n%aHuP(Npb@yy`T?h(9_(j#Xxvc6V74CywL}ctQnGb;E`57_VZj{V@W~E zd&sV`c3tP~G3}>2_=IzwF>7zipTVV*FR6y`56q3j@@>!$7c8x+Y;ISU!OS^nE4b;s z!|Dg0dC5>w0!V&ygB!2?2$k~|e8}xa^8)TO;27UuR`#g2*>1DJzcN7+NUTK?fX7Af zE7KAPN^0iDka*~h8l=92yDCx&bVz(+Z@N395>Smv_7u3gfYY7X4>*d3bN&R=&i)gYEs0>V|<+}N)gVSuomHmr!uDP%Ye zDO`US;4f+6c#~~wvt8X?(Jk)mr+}r7<-fcmXY^B8A>D#m(l|rDMZQ&+QV4P-__jut zHcg0>9tf`9rtykZX8VQW-aZVJm`HG+1Jvfuqs64rr^+MYAO2$`T-UnL;%|Bb&0jV5 z7Y?g)JOYmaK7^dURg+BV*N4L~=4}L1SW$$2__s{p%axC^*zC23AjLChH!pHT{t{G$ z8ONIj6W&hhau5t=EPa(Li!9W~z(3%;kI>T;n89WO6TvO3?htN*e1uyNmM+fkv!-3e z5JtLPy+j(e7#7%%G0M2nOm$47F;D`?4V0{W8$<4jR1I@go}@H*;`Vtvvc;H6&vpEwpY6ZypNy z2ybL9nr|r^pvuP9e&j8j>#?E$IL~pjg9{lKa*s!T4+So)DuJNjF1)Rn1;q^#=9D4T zd6p*~cLU@5nlizkJ$JR7_2Z~AdE@)+}hub zOV$aT2B<-1xs@^*lo8b#KQ0AEV1Jb#cL|>rMb;0%K17+RUVfgd_*jhMHr$7c46O8l zSA(>cKex8d5B0Fdn$aMUkde-f0pBS~Uesf-r z8vBu9l_ThC$0+%|8S9l3_280o|2ZEL41*osfgxkzMV+&Ko&P)wY1=S z-kUvTcICMeXA~0X6VB>pQtt{6mO)`fKcyY&-%6p>Sv%9gQXRlX_$>)5ghHvx0GF2O zB9o43p+!~-PffX$x1ctN<%)#zHiNL(c(xb<91t{65o8X!>8rJA<-`38sTi_GCUGO^ zHWSJd0XdEDibapHJ+8>cG8L|sEiT6>O93i?UU;WfVOP`0Gr&sz=|l*R3Hu3P1@3?r zF`&?!gF9msa;%i0=l@mPaXRBof4t7hiPhA81auXyaj|!)ugm~OON*qI8=wpz?~DA& zUed&kDp@;)S=X524lpjOio%1K&G_to>U}6*ppJIoq=ZvL&1QcwG8L8vsxAzk^yCH2 ze_bECFaB#E0NTMEwDcTYrwFwEJYK@z{p1{AjZ=wUdD~P`IhlD|;P)lye=L8vUX#s7 zE=Y_^3K=8ya=09WRFFQGzR6Mos&!Dy;tx`BMKJf&$XA&|`~wMNAD=$;sbGTSJ6E20 zl@Jd*zt1VhYzkWkYW;O^Bq?7I5GN*tH zNY_B?w;BN`ygXB-h`7#1IG*_Z8>}eW?U$+ z5{ykc$G1qKh^m{9LY&McMkZnI#TH$M2gn!T5;ENGu{HsklY$Q^K3_}VDVMMVRd=GFWnOz=Z=U>z?pt|~g=Ep7!!GHB0;hYwVLCl%^qS5)%+pnrL^%i+mw z$nCCy00+pH5$5_z-nwfnTd}-aGr@@599VMI5Bhhxp2F+|^ajWvP~kAWpXv_PFHjL1 z!MM_)5#|`Z;Mahu{BZS|c!L@yfvT2-vgFi&*0iUBzye|RW?m}vvcb@l_8|rO75-*a zrGtwzqQP~tOVbJe{X}xH)-d8mO%45(g`|ma&Fb71PZiYCI6LZtsAV;ki=-b z{;?{yL%B^4SH>Oty@`=w4+B~|YSPGlHH@J>tw@@F~v}{;)0FFG__%|`=AI}SI zccsk4SH>t0$S!hlRpvUV$edSsuWzYEnxv#B-!*uAHx#znG+$Z@`e~1>)`@( zHXwMzV_%u>!JVLhe7m&-3d^^t8b6TS)_-8Ytrh9KXjolA#opYXR+9R{x6A%SvWMGy zmVtuQg`(wG6HLmzPkWNJXl zuBa|sj)RY}gZWqIN)|~U6Y>abUyHOgR-V=~ji{u-|4Av6I`K0pf7?oIXN}*@^K$QHUNA+19`4f;ny3vois7*& zmz8;;>KIks=TJK*e2J)A!GYq}jJbXs+yN1lNLaZ|J_`)Qg`N0M8(W zilV{?AC?J5E&{J0^39lmmiCW>V4Yu4lZWRXP9!iAb*w_+&uIg+m!!$|dP2M@kjlE$ zWq(jlJYZeOgF0hm?n1Lj8*OXdfet8Ak7zfTYr6@tR>pwsh6<=eE$4J|-BF}X-idy` z(gaQ>T#-~7tJ$B4Zp9(aP^-o~KWe~1|LY6gbTm7G;EFgv;39*~^^zgy65jfDO+BV1 z|7IoGo#rK1nQNW3@wVpTy=10zg`uf96sO!fVjK_mDT30(tuTu(CPzGbq(um^s6W(h z8%uHpLEF^&^ksofMQ?E>-HQF+dYYb0>FA!R1SV+&lhF z<&h1(VA#Dx>xYOzD)d%(F-*%&Vu{N}m`RbgP50}Wk)Tf6@GfY4zUm5BgxHOUOVw>$ zc>$y~iukquvem0v0*O$19oV!(ADU+bDPzoPYzvgp=u7m_uxPmWUrL6DT}OIk0erJ9 z;jO{%hjpGZ`{Dr#v{6ylm3u>!L_0eQZG^MaG6liEStk;1e{YLinjAYmEZmen)Nn=S zjH9qn{ojhn-J!VU$ti#O8ZKBMfsO26Gnlpzy|o)GN|*++9-QT^hYF26%_9$oeW%X7 zU|a*;)1$LBn70%_|7;x+DQV7YXVam{sHXy0BU=F&-Pf?L90n4%aH&PsFI8%T97jq2iJD`8Y0wRd-e?>mL9AKQU+b{$M+kf1K71kga;3x$cgTj zFyCRAJc~6GNB$47!tKCtZV~c21}J)Hg=m5DrdK2S~7+!3|d_?3=4H@e1R7_5R3 z)|BT=;Dmm5*%&iy5#>Yc|29Q``LJRvWs1Oi8GeHuAaCmg6Rtbs4MX9|NgQ079={Lh zt(q;o^^}TqUM2t)W$~3JA=)2I2EHa;>4 zK=$Vbm>Ys}n^F}Uz<`?AVLncrRmeCGwl-+3rt<4z$D7b0$F$Tr{o-8!RXqLBn)H+& z4xMMcSj9`MMhDkb8=gS#Db#|&?T^rV;4XwMjqV^c{TF&r4Dg>mMo?1mg25#c0Nb7y zGKfE}{r&h>J+hd~cjLAt^5*efZ222uVfy-E`7;r$<{9{pQ4Hvb1d9bS#q$H^5NXgwP$;^ewzjgF zpqpjP1olE;f%#FXjqwJn%Iv8bms|G@=de3XO;|;NszKRR zR3JDmKoEkUL1c>rLW0&tiVGG=L=;mOa0{YXQ&tHASBz1zqG(m9qT)J;)J8CgR4pz= z8$`600me$T^Pc?Pzgtm~d!OgKobx##baX5=r!KB@ibGTU7&TW?PL&5@(QJn`C+=!* z4e|U0L{*-*Nwr%zSrvgFjl0*SBP5~_%q5onQ8S;l;BlbmRP0z&SoVeCNJUE$E>9wW88xujuu4vEC z_>b_nJr-uc7$h~&7TH!tbXnhhq%-YX*vwtj7=K}B7m8A8F$Z206h#6kZ>J}a83~mL z*o-h%wTforKF7z*gieAMoEwiL=}jDCJ)@-ae?$Yalo;0=>9lT)_-*>fzTok&CUol$ zt>i=sSbp26E22Yz}yelb6|^h|`>|Rg76|Ov_~*F4_g)+w3^rBF25SgtpT#f2UEl zwkWO3IDMg-NhxL6mEQNiPhT;Zas*BLgoDGXp)hU4PZ2)@ZL0)u^%2r#%tgRh zj8QHBEOcExh+mB2yCqUroLA4-zm7}IX=A$A#4)T2S(7Hqn}S}YiiSUbe2>kZbl0q* zVq>WBw@r0it5>ck4>^J9)iLSVk7UUsv-H!vO$}SZ6lgQFE1@<*c*loa?%*oi5#viS0@4e!Z*~HW}GjFA$C#gqV1lh zSvMj5CYUX>XTz$$dJjun=)YcT_Rfv61mRVyHw|M@hrZic51R=;*-ASH_xlkdazqLaKD;)VJieJ1fdCj1=7Fhv-zH4*zW z3%z3&L>Q1*L75mMZxtR5xasC;N=81OLz;1Wv7wJa@DC;l(S!i}GQBjw&rNq3I||gM zUog$e)Ot_j1E`E@ETin(P656&;-RMz>B6~??GT&k(XeE6xdVO@rdoGbeJVHU>0ewc zhm;G3y5zq&kjhpV8vBe53rf58Wd2- z9X!rr0^LOy7e-kW9Wb@bGTwfbO1bb1Y0gANi_63j-N9KYKLHi+2!)a|hpr)syu&4* zjI7B1ZBPhQ9nygEoHCF+mNh1Rw}{i!O+ynzE)&Hm9kB|dkoJ0e(_54>^rRS-h+^@X zVp>;ujs7=?+UU~bEjeIcZxJ2{?oil5#kj9-FyTiyo6N_qGj3dkz@J6Qeo&f)@Bx(3 z8NSt1qLQq<{GnNV+|GC+c8J0<#`CHaO6vU)e2f5#HH>T>%e=3NVl^}N#(L=w_qdnw zUb8agHm+Dxo)9Jr7FA7S5P@x>3(!W=tt|7fGn|OIje#EBaz|pCXKvye-VZmmJzKJ< z3uWD}`2T(NKcZwBjWV8b^v@ldc{jCpaXKDk{5CVjP}f8WPtY&glc$w|E{=_0q>yTu z#AwJ<*fkh_II+UP?KMTy=++i;TuvX$rMBpoSm+W=WG0|2z~9ZQ7~nvCJ2P4zG0qh5 ztl6idbeIUctDcyY*=tF0JXv?@5})$10TQE-N@~qZ@?5dBC`MubA;rAYFmD*A%7nbD zrXM zDHo#0nS?VV=Kp44h}7^-pZ`O94B|}J>vJ0;mLhzX(*pqbraknCw`7$hCO2rlDtXFR8yH zmNwl;#RBWfxGnV1wQ_8#ewiFc0WC zN?LsDXY&>fq~O-yoWD&p^|5ZU8QS6%d{=ML%`$1}61X}%w+MfGGr#v}9oM=In(LID z4lIr)+G^Z@t)Vd9!b6!Z1oXXduWIZ}V#VCHz|xqhBG*ib2bpFrxh~-1v;IP!r&ze4 z>uD1G08}a8WJ`HL0W(AumJBU>jem#YpKo6+945w-huTBt3ro?GCfimsX;bo}S(FHV z!VQ(U-OG}AT%NGhZ1QM*{j_wL5_Uv>7<-iwP5lsS((T;%0kV`C!0X{!J5{*}j=k}t zQC!1de}HmnG5+Dlgk{OJ$M_D$AJ1u;%iiEEOzpBd$N9#r{2SxlCc+}xi4LoNIHSh zl}n`XpRdPsAHx%Ug{fIvxw#$=g_mt1qr5-#A*hevNz1l|C()0i?wmrFyB9?E|E8tf z|6~k(+3NIgd^Udieq_F_j1&<)snW-oR4`m$JI0O@@M<~mzc*Lz!QQce&Ng87$RG^< zj`8iy^CdIxBccEhw>~X02=$CQUL~1<41-?zlx{^Q;{=4~F8v=n*#)Z%^0TOuYEqM! zI=uf-?YZS*hH(ZFYB@*9!YU12tHcHsP$B zT?y?+J3Drlky7t*nM*x_TXqKTr5}l&twwQ{_W4?C)??Oq7j>2H$5H-QPH7L{eE{vh%Y2 zHI{k&o%i=E$}lBhZ?DB|Om0LBx>+)DqkrOHa*Dj^Qm@3VsUVIm=KB-ADy`5n_Shcp#x7=O)D@ZS*V1vki!W zgQyTxev0oO0ig?Yp@Xnh5XSD(CT}4N6vxWe2!{<_RDR*8(Y3d7n?E(pn9Sz@rwAP( zqf}@=qFW}8Dg`yD(!%Xv3s+%y?q0D@8w~B1Uyu=4fcMIOWl>g=YtrO&1dx1fqImo3 z9B_@Y?4c`?=V|ZWRM|~o6Gs`K~B2#s)n_LBXiqQ=%UY_jTM;F^aEbD=`3-PgU zGCzF)QP+tI)TE`j(ZgkQR5up0BDa&|N}A!HCZf-La?e%%2Qyr3<9n6gWwg8OCnXTS~as^d5^12Ar(TM*KuQW+1ML^+g8F zskyapRl|>OzlFqT(%4s9Ph55{StS?6^feN(9jx>nI)^UU*KJ0P>(WdO zbT?d#^=BNde%C44-~Hc|B_DN9J+^`)yWodzQK-T^jh1T1CQsQ8xwHX+!^GC<XOk+iH#>;(kT!Pb*oCyWu5O+!CreF1l|W+ zo6Sv%)C2y%vc^S5A`E+g7i6yABO~PNHORw`fP}{(i+byN;~Wt~2M2^cp?Fw1i`;TNz3HW%V0Gwy1C|R)?m+2)PIjI4vF^%iYI`lmw;q|!gkxZVKH*}d za54S?Hj%+%rQwDP`J@oxp^3e9r)`2Nx2JVk_X_xPBj22>q~*yd z8}9d!n~&%Y<(cK@A*x;ZTt3_Z7u4xGZYO3!KW#?WOn!S1R9{Sm9*r75WGZ5Hsa-_R z-!Nv5;J_nppgp#tY2f2L$8w@Ih-QDX6*4U(3|4EL`qEmb^qhs0t_ST5|!wHX*yi zjlaw=eAoXgY9P;{rBPwDTj&iCLwiWi@|I(K@TF)?7{mNZ%u!gWV8?$}eqBJUYP4z5 z-h+c}$b^Wx&;7lLCd&Vr4mToR=3;6Gk*)@v7a#=}kJMlH$htB6 zw(717nN~(_@C8>!GmD>`E{<~N}<{rE**wcgUcs1=7bM#@M3!`01eCJSQgAF4N zgoj(`>=)d|g829F%awso7g1-7UWXxRjH^Xy{Uy<#G=P&!(OBQ~s1e z_$5*DDwj8hSoka9&e(;9a?4+(vvMzlLZOiJIt5aJ5xfM}g|xjGucEhqZ1{&#nK>oYL@ z7-pQkYbK8g|H3=*(rhN}@@qknKv~KMO*iTa?c}O_;=KF5kN?$LHx+H-vnHU2oAf~y zw4Pzu0$hX<9<6VbcN__fL3P~TlgU8I+n{uY4M&iXfNecVc!Ca+BRkzQ#KF@=iPUz` z8g{hJ!N<3TskG~sKQLE*gH*nJI2Hxroza%KodL6NG}%MLU-~Gs7|* zBtC?U9nl3_xO>wx^pwHqaT60~0F|tSXm(`9qJiWu?<&SD&8+v6i<2A^ih*26$M;Kb z$T!PDgeHUo3G8+<8q|Mt{cL&D2lL4stgs#}66_EaZ-t7HDo(j3%FZMo;FbH*r&^&C zgy;N3WCkWt>n7Cc`Vq?3IET*idX3aGSy45Ha-e07rY17Y>NOQ92@s-t^1F<|7Jf=% zk#eq{Rl;5cUDP#j1B$XqVGeqrhe46co%NCH&>J^-?&M8w_-CtaHZ;;-ozHvE@g8xl zB=fPS`q^g5OR}6SjfRN3NI0p8A;JHG>}vOuaLd2z0ZHSCnbgPYIhk;7WGWrWq^A97 z!2cz0`X3Q=!jhI=^CKs)#TV#fz;%fYsA4xJTx2|zVPp;Rm5quD8)%_EA|yr*qcCOo z7nLLA*9Wow5N%{0V000?`%gPY%;DG|-NjAmX;OzYSz*Omj~DeJ8h)TrzH zDd)$T5+eh)1O;Hs8r3cU9M1x7Yzvb|Hn<#?CE%enhq+87cP@+z@c`L-Hfd#;cUS3& zBG_t>y~lcSuueX#$WM`O zX(Xly*oaP6atR!WVtu@=kiCv;JypzK`FW7@Yhlv&2Wgbxi;cAQY|P`|-qf5jtpOsY z9&^ZrqH^DzskwEjC;r4bX0D~W5nm5+gYE4tH=nZ@}p3w9bGy)wG4NA>%oKbVi5syY6YICgGiAjeh~ ziX}8B-f>BzJ^~@~M4yXQUF!WU;ie>XOCQAW<Kc?<)<55li+9~1ro7Q|DMJGaR2+_bv6qEoSnufg|$EZ zKyJ6nk8#H=@hIkI>1`!nY!SW*K%wj`CGuP*mWnu!p*%+T$D4<4uyF$o`C}a8Aj--f z+>_qN_B02{11@b6RIF3jh27PzbJfQia@&@?gNYq^DsoKzj|Q{GTQGKfll<4+il1R^OE zpwD_MG9QolwpYA)%I6j;u>HHGcLL@H4=gnUL$h672_c3+C?dAKg(JiR7ZT36RI^MS z7vkQ$XgiJQm~i47-LVE=Cbr=xWu?OzmL)ViHoS1tdJMe5^Q9L)Q$|6%wZ-43+>`f* z!d|&baHws?8iY04A}5&X#F^&Gzznv0r=c&>h7qcAyP>elLGbdZX{RnqVRuC7H>`wv zVBTG1H792dtJh<^$<(NV2UX8Ox5U~;yI(efuXZ_m!yFrWeo0-Xgl5PDrf~5`+=PE? zpC&$Uqg((tgR#E5lCf1mDHZv-_4CIk?syH9B14sTB2`Lkj>wIQ;2pSYN&KyQaLhD* zAi>l{T`S#8bm4u<5r!JSyQyBkkzjJz2gw5G2oq3#5%K`0yDGwz)I=HUX>e+uK9d0? z%-_JK`qbFcal|eh-p@ZLZ`z1y>${Nw_9PkaPfT}RchHRQunyTUuFJ~KSAL96HNugO zrzxs_8$A?D`6%zY2UoB~-SDlt=vUPFDfgxcgZ9ZUA=AG_{BojZ|V z#@^O5te(ZcIM^+r)?m5!?TO=-HiihzFVVGK;E0Iz(aCXYZ%6FDImfG?)lTiLbFTm@ z#jdyZiXC}4#(c6Ja0o=S(dM- zOP1uDMU1%H+$3?l2GUKrzfueyYe21PR+G`F!rRC2O^nReAkd~+Fizd&l*2b8p^$O6a$YPo7h{fN0`3CD5Q@h>f9md;2Jye{>Mdd z0hdl2ioc3RD1a42Kg5Fv#^Vf5aQ>p!*r!<(u=l9tBfvwtqOjk*u&L1?neOK$M1&a1 zOfhTXzhwU-LVF%=9DavI^9OUeJluTJIq7*jgLo{mePuFuw`f#BN!qp3QU_SNd zBh8oqWXBLS3GIy2AGIJ~yjM`ucZ()_H_!3}d1I}_)rr2OMtw0I%lriMpnh6q$dtbB zYBw)`VlEKahJCW59D=#ZSCt;aQXpJnGa%GKbca!{$Fph%VFS#&e*`)^qK$^M3={Fo zK%yVH^GDJANQiS@>NI(ETeTNhOwhV(Kj~DuVAiWHJdoC6w#{_xyUcao^*|TyWl#e5 z(2J6h0x1rj05CsC(p}=JXJy3SDqnZ}4(#*26-7dY@!~p6 zvrAh0L;1gI&WPqmz!ik);}xHJTbVbCZQadtDZ?5oT)@2_)sxLdjQw3!GPArsWWrX+ zpeVnE@)&!XFmOAhuut5fm}A4N;fLlvlh6h~T1H3Pb4S!cD6!rr(Khx;m(hq9^jqPR zkygPVZcldch*|!F#h}>0r#USOuV@R_{Km5DCfgY=ESU1(4Asu+ zZ{$gw3d&f1>H zaZL7WWqoodC}z3$3CF@@z|LpPM#xk5$)4RDCeQ?SK1ZkO143zsy%CoF{ueT5bH4HU zllobZ32hdA{mgVDPXoR|l>pN~*a)*S+kBZ!<)+_Rj_XI1Ps!MU1S1$#j7c|uV2`G9 z2oHTgN1{(v9H6~(!SmF(+<@868aBZb7gEU0od$F?{tKIGEag)x{T@e<)@23Gw*$9v zAPJMd2VE1%@*g?{IGC+>@q$t%hJ}GFm&coh2hXaKZUc#o%iMd9XlrnUL1&iOxlG(F z%Ir@GgWlOz{hf(OMUDT%RjFHW{K4O02!(bgE~ROQ?h*Q8@VcKYa~+6c9QZSfg0u@o zD`-a)`Hp%Z) zM*~<7VEZ4)qEOyHIhG(z>mriqs=o%JJ>|kyFl#;wqs_IkI3TKf$@Frt(z~o~^G8if zdDHGHDAiE%boADsFESy}e){j)Dv*~gI-Q@I$9tL%Uqd$x6RH)t?LPgSe21>wvI%0b zWsQtd=PF^~qY$({Z3`(C zhVpi25pDhgLLA4gdMMt4s7gHhM)>%ymTNi%E5Vydr^cbAUd1Mg`IjF^x!Ba$q5W~3x=OSo9Uo;7 z^L@(sLH=8HI2IBJXw5gK@oXXZi0au5Xo#IJ0va8Hmv-^o3Hs^a?qg93`vOS+vBiCVFxN3&1xWd*|5TXkoB^0DJbSm#FOBtG>}ZZ>PE@M zu+~S1s)(8Xn*_ozEU)}4A>iQtVNw01vo|5686!_j4G)RguI^_%Xultp_|>0K&+0qN zPyh;5WR_0}0l*G@ALlwP@UHfDzQto4$}TpQZh-+6K6QO$o;K~+t- z1_8)!e&SZ0xGhji zjo=l?Wil)sZRiQb;v^J>7Jtw0DD9;pvLaq#58=}`u|6>-)U(TKG4HL$sR_XDYW8$} zcwHf&fK_d=gM)Vhy0|eRhas%zPSSjNknDU`-+=q*cz0c}nQfj>QBT6vzn|E8gLsXCYRJmtuZ0nS`(2x>9#`=?3Y!wZIM!nhQR0Dz0(DY{nDV@y!? z#1IjojUKuyy0L(OAYwy5?_%S$Jy}%NN5++gnc2nyth^4vIL~C5vrXAMQ4sf-&Y2uU z@lPavZGnHm%n}Ex{#Ux~soBqDAc|AdUh&4aO=V1!5^4Ee^^B*_+1$r~E9*m!;KLga zrJpVR<|XAC%mE>$2!{V=IM~~ChiQ2#>ZWv`qz$shKx*ZO-^2(9K}C!1!DrEryM@2C za)_kP3QiE>TGa>y9wIjkaU9$KuOkaZ&V6!5B?p_QV=?USJ44*J>M|6RO2(ZsxoB5r z4cL=8%#s!n z(de@}h4(Yo9BU^tE{&R`GIcmnjH4|%e?IReoE_0S4b^I3r7v_*ujJ|xX{Q=WsKXd3 zA7U_PhAqrp$VhBGoGO@~pWrdw= zcTF_gBKVNlg?M<@ao@pu1zNH`r2owoo#0;>B}NLZ@{LaepVa5rKvBY68DhVQjUfhg zD^oSAf>Tz>Yj}k{Us3(zN5w^i&9nf$Ix+zYF7I72PQ81M zn0wFycb%ckku~h5PtibZO~&M8l@IBIw=*CSHyQt3^>w;spotuEj|(Y?S#v@`bGB1Z z6mtrFuEo<)nl)-FmOZUO9yZ zg7`~uv+hO!^6Y0#n=sue?3Mi}@*4g0 zr=S?sY;O;jxJ_=LkUTgZw`39eX$xh=c}ibE#UIoS4TQ9#cMxGCXs+H!#14=pMWWk& zs=&h%A1gq+vQ<_nJpWR++||JH?S^p#HSfeKT3=dfrevmb+q8vEH*%vxh=}?0TLMWl zP-@h@p^T#qULRG?DEb#g7!U~E25}N5m4AQKD64EOtCs=Rr2VoIKNq{&(&6rEXv6a& zVOt8BxO5=iTjKXLJ{f;xSOXl%)Tv#1c8nEj>^m{BFGHB@4X6rTMvYZln7W9o{3Dq7 zujnJwoBj*h3YKJhf&yH^LM-gDJNf+l)MBU{5ykN(>X*YOGfHKKzi?XCGS!n`HZED! zJgw4_ebN|Gllskt*f(H34%dgNZQ(c{XOiG;9f5eIm1A?Q$=@tP`}QL}lvf@zuZ&Hk zz50ii5xJ+Sp+(ccwW7XkTts^zdm*E^r5Qa_-_{N!!re9%3z^cU;;RPm{P4is)y!qb zV#UO|Ezf$-{+y@u^!BH`d{)taBR3uf%%^Ss+4;PAid^KSeW`ldDf*sSzs&+24AI&( z<7Jw@hSSdY?*}zjxJ0UpPh&_xOqjy6l+&_eZFMtP9ubQ_P6TSNbAmenzoGFs<6M=M^?;e7aX|M&qlm7m>2mASCS29 zpD9bOK_TCL|RR4dPhJ0fP$6ub+=%YJ+Axo4H54HP}w;4zOlW&xu zL0Cf@&&yWQA=UKq>R++;=rf6TQ*W4=EK*uwffnlGVM07z8Vb!!C4V6Caq=p3>oC&T z(F)E*Wp`1oEE43Ztvb`21`QI|=f`~h6#gz|;nsZ#0VTacAL!&5-&n!=;+}U5tABY< zrlJZRN?#%S+CNQZS2g&@wH_`(VOG)x589wbi19{PDkh7UvZui2;a;EPxb&h=%{`du zbDC;G{HobLdLpJwWY@}Gt3IF62-qp_y}42~_5Ugt%$pd4nD4xQNsi5JixpHNI-s(j z^Gzq)WeBo0x8kip9u6k?)ONx3Z8|OPKIuG=bRSjx3^%A?`9Iv%q5-ENd_y!x1}VV= zgAT>Ap$KOZ!iz6{wg9@=VpcNSY8|hzV3bQtB-6xtIGP*h!l%lUM2aScw)!8D%wX&< zNDNw!P;!6DN9`-O`aR;Pk3|T2r4|05b37ApsIB6M3)HmP7KB>s~)cLyS0VzgMR~`WA^*nXGwe|OA7AmUfZs<~qD+(rOky{o z*_;pii47cDzenm&AxKK;r!VP#-eW47i%7A?fI%MPTLV*J6;>bdfa8Z`7^pPu%&G|a zr!E3BpLT;xn#i>hROK6g+>9NqQ>oIfR5TU31mp(hZz;V6Y%7Xo)`SDJUuXsYp?keX z-_)PdM{fBMyNMt4(NlGUZE-%B%MT^Tw&T+uVnG!Qfq~w^be&Bc=suYfzU+_OFSd=Y%7SGvko+qryPqI*JX`dhjv?aMQ-Xb1~0YQ%#@gs16jj3ozx!drdIjIcOPZiw4dGC_ua_&Q% zr~YABW5C>QS`H%KKWGqJvgRX}>A)dxEuV~S3>o?mr2 zH>H(`6(DRyibQyLVRtY;38j|?p4B-sh>?*IO-8A(z`|&@zjZRdwnh7BJrao>91v_M zj)uYtXs}wda;qibhY0b{&!ooyt;NnaeqoYqRoE`dKU5m9U2v!p)AooVbRJpsepWPi zoPbnPfd+6VDi_M!b4H<ZS$)s9+yTR zT`d*I@|`yV*bB*BEsfOzy9qlZ!>;{aXF;L{&0s>>3#z?_t`Y za_gIIM1iyCVu>B+-8@k$?x;xgV-VeppLRNMFeTkrXG5Xi;9BK&vbUV!csVj*Iz3-= zZ-58BDM)i}GrWh7X){1mq->uU`-cz_azsQzR1UqhKS59Uio4~SDFUe}q?ZS$LP7$^1I${wdwc zwxbVWJFW(4s{F3~-%Y(@GT0Z-EpIbw# zBgbmjZGi7$dqM2;!6vw%ZBE`x24yLBD-A)Sm~?tW(+Ke8#e6s2&mzA~*mcYp(G)%H zKJ|xgx`?QD(+5^QQSzfV$dE&Q8JsT z+#V?$V_QGBl8t`sO-dVr$a5^$NW>*KMlmv;e&i5z#K@`Bg~f1XN6)l2E*>@D2BWZg zt75EQiCI){(Ta~Q4s}~~Co!p&M@Wm}Kb{h`&^o3o9Ck7_{yML~cpHKllW@VOKALw5 zO2c&{>WyvecPYUjK1DjwKZ|HNdAdo7T#8TXoEqKtulOFvZ%L*-eooR~t#6#}gnJf4 z8FSrjhQXf_%y#q*hNEq=PW6~=O9wDw-dWX5W`7egqMA;w&gNe2Wnwvt&?Ikc=P5(or5fp>RkRl$F;O&E=fGx?Q#Cc4Yn==|{KF$| zp8`TtK-@rZP!u-zBl_4l2Jz~HZlWr-o;KnNG>ei>3IxQOGDgvy9U8L?|ExsW9oLWr za~q?lDr_g+RLmEi&Qo~rbs);c-O7{{omsgY%u@2!zEAvd*VGcPCU=K?;}Rh4@`(*` zZA<#4=kv6xRo#+Vp@Pl0f7is@VQ%u^ilJO=ltv@v)>dPyZBG}z3Bq;xnO^+KSeAq~ z%`Oi%zK($uY}pXa;_#TUpNqj5tA%j|+XmXFE~-tt%F!Sr!Md0p+DQ5QW`4E-UlHe8 z1zF?0?=dEmCjIcDUHmA#cy^KW8=3XX+v1%Y37cF_W?R^Cc1aCMEH*t4B9nxgo-XTK zY5XG@%RZRvaGWtK8Mhv*bD(8Y*w9`ny@dCE0nK3eL^Y$JRk&0Pk#~Hg2rsbVTkSd| z02h7IM|zOoq32pjK+_KGPrauTxTy8P(6dv+Lh#HI9sEB2n-3x0qp^ON zcp0 zUvGq`bK~(^EmB3UiVX02Ci^|?71yRfKV2$XXftbm46)=T?R8maJOqhLhq%_o;+R=F zm}GsovuoSJG@iyv6}`8hqE{;3l54?}9>8?w+f$UVKH6gpMjPT74bPhTC)M9^Zbqz0 zzOlF#qTefMR*Kl1=YldO4XG}z!Yk*Z{;LkeZnt8Ui_iSM?gcB!Dm(>C1EAzE6@PQBSe*g^J(|L@-+}*X>=@S8aRH00+{zeHrGNm#13=!j z9@#fg3 zoWLJubt${SU4EEr^ZHe%V7ch@;Zb9850^4MHF3CXcI+xS;x7GQ9~oFy2#aMFRqW^D zmTfMdA2WSDq%>D-WeF+pC%)*ieztMWC(|??))zzVjPCmFs*w9O1VqUw>9P>Hvuq1f zHCDQWq3oh!=RKqkd;e@K znhkKlh;FfYpN!&MU8tXy_O0%_VKv4wP(AL+JJA*-@lBW~O7U~~D%@1?gFp4_kM)}) zMYECDx-184!+8L>g#9p`+%P|jNU2P+>aIv$IQ+JY62)-e;8{T;MXq2(+kvSQ76lE= zm-TbXwpgDokISH1%lV?<+%4bbvEOzq>(!k>`>MiShQBXDMG7Yrsx zzsaH+>ZIBAky*vKnxw?(0exte4*T}IZNz+XB`F3M>^Pg)B1bGGSJ%(efI{)ZOESZu z++lHx!v0p~@!wxT&!CT(O9KNj+!)voBu(nER;I*u6+YOk%^&{-3`{n} z@uSVcT05XjkeevY-pDJBA-=4N6xq4hq}$S9I)L))yOLd*3X4Tjgf)Y@F*p zGLje;uLyu&AD+!Bsg|kIGzZ3~5cLe$0!iaC8 zO!{frDs~<_P)cxG-q7uPk9F}^AmbwcVq+`^cN9<`9qj_)9?{%j^6MU~t1xUxDC7V| z0;ks2>h7X!>48jU&b`PqCBHs|hs@K%mN*9lXZ=ij-^*91gi8TbE?8$iR{Ak9u6+7OzO_I3D z5jJu~_!Ay&7@OxNi6Sg7TsVH*;#UahTK38?rb?Wj9JhH8#LtIN~fRak@9yc&nD7HkB{ z(H(kM)hw`!2#u~&P{wEguCD->LfjSG>V-|jrJk%!uuf{WDeT?o2LiUcxMBKB9W{RU zl1$^h1Md2%(8{R9VF!?GJj(7#mT1~zs~CTNYAmulo2;)2(6T9S+-? z>z~HL`#Wj;g`}LA#b*b2FmKzxavAG>?2edwozJp&a%QyX5&p25L1>;-w7#|&!mZd% zdLXOJl7CI-f8Tpz`N#=50uC$jz%lmswR*j#oCyN=SBHR7HnFxi71RWBGHoP`M*P;j zZ;KFArgX^+M<}TTg}W`0H~%W(wsdGJNetwyGh(-M;h@NS?ID|zvWxqi+-IJuj)5?bCN^5#+ z*+sCga))c7a|kBk^|Q+5h~YX~RDxInW*>QEKIZ%P$V0|%&HXqA96J=%(*pKw7Y~R6 z!mj0mh*v+CwaL;i&_0>`AN8ZzQ`n94bEf;2Kn|m&+)_sMb5EJRGZE%AOBZE6U09)y zo2qd5ovEUkUl@0GQOiUdj5qY-T4YAAe9;a|-@fN!)*54H3s16YkU=AC33q{(5tmyvj*n~uhc7Doy-`*KT;Uk|D zj||Zk8e#ZHc5Apda9Di++3vIDvWCYITpIcHrHC%;b7XaKs>ASaeqLFWbpzbcbkTB5 z)B>_7Qc7#~#p)O^r>WZ^xK%le!X1yRufO`Aywnw9ick0$Jm%{GKUOSnT{_I3ITh zg!Zb}q?|`*Jh>47%>c&Vsdq=x>fD!A+DiUaS&HL*yleC-0&Fxa;GBEhB3BEB*!6gv@eKbUkcktq^?G?{N~oeO3jZKCu{`rcfgn zE{8-VP5`U)*c(_nhUF&Bm~!Xj^Qe4c2GeEYR^1pqqYSaVm<8`L#RsHD;F^)w@mKIK z!Xd&Mx8@@TMXH>u=9({Q#82EE3JaOq*-^?BOUw4QGFO7Km)_dnb}rE|N`g)6bR&9}a4B~0i6~}lRSA~6la8eGerNkzE*rI#-#p#$E#;22d2tb^K zp_vHzWBdu+CKw(pC!g|0|7^qYe4^wa?al!2hu(TzdG64~4^ zF%}6z4N&~VKnr|Znllejqc?%cRw3@8VvBBZd>d#Z6;DpWu|Wm5dON?6Yjt_+`}p?F zBZ`Yt1R3)kk@zWGC_8W=;Yv4%-rW@k0*pUm#y@U>G0%>yefeMQPY@A#*eaddVv6yn zsTcBjRwQn{1E@K|rP;C*my{ynn}GS@U2%;wk;NDt8F#rD=W&ZZ_651aZ6G?%GJ9Vn zUSK-K9!ML3BH<0~)#ri~RE4JB?rJ{_C4C8I?|s-p zWFdif;J=Ee|3TlMkD0cGv&wQ;mf$I!2lh(p$GhhGDu3#@7pCuo#ewmbF6cO0Xf6){ zbkDbV6mhM8VB$TP{G2;$)OdtVR2;{6GutppKkvt#UBqYH?fOO5hML#ZkQYPGRK=tG zZN_RTQM-FEnjgJ-x6n;XNl^Sqm(`+F8dhIeOLh(<&P8>9hZ`8(}OeiyDWnj(jBRJ$CgxLGNeM@`2Z16#0Kan(4B7UzO1h_1?Q z04$hU`8+u~z`V(JuL(F_*9uga5jcu~br*84u;!Yw9jR||Cpx$_yg zVbX=+?s@8pPLRrk2}-yi`aS`?lPQ}bC<_CeYQn4S>We=MkABWVlKMRDuO@y+C!e!s zfHmeaqIek{|(dD_13L2NlWgFG1Hi$0{rgcDq)8;-6IgaGs1XYoDG=noE}86@Mq%}{=AgEO_ze8VN7{a4 zbUuzw_jYQzCm&K(+&=jsY~}Wb7a(yaa;^VuMP{_WW0kw}J}T@VM*?4`k8fw(K?CxF zXWK`9`HBx@=|@eDKK9!1i!92&d&>B+Q5+cL?COZk`HNx)*-+ z)uuBawMw)O#ilzVw{s~MAX%+UYKv`@kN7KWLjp^~x9V1`SH+qLJzhK`{Vra~m!vbz zN%#ng!S+&NNuBB0DvWjgsdI)~G_HdKoLN%BYSj2p2QPzwkcC+HsX{pP{`=S`Tg(#E zXzd7oTWx%x+y9#uL6!&m*|!;Ye+`*-$N&El)M5kv{e4Ohz6!nDsNpTnXAp6PqCD{H zV9nVr97v>%pq{gPrXJDR!{!<5Pswo8%^ysR2G)S(xSAd;_3B3`jT1-_pYP{-BG=h@Bq&7gZeN}Q@|q& zchg3+hH)DF6#>D03~9JloP@F2($tMW^dqMlki1iCi(DBpN>KBnZE*-tdf5osmFI0l zr~rv5#Bt=<#c@gUY3ZuI-2iTwEHi8z^QVqU5kH|rf)4~?;&RW&z#!m)iSv|@FY(1L8^%n@^L z!fH?Kc=;djPSa&7E#)grfBR1()%pK1_U`da_y7O+YmO1Qs;L}OQJ5~3Y@x^^tYz$= zT&=EDge@u7fmJ9SmqRkei)Prdk`8p14%d=u(YBNha#0iNl2{?~d+h!BeBSTR_xAn% z@%!WYrz_cBd+qgnJnl!>qK)qTu-(O>8a5JUa*@?|6(^A3!bH|<4qa-_>|FtOfzoy9 z!1G4&;tVi0AL?r^7}mfSP4P*&zysuH;LO<7z1ao=?{>P{^n^-h!LjuQH3u1cKPetl zi{p?U7T>}n{jJ3>tFT2-q67lR>7PW9_S_VXpA6PJRdHQDk7L0D^wFuGo7J6yB4>6UBoNVUEw%j zDi`*xP6D~gPNDfI?$KM-0)->^-%UiZN=Jy-4zEQ#a`74cWQ~3U0A`O~=?ikmSvX!+ z4*SVGHo(=nNdQ-lbRDEu!G38;s^IaK)+`2{o$cad9iS}Df|A8Ku-k%PtAuLE$qZl5 zWq2t4{64`=g>4RVTW-@sZU6;B;ikfM8kLKf*po>#pVP_Pe%iU*mulw&)gkYDNo#o< z@PFqwXJFMyx#Se=LA?qrX-L)CoJj$I2JQiBsy!V;v@$}+(;4eCrfZ{Xy;o9I=cvZn zL)e$vJj&c1!f`RrD8X-Si%!Fk40hZ=`mFY%YbGU9EHaWryWT^ez<+5Ex<+jQi`3A& z5_(=Td%|gGF8(8!JSMwo`SpV8qXS=nEY2x>uE`ou-EA@o+RpNzGydp*7>_Z15~4Z~ zZx5IHx{~m+lC+q(A4_NnkuVaU4 z?g$PtjDieA{hp!Fss*HbPgs~Ko7D-y>pEEaLjmgtE46Y+Z;r3iXi`N=WW_CfY!L{B z?&cHC!+nIM%rIb{g~&DRK~*elaLBlKq}3vY6TrMSL)@hBV|O;{1NPi3>cF4-nipbE zBRU*736-XIxC1gn#x<&8(d9jAk`$x#c1HrQ2nIf`+bJy&JzLyluwP-Z&lLhnAhoN( zwurp88T3G5xVs;cD2B}fV`MtZJil2m)e(fO0ju}llU^QHuLRqsZYlrE?Fh2Ytpp~J zm&{|eXE~4RD}x*%8dx?7+NZo+@`;m%1l2ZfzoTX#?O1+WCpBYRYCdiwBc;1S@DCcf zziCUenv7B^Oka!MKw8?gAi|*)IS=zrWjHnB&AhdFKiiA+uu`jNbtCh9RZb1~@0$$Y zS1uogDi+x*ZH93Vy^8Q#4;=yl4PERtp$pjeK5%Fx7(fW#{$%b7nun8kCZ{#y_aVU; zQi;FAo;U~M*eTjrbhGUEFMjb5JVuKk&VP?4Rf)y8oLSKsSG)W#FuY+l#x%fd)15s# z@(nmFoe)zYdp(a`Z*t&vlkw_YUbfmgp&o4U4$!q?-%mE7_~fG#ljAV~&#$}|FIUcc zN;O)R+F46F&NCmT%R%c4q5qGHq=vlJ;7Wd4w6t1TusPq)%^6U0c1gtkqmqfsV0c>s z`uq`fsgy7o^wTB7n!Ib)1_B@XL>OO>0bVm8l|vob&jI1MY0$#wEU3r23XRQx_1CTA zgtnEOCbeOr!JwTbS?^v$B9D8`lp~y9E ztM-C=G>moqp8Ss(#yD`wFRlKbz{?K9f%hz$6z_sBY=jXYDeQe_s3XeEaIr$SWNQ@M zw9h&ENj6oI;}&UcM>RMXaUeX2nh4|Tl_EsRjO^0D^yHtNjC5J z?n<|-t_G@Ff8S*8ix_Uf`V8xbA=ZXc!bY?_Z2)69&(cdwdIA4lW+Q&!liSRXzG0P4Vi?ANutHJ~H1N?|gySB!?{6*{_0glV$yZJN z{vmHUo~p0|k>i&RYqZrimcb z8QsTv#$JpRL}4ZCG{pXA@y;0zv7-Jft>sX+-U`R&uQ<8r9qm9ka~BxV+-o~W(Ck{)5Y&^Lh^^SD3~jI2*a)Kg3T!n!my3|M*rq~w&LPC3G^e__YYj^j zSBa1=bDv+&3tgoT2IwG~nBljA+#H~1&~>vn%Kv0#0rj-dv-Lnym}B7su*tND8Oo4S zG8gcXio8Gz-lT%l0K!1J5bh~XG)y%(A~kJf9u)A)U?50{J%dg6^ROe}*IiU1i&mAN zS#TFhN>WY&s=dtt;8fdJo1nX9*&5g98zDCaTkbF~iB9u6f0!si2hxU@$ z?dQxEu=c*^A{Uv3T(wbk**8!u^s5e33)O|2GG^_Rp^~(k$yCD?;?LTw0NR>I)* zDUiq*#eyorl1fjAL5->m&E&SMRx5Vo0%oM=EL!ZTne3^*L)=g zVbxhX&l}vbhP*^}a1_+BLAoxQEMQfGP0q|PDY$zXi3|ALyWo^B-~_UV_*QFqwH{?8 zTCJ9DlO4g{8jZ(%QS+i_aR1cQ1Ap``;F>SPtPCDo!S!6eeZ)A;7hrx?#z5*#iP{3< z0EUrZ8*95<@`+MrG&~?D|k}xSd;_c^$WK;YxAA@d|pTJn*EMlf7*JY4oPr$>P#m1X`Ry*D=`1C=vYvU61Hnv_reXzg1e=o;piJY;)QQ~D zOl4GH9Yb)1t=K$< z=$edD2c$x6ejLH^LO5Imm~y=3*ccA*`__dVD4CJ}i8Fwi%@VKEigAEzgry3NOvE`A zh9;t;u!a`4R}}cj`AJGbbB5c(Va7N=_7h-MyC4vBt%>AV_mscow9!un-7CmZ)7h7w zd}09~Uf9s+oc~lwRPA}Lg=J;-?neZpBLdvVE)gi|{Zh-o1Wvn}VPCPdx*eC%e&#wA z3F#nLm;k&h*o=fqAwy2z)RY2FgBBAvm>MrYBQZef<$Q-b14mG}wH8tAWWFe{4mD7u zOEu&{km95(ajnJm_y_ax@4bKoK=#o4XlAaHAk?M!j+-;Sa*l_-){2*0yAVR{|Jo|3 znWYs5D?$PBxVtdrRJ4n%>InuV4y-qQAY4`iD|xo$ zjR|09R84s#GQ~qyMJJp+Y5WcfXvw|siTGPMFmLbr#rpKC#@fdc`6F2Ado?J?1~TMw z0kdORE4`k8WpT5lfUvP#i*Bk1H-tHNK$@U{f!}MZvs);a9_^fA+y&_x#yZI(_@2#6 zj*rxpy(|G382Kan3cb_uoKwUt8Is0!d?mf4pRgx`iW7pNLFWqY2(Xb#Z3AKYA@AMs zl%e^>$IMHjJ=RQVf@=|?Om-{G@ zVs}V-m^PrMJFpJ9Vv2;5xVF0ITB{^Ec7mhntUV6njcRnx+ZmT{-Y)qo# z?rJj(Ji#gKiJEI6!;$zwlT)AvLFSXkNkCUf({JvfIJb~c^)5IGa+%`*5ir3)^4BJm z;+v(i2KD#MVf7zvjK_Tx@ZhmAojs%hsOK<<@ONJVi4ekeelBRU5(ut|xbvy_A~0{p zqHk5XOo$^i!Gs%RgH*U0xqqj+`Og{;X7n5+pdS0~FZG%2EIwwYZ~w|$ePHl%08%~~ zQgoDl6lBRY{L3C_?!jg4gIu1(eED6!3$5oU;QVs-q}WEjOe z_RLJajzgvrZvu_f_wZ~gK#B^BoSGQ zV);wcz&&jP`3P=_{6j!68R?`La*)?9OT@Pl|GZA`(m`hZsPJVl$&>-aN7m>p?$t*} z_UoHOA7G0wLU2i@V(uchnFCXVCXe3s(@tqcNoee2gqWnt*4)DFg+0`3_JFusBlggV z-w@5~#7hgO3RplactACS(&r{U4H^hyR6MdLaXcx$2lSWI-mx%cIk;LVj(zlErjP8m zeuk?)erM4qu*uk~u=-oNkHRjLRt7&IPK>j5+#z}sMuWRxJwQ*i%p5+X6iqsOSDRPd zT<*C?Wdo`T+t3_sVU)AJh{)KZuE5*Db+Zy6Kv-d{g-Jwnw#x@~ORd`!E+q5`-t_V= zNC~m8w9AN-R;p3g;(t(A!)w7>@%jSuQf3|nfi?qrufn4gve%Xl7%CUkVNC#JT&=LU zJ0OFmB1mBJ088dyUi`8dX zh&E$-7B85&T?j4{^&cJ0qPd|sqJ?{YBVfu>K79F1PTdn*q5%Sa+wCR{+0<8c5IW56 zLcGU(dldoK0R~@K_jL&o&+kxHTY@w}X!fd)`Ut1E-d+K!4Gf&0v_Ulo&ilZ6a`YlM z`t67?352{f;)^e{Y{LG*As}+VG}ij=kg0hE@kk5mTlEmCBy$GA;6I^zV1v2k76-en+h<_~{)kwru{}GM;Il`gr0#6}iG>}-}akQ$K2HD|!@L0TI`iIKXQuYXI|lsTgk?*nZH zyZykJFwB&scisT&Pj)`h&=tZ4^U>}S)w%xAdb~MuF}Ek|U5}n=eE_F#uNd~UjaR}z z{+yN4Uf~{|R$u{e^Gfxa9GvStKcFZX1#)pRD*~WM^6mZTv^4yDgTuaHd2s_7?PZI6 z-bR2%mWA7^1NxPub0A_xqvqqkdW1~}Ew;tPq?I3kGU~%{@di#aYVZ<>180nn&9LFE zqfZ7m+PJq2D<;PQCxtLz(MY_LD&O>*-gqKX8Ee7bZj@~QL2YFs%7d8<@;h+nCcaqZ z?W=hNUQ9!5c5{)oZ-o|eu+s>3eI@kMN{`}iLPWjnURkkoF*Hr4!T~lY5+za){GBE0 zP`>|qRz($zW*^iAXFYnupIPBC3km+af+DSQ2F4J;diL0YR{KuStON)ULywk}lK1MR zex|rCv?e}1#SQ(+esO8EO=za|$CaO44KkW9e6>bf!N^lCd7WXeq+LiUpV|7 z(p{8u=LLB%1&BqcrzzA6dr{3LR)+rlK`WGD3%^n!yZ5{jO3SVuC$ zT_C0d$g;p4%K6OP-L>9ca-nJa=_e5Mc{)XCPdMaNjd!>RD3Ev0H^8LCOB&{S)n4b^j zL(FZ!h)%GqXa4z05(@yUV5`IK+U3mR>rF=5bpi_hDx0u$;BO^jasoBGE4c>Q3RhX_ zjpTS6{6~js5-lI{ z9!_$?=I1GhmEo2|LxG!@ulf~)8pb1*VUdr3C}*p^8dGhHAd*;_(2 zd|HG*tDBMv-&Argn#U{)=N5ohEVeMDaw%HhFEoKdyn6C37O19N>Vx*iMbsS(;uFn57uIYYaelzLlu*2 z^?kR|?j)!u_=NFT=7TR1MT0C&|IPN#wxVD$j$RPxy0pNy54NpDP$j56Vg50P2ZaWD z>>za0Bqszbq(hnz46`|f!EX1~<+$NiXE^qf?NsQ%+qNd7?iRWAl~oTk$LAn^&)lZ^ zK-6j;4XSepqzdPXhq&&yK%-8|t~L?Yi4k{Gl(kotXd^V;I!)}+QDgpAeH?sNb9u|Y z>BPGrBN;a3fr@?0XE0*$Vk+n`~FVlCt$LpgVov@^F{0nlfDt zuZN`-%$$b(%chGCagMFH^L5#fJ^yfNHAM4678)%a?p!!gk6^1oMUJ)S1$dI~XubmT z;KJN!?P;M&@DZA9I(6U<{~#5iIR64UZB`9J2B%=5{fm9Ss&5Sf^9AL>aG0!>2f{2p zfwlLxdSdrxfU8E%TKpl;#j1N-GiJt!fXMXvCR0LltflXw#hTfA)#Za&P;{}cLgpJ{ zcR3Y?bTgfp0icVjJgvqPGMz;5O!Ox>`Ylh9&Ih{w4wnJUrGrleOyoPgcb4_QX}rOk@=)UH#yhUo9b#U;xXsnf?_o<8&5!1l;pC<^+Mi(H$+)kVzc(xV%jsXnpi=I7!S3-&^j^4 zxf`Cb)5`fggdEz}1BPrH@Muc%o6m%@k?}G8VU~iL8Qf$C$nE4%TF^YA*fGP&jJKV< z+py9Gno_C{ZD$Qt_`+!d`MIkqyu~IEuntU|9E>|5WTfh9{Z^eb&J{lB)CpNO-ia(v@s|8248n(ZUx- zf-|$8v8TXcdqgYBzRbQ^N*ta4l-ZGCA z7LR!VVcU5sR3freK#oIu8G!2AibR+^U6rIv9y=GB8Ek+XLM`^HHT3UrOP!}qHEk8F z)u?TTgr@4*k|H0`eEp9C1K%mAen^H~aP9y^b3po^74&MNx!yuAn0DR_N-im(Fntj` zZag{5B&T$G@ga1Psm(iEB^!s;YXE_9gti27S}Q98rsqYQ3L~wTm}l4HktP;Yl-$#T zr4vl^9(3a}p!Mt-527KAS*#y6Qo&BE@$l=qE;OARdxpo>N%!Xgzun?Nnafz9MI%up zzGC+@=?2DPAV0#^^B*VMB1!EPCprnUx@i7@M~bw9>|RsKQ5&n9x+G_j%!)0sY{wg!F!8L&F}vjNHBGBjhN;7}rfCA?8rgS(xXq#i*C{#C2^t|RO=K!=)0c0leahiE*{ zkA)GIpvY2wEYFjO(W1{a2TsSv8<^ze$5npD? z?xI)KB2N7je8-niPyA_HmCaSfYi?uW~YqjDEk^#)JGhrf%3ONd%zzV>Yil~idXvZL?zhRl~-L3KNa>eSH; zOFd%4f#m*L)&)i8ZS7-oZHd0_|6&G!7cDa zn1Rw<2-e_F{oTy7`2fVtjiXwJ-w1RfAeGbdK4|ur=sq#rDvglRoL41@%+0J?rXpZcEXt_wVk+K;e z>`v_Do05d%QN@Ud&$S?z-B2zU*C;JgyX$+#L`D6&pb5~-9fF;uA!AH@= zQR%HrJz=ZFZ&qf-VUdd0c{l8zlitFau>i$1l2>aBA7)IgF@hd%eX8zPt~xVfOY`a7 zAW7+8fhK-N13}pVwNjmWtKt?v#Jluy$!%>#6c=KAd^HKcxGvH+-0WA(+6DndO@>YC z2artQ3c28=VBb7dco|Ik0HTLp@1eZuVk{h1?*a_pw0232>pB6846YI zTT`yfcQMw0y}`lKdBoPfqcRwHDCaePU9FpmcK9{`I%fHzdjsdwGU*R~D3ICZydBl_ z4ZAOu{M39(cDp=-S!7#il?LSnnb5Qo zU;z3A=tvTpEaHSN)y?qn(pXY5xS5D z;pmOnmt7%6T@?3QuxRgWM?;oY!xttFq)u79EgbrnN4=pj1Hy3+Il!IIdu%Y=WUxcH z$P#CYZ%QIgvf|q1QC62h7Ff0tZ@)u&T>@Y3Q_kncyB^0;9FDqoa3paPVPXyp!&6o^#T-)8KWm_S*Z`CF6)StyVXzY_-??JG-i zTrnpnuss#z4j;t=z3OXaDU&Lwz7>x-ToBAv6^N%fXpoOg5U)k3vylJ>{$NCQb9pqB zW~;m1->P5q!=3oA>`C&O;P@CNvTCzp!7xv-ToLfVw1;S3yN~tQN8(QM0h>x@Sfpg% zYcB{&H5L2vjxkH@^b8Q&RNh(v5phoIB?4PCMSJ3BlTq0f=*23Flt+Lt@if7%GCT`5 zMFSHuYhA?Cq8^IJFx6-SN3jI^g%5kNU4H8cdoI1k3pxoZl7K@*&sQo*C*d=*mMl~a z-5F2~fmQ|pFn5YO{FTHGG&o@t2XX*?^uYPIRELV_o)vd8exIv4-G1(vN0=3HlE*tz zY7a^3geL2)KAccR7`>1Ip@!4C!a)YdOJ7E`#Anx&SNrzqBh-!B;2|Zs)CsY*$Y@K& zZfl3n17g^(FF>$x__HF|yFkH$97e$t_|)@X4e8L&Sp8+lUNZ|?ZAJm`M*|j8K)bdp zRbN4%+Nx9dgXX5G`WEi)1}zK^Ckvl6)`WFHW*a=Y-RI$c#)E(!3Ale!v{dr|8d!!I z;CntESLqF%=&oXdZS6b|TOlBmw2G#fG~gk8F>z*5l!4(7LLCFLo>t_ zar?utFTAzzaCET3zUC~z0tT1EI;asZ#1*FsF{T8!EZ!y_>>7wuj<>bNneJWdKzjuW z#b5jhTm}zTlsdSBkO~NVy3bMK!dx-u=;pBtSS|IiP^`JGlXMC+S(^K>oa)V=wQklY z1v7G75q7q|EPmFyCL?#{tagcqYT73QH)CH#@MC1TH<+Ag`+3w?c+AqzKd>(!#8xc) z`5XYZb(>HtycTvXKy?onWG}A-Vo-rNfqPDwPkEnDoa{mim#zAWYu$!Ls%RH=QXA;s z>$OFQkiFw?yRKb-r|<&aDPb^ofylBwbWbjQvY-eSD3Clyx|GQcw5Oi{)^*fEWvvWz zs%HkyfTs{Nla!Y<@C;yeCZ~D-kj&ASC_o9Jqgx33zdB$Jkq(!G*@ul3`%)=ETFADA z94*pY-Vx<6BU77UW7HZO&r2-_8k~1jCw~>g zp)G7OTntmV3%(3^&rrF2^aW^Mg#bpah|xte?9n?Xj9p$L8TA>hTF6|I6Nd6YNLApV5}W|*tLst-~PBXg?zC=VCI*}mPr zk8JE%vJ-mT%%?hMLkx3147x}?{)XhV-Utz=lbiysi5I;T#+dhK<|Tu^&<9AM0%-8! zZBKX(Xe?`*$dUgoMHCHh?9e63J5Wqy=v6xh+fQQ;0%%%?Zck9_S4PABU=F2Wdi*=0 z74~xyFKbcIl3nqb8Lfrkc&!#%yCDwbuucZ`%reYVw#zi=!es$~F?kV)diy9F1Nw+l zoc89h$p@U}tL`w7gc>IK%AClwY9y1F$My(^^s9R z<*>beKzLMWELfzUqk+vQbO3J6n9jN%&OI`lIs=PYIt&UMVicTL81HS(XYQ1*mg?1$ zl=)~tYxRRUh;J!#ix;-h6AxZ=OmQU?ih84oo?t0)&iISYmlQxk$6;xrOyS=}G_>CV zGF5J{4ZaoRJ9ds|vNWzb$2v$*SjQMQAfMJ*#mznhxG;1l&4J?HF9T;=u#Y3WeVdG4 zJ(3hC^o5)RWF`|y+5fLyp;8Do>veYM!ik5$>tvJGcui{NaWOj6!eIDghv*aOf5!0WP#ZDItzwyBbuCEF@zLDFdszZe1! zcxn>(lAQB*<3+KBz{>f0*@&TQ>V-aOdfteD;g^wxsx$n z!1C-uDFZ@~G(0dl0jguL03oZa9jjo+6i)(%W#!cqx#aj$r=LE8iF7tOtB<~rw%iTNq!5^L29M)p!I>+DYs-_y8Ze#|t7pvD%F?Fq_cEg(G_UaS* z!kNxWOI2Z|!uSfDgARN(qyU3J%P4J?d)@Fo>OY-5jAc*DGiJl1E`JA{aQ{T^XW%-~ z{gz}Y&>=MeU%KdCapB+`VFxAZ@!}JG&4&T*7(m2>*y15s3m75&Tn%VS zZw6LfPeyDmg|cwW@@P;+IzSkH~cKDg=tRD%zbzo{^8|m)xGn1TnN^*7BlxB z@j1ts;B)qwkI*QJi+a%JGttG!YHN#vB@;ZUk6aBF?88w(f$h2!+$g2o)q5k@(7cv_ zLP4h~Fb2R`J3kAWCaGZ52NKPA!RvBxcU1uDdwzi^=JOt*8=;f$B;Rj&xN}B07b)>G z9+bVPW2Szf8ceBYQU(sgGh|nHwE9S`(>!>Zs?Mnv3N$;QQn_|XdX@F0Yn8qPus;xT zfYFCMjJ!PF)K-30)8Zz>=bnSA0_8YXCrl5-)y3660j0J#G1ErJMuH!zaCMB|uy1h|94O@=B-AQdVCotg}; zD+Agk?!7c0B@&Wliz&PLoQPRk-OODrdj&4gVO460dICpj4@#1j3Vbvk1`N}kJm_RE}!rRXr94y7pA1$7-h9cfStI8Hv!@Iz4pAd0CmLpSUdJZnaV1eo{DJ*cdut| zAt1q#*u=YKXhke!5KU1mxC%<`BBAlS;Ne8pGEi{ZZgjza^Mt5HBRM~Ud|gg$GPtas z#-V#A(ID6*QJcY01~Z}77~v!FIC3_q*`Wvn(6OyrX%g)+Tu4HsAL}FZvjY{InWsQj z7eG&av^d;V1`kbSFa4iKItiw+`3%gXZ@PfGkN5~VKy4rB9*PD2V&f}NeNj9ecL>Ni z`~FZD6gjc7Cw$UR4bol$DnU_$?A?uFRep1!1}VNFHuYoOO6G(I0W#&IJ#a(nrY$&x zEMJ}96TTA&4pWGm2y&W-yAMGjMx1~w+aw!&AH~0!xAE<_oRXErMHlt68)L5H%m6B;7V)8w#FFyFR?2};vXxBiuv=Su63#(Or>|iVm;?shhEEvOTJJ`#U6y5|+ zZ^-TuL!~DJnmv89bdt*gkQmrvRbR9xs77a5-QKbj{d6%h!CMy71M}Qhwi(D78?PHD_Dwn=rG^eug9rH2iTH7YPDS6wOe%!Y-{E zGtVOjOj#E z_C{>JxQ9Lk9IyiZwg%ZsPH)(1B?4~ig|VeG@Y+xve84+JiqJ0FMFoV)mcA4RyTRI( z^j5t@ldS^LJHq`g{rsNh#8HHTHVmmhZir@|wvHK)Z3ZVs(#ku9TJQ$UnZ#A~$^w5r zzXLHy^lh@DPwycDg&r!<%}f|LE7L8!8icF>d98Y7M8W{{6xg!z1#TuM(HQUqjXd^! z#;z+0v(iV^6?SF@T!pm@lsY`?+W3v3P&D+(9R*VYj0C|wp}*t4^Z*Bs7`C- z&)SrSU=B za$$A8z4}#jB|ZxRrl+%}^^;lC(tt#H=LuG4*-d+WG|jy}l8U3)H*pLxx-ojH`u*!S z->*Nv?ced!tM2Qa(ydF+oxgv%!K!%Mqzxh3;KxsHhGZv;9Kz=6ywmU5jv=w_OVf|o zk0Gz_olXC?D%Jb5_OOBUvaER5ol~RC$-Df`vX0=-xm2e0lny$De16Rx@g75-WJZi3 zAI?S%a>75q8G|UeF=U=4V+=WY?)Yfj{W0YCJ0E{-e41uGm)rEPZs(DaiC4ZSPG4%D z{_$VG!}QmL#RrYkT28z}Nn^-q z6Zsg@5Rv`>t&4p>>@s6$JG*qGAY@p1=+~NkPev3^)3E0{#3szq<}u_5Ax&8~5a$2i z|Lh)6v)ek6#9t;{D(M&mS81v!6WsUe&v*K~Bi$ z7PwyT|L1zaD>~x&OIbQV8AJZ-RhJGo{?|KuP&nc;NqzhEzuwrt{?7k;WB>O*^FMEl zGvi?Id}g+wZnyv6Psfleaif#dN3IjXf00G{-<9^if7ib+{PDsOr*~ndzPA5*V+(&N z)Bp2=_;8o_$KF%_`>oOca~0v2fnjM=d)+ShjH|wH{m*ASf5fNm`?fKpoHK?LjOuR< z_!!R*MtwZ}w(hA~^vfx#3?D_h?uhDjnw~A-ObOvo&7NZ=h=(Ozgs*8p4jF3W>H z373-ss~Y(0vRIxlWG1Ld1L9#-lc7g=U}iHKzrGp+PejXJ(qRF(Ql_pScwO)kJqkUQ z=QhB&t(jBDOdLb{p#oa_TpQE3>VZ}uh3VmQG7je5CyyY=4E(~WT@(V22|EPY+E#7JzDS5$oQS=ECYWNF;jawvYvrDL`Rx&~&0%_JTd?rcR?Ks1(9q*r#;$ z!M1r4CmvM#n$s8H#FvPZ+;OrOL(pOZ8t5UNOi6m1Py1H{4;Drn+wq$+CceQoy;XVc z6qy1aXt2sh?jc~i=K9DFdqA6lqsD#p9EX}e0YffRtf}_DJ)JH4@S-?uw!;9%x7Jlu z2usiBOT(&6Uex^8`P1;2+1qkz7Oq&eUwRG(ov&&8KrAMc>~dJ)_=kY&r883! zUTlA>Ir(c;c$s)E{80+U{H!q<{G?bfqQsmZfcht!xlQ;@;B*7+( zOEt{98F4nHhvvde`)4U1>GX}5$9nd#d_E*=KC|>;R#*--tgv4<&gE^oix^sb-%sti->@i}bzi^-rHsn&g9m@@MwP>{LvVa>jNZVA-q zKH_7dyd4F(`zcBLDbE&D!3H;-LU=>?Z7;AJ8Phs^(T&y~WRQ!8Wa-4xgo?xvEb`!9 zJIaNMxn__*x?-LvL!|~~fxNsFp(!kx4^6FSuKTGfgbctU)xOx9jF)!@u6EYi)r*h@VvUgHBS`Y7w9ZG67!1bk!9`*~7Y z!tt=P+gxcz8yE*W&@GS$abrEu{6E#G=yJ`;Z~u62ohUjLY-a1)5bJ_qdQQ3;>Q4W% z)D7B&@&E1f#BO!K#*{#b85>C&#=ty}vaMYf1{&iMYUE^zL%x|q@fdB2|3T?s$1 zcj_cJWf{w3P75vjN0@KRn;z|w+`EzGFFy_)cIx@#*o#lCg5yDhzv;|f%dh{1r9sKX z|Ih!dpN>Qf9sxK>bFH}b+mQK^}%E9soO4mJ5lw|mok^5(Wd)#e&IJR{Jv{$_t(pVr8-Wo&9Uzj zFTX4Nak`&9_1mA_^VE&_!z*lt7EVg$=&Byoz3%@zas>DO^$dmWY*)H!$-^60I*@zw zd!6bpTISxbTj!zJ>pzAV?;2ct^~e2I&r4ZpLo-5dZ}9oO=;WmIHs@ty2J zGd_z3WR?BQsuz2oS=_90UmvL%JbHoKWLimTSaHpyZoOY0Lrj_ww7`CbcwRSR-%qV2 z>%qY(+u5X2lL|l9s*B(`jBml71dZhkUOtqyT$Z>w-8;81$IY}o!#E-0=%CDN(;+T$ zbxwPpSmygUKo6J?@yousO|Qp)1k9}7u{;UoPr#W&{FF|bGca+502BjHxQvzR@jH~W z*hm4iey5x~*DKVwI6Tp!;e=`2?deR!J|aQy)qW3q{Ab(kr<=>cOR^6M*D}^zp&e1q z1BllkIi;60MKDP;5j@jLD|2uk^-Y@#hgH$mPVSzt8xMHvLozEfaM0D#OTDg>gEQj+ zBvUZPGd@m}f&4QWEpVIDrjZjT$rZl8aU*8QTVq`fm|HHejQC%`#vkk}z=@%ca-Dic zYlh7-_7+<>D4wf94w9$zGsma;bz397~Sr~Z(lC*r3D?av9aE)Aq zxDN-GF?h~<18N-0-=hb@ga&AEIKB-Btrie0N~+`q+^LR(l3Kz7o)vyF(PRN`cT#h) zV4zrrS*J-@$QorW0u|EuA@$4G8FkEc8^A5Tto_W*3KuLCmrV!zgsx(zhaR3~=dC3m z1h-Paf+)*JyFSC!I|%^FpAI9TN5zx5G~ zN(RpUd5KGK$-&>T#nkzEZOSzc-Q1=<2rki2uC z1Hw^rwoqtPZ*8ZuUgB&VI1}=#P0<45KmnWd9v1un&*s7gCB&%Un>T>Lj|yQ1C8`c5 z0Diu?EiTG;F1n%{A;GroOuNlP9Q4_et`e>N()uBK*54tPZ{c-Pn>kF{q$3yv(l;) zohqR^H;ED_iE}{%k{~yzk;|%YO_vGUmtI<=}v*2bV#}6sgm#-8b@Kr6tL->P%d&@>=z^PsK+Z` z!A9xTS&6?+uquY!Z!mK{zeO-h=Te7&tHulaDU3n&=_(jnmW98`3tinv-US#0vSl7u zlTb>|EB4c;dQHsBQm@b>Ko^DM8amMkKbfBGHQZl;&ww;1z4$jBuiGnN11nPoOv9={ z$u0GiP@dT%p~;fS=TTqDQTv@>68@rStM#WIX;clY%&cM~IljeFt;jxf*`zV#?zzt3 z4==mxN9>+|b^SDkSl!;%XBTns*v{jH1D1Y8=^y#t=q|>mKg**AF;!lR{u+%m^QnA& zS;tMkH`zbr2|kM%w&qy04o?e8V-smXSmgire*?WisqgC>c~M`F#J_L3`Fr7u`As8_ zQ?DNVy0#^E*&zC?@{i}oOR~B(5#=A9j*XV3;nMG~JUe}A;ORwE!hil?DE_LrcC#v+ zGf+h^#PU2=M$KJceNOo-MET;Jn~HNH^3<=E_ZC))s+Mz##ha_swtlI(_PwCcP}{#@ z=<8D1^mJRDTX6kI4YfOEv){Dcrrn<5gyVb}Juz7dR-;?nx3MMNCiMKoX`^7+6CtEKI9b-H;zl z6@N3sV~}+aBL%aIZxxezU1)ra9U8H*4_>T69XZ$V8f4G!CSy0VKCa<`8 zfF*%uXrSz~nP(vB-{i56jC4=l!l7NJ8b%C2hR;JQtgs)zo86Vp!=WmOLP#${SDh+r z#Fia{FH|Ei_K>;o;)WO$6I-r^V^!clzrb45)=xAq7cn< z4Ag4^{@sDLktRbP#9Q?6l1^5ndP3lZ?W)e^ia70|^k*4!9MLC`3mLdWXi~Yf!aWn@ zojhbU_P!qrQvdR?$bHwjUEyy?As_{+!oNO|r4zwZX8uRAG0qVCw#o1&-9rZ|BXa5p zghup`Cg7glz&G8|AazWZ^iU@naEFh)&SULo2RVQwJlwG&2|e)@mjwcEqxCh8o63zC znb2KYkuNV3DS{h;CCQ}EI{Wh%V5dNI`hfuWyVxpEu`n3=TTUi4rxJ3bo;ol3yUo26lC z)eWmNa5vjjGU-i@K3_c${1UwLRZ5Co48Y>SHa%4G0`{gB>+vXZysV6QqGjtAkS{vJ;XI|8YDA9p!JSM%P8Su9dfVo^a|%&&M{H_RudW7XnF<0Ug-iu}!Xs z?>Cm2bV4e&`GY>yUFq5rm<%572x!;1u9L00F6|W>@7BrAe1qI|+JZ+y!`&Q|ro_*$ z^qYzz9wl_X^sK(Gae3r)cq}ro_=g}7_;W~#8z?($iHF-Hub(bgu3jx)9R|9qo=1{8 zndBH|y58#zR&wHq%0oF*U!X#?k;oN(@VcnCA@5AgAcu<)Fg0hXKPK+$b^%XEGV2g9 zDmK`M>js2ItsMGnLBSN<<0Rq~BSH04l;i zTePmp&X4N-M^>G^TAbK?^*skSxAaEHukovX z`b@a92=;}qg4<(AJ5Ks*{GUI)4aX3ts{qy=)OC&_6Qd3%(XD7f0T{{nf_ldWA0Z)c&$b9;^)84|DBFTamSFEAN@JuBU7$@Pih>= z(f{B}_(7MErMJFsA4AIC03vpu+dqaJt-Al+M$$Hn`oAcd-@mdq>cc$7kCne>z z`l7#be)=n?hqrpt<7Ny`ukjyosq!D>jm-6rYUbOQ{;HkP{cP%x_(#>Z8MvRK^tPJ1 zD;Yf~R_l4-b0$|M#n7jUfkKr9CsO%KrE3 zU68m(6PD8Ue_WxR{(pb>zmk^z;Xl8?6+WewH02lqA5-bSJ|^oyh?Df9{rk$!N-n+K zIt6~g-4WBl%yzp!C&Sz0Rn<)2Mo)Ks*i5n2w#=L3^fAy$wyL4S`J(R2t{#Q0@?qKe zN1JclO#Ww&714BqDY7TBed^)8%Q%0BPkhBjmwdjZPIS6?=*o{jr!6df8u__1V*7jZ+Jb;y3c-N!SAyX4wbWh4)T}yKB$?$Je{ZGyVVn|Iax_Nrx$Cg)k~%atK2&Vr&ehR#7QTMUt=> zQmN&T5RF)|jY?9agF`B$TC^>toE15RoEd%ZUf=iU{X1N~m+v31*B^CZ&*xK~kNe?v zyIp7YPwAt>ed_j*x{USC@*aF&WYmt=(b(HH7E#{vs%3LE^Jj&Cv$$9?uMdUKH=NfB2VU7_+)gS3yZm zGr698-u`105J!prvW&wls6=KP@ri{Cc4D9+)c`0tMwwSE5hGH;#|zRfz>)}9*}KSY zPg$Z2#5Iq{=y%()!W5jd<~ag*L%%Kk;EG5Pwl?hrTVVbEjog5ee{`X}r*sQ|+?F=d$lvMXk_C$Xy0);|- zG+v28QDBPsHQWY-+_Br>!O1(p&m42MiEX3?qftt#;Y@=@tZ8o+h}zSvC}F`h1#uw8 z(cCgXS>Bze?+Cjk{eJsJO3ads?tTl~h*#3@3}V{TWe#!1(m4^v*n?2dodDuvy0&~T zgA4wxeuwvTHxMR{SW6fJe<1d;6VzNMR_d;gCZ9>9U+mCq5{-kylX}&(HyetQg$W)2 z2ryVQ2thy?$HH_lfS~#LF}^-2z@n#UNvO0#@C3HP;`S>EPJy^^h+Nmn&jKM?yB=ts z=LN?{T$Dp$=L;^q{yPAQ5;5gk_b?vrl0#pG0N>RXg3G~#?{oYVe-Ud?98ca32mKuc3k_qZdM1_|19s33P`HBzsJYC%-eVrfF_IYEFRzdJ?(@*r(27?7gNoa2%3 z=8!X}2M6lYLTe#K!C%Cn*7Pj1n584%56{*APybs>$_#Uvb9f%hq4UTRi=w6k$oH#G z07?|{+BQK0NC^Wra5#ua0!!=Uf1EAc&%xF~?dZS4P7KJMv6YZboyl3M$B}V6oqRAU zK@4S@6R19yf*SVi!#MR7Stm2DDJQcK4MsAUuj&~#U$9805id@y%#3KxdkmJ?v`O~G zS_k0*<>~A*AiS1WI;=}cwJZ=3abT*s6GLmaB|~~4(Z#a=JEmlc>mY+*cL5=>gv#qn z!o2lICCb_9hThvHKV85&0O5B+%I%MoKib$fQ0h@a9PZpIPZA*dWa=~*D}B02gh5dr za~X(W)grd2`X^*+JKlJ;&*>XM6qb?n>(W4)uOTNTZ z=!eLr7VKg_xLp8;{qbiD6ET007nSadD6aMMwNdC(r5I-IjH|L)VM3u@S#4 z_1@s6Dx2Poqu>%;X^quLg-_2x>jG6po?pd9$NHOAZ4a5#h80Kdt`51L5JpAKynr`r z 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 z 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
Wi+GvvU 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 diff --git a/docs/content/cover_small.jpg b/docs/content/cover_small.jpg deleted file mode 100644 index cd143a268773fc56d7ec855b1236b014ade23739..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13622 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 diff --git a/docs/style.css b/docs/style.css deleted file mode 100644 index 29088c5e..00000000 --- a/docs/style.css +++ /dev/null @@ -1 +0,0 @@ -article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@font-face{font-family:'FontAwesome';src:url('./fonts/fontawesome/fontawesome-webfont.eot?v=4.1.0');src:url('./fonts/fontawesome/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('./fonts/fontawesome/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('./fonts/fontawesome/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('./fonts/fontawesome/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.link-inherit{color:inherit}.link-inherit:hover,.link-inherit:focus{color:inherit}.hidden{display:none}@font-face{font-family:'Merriweather';font-style:normal;font-weight:250;src:local('Merriweather Light'),url('./fonts/merriweather/250.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:italic;font-weight:250;src:local('Merriweather Light Italic'),url('./fonts/merriweather/250i.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:normal;font-weight:400;src:local('Merriweather'),url('./fonts/merriweather/400.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:italic;font-weight:400;src:local('Merriweather Italic'),url('./fonts/merriweather/400i.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:normal;font-weight:700;src:local('Merriweather Bold'),url('./fonts/merriweather/700.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:italic;font-weight:700;src:local('Merriweather Bold Italic'),url('./fonts/merriweather/700i.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:normal;font-weight:900;src:local('Merriweather Heavy'),url('./fonts/merriweather/900.woff') format('woff')}@font-face{font-family:'Merriweather';font-style:italic;font-weight:900;src:local('Merriweather Heavy Italic'),url('./fonts/merriweather/900i.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:local('Open Sans Light'),url('./fonts/opensans/300.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:local('Open Sans Light Italic'),url('./fonts/opensans/300i.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans Regular'),url('./fonts/opensans/400.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:local('Open Sans Italic'),url('./fonts/opensans/400i.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:local('Open Sans Semibold'),url('./fonts/opensans/600.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:local('Open Sans Semibold Italic'),url('./fonts/opensans/600i.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),url('./fonts/opensans/700.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:italic;font-weight:700;src:local('Open Sans Bold Italic'),url('./fonts/opensans/700i.woff') format('woff')}.book-langs-index{width:100%;height:100%;padding:40px 0;margin:0;overflow:auto}@media(max-width:600px){.book-langs-index{padding:0}}.book-langs-index .inner{max-width:600px;width:100%;margin:0 auto;padding:30px;background:#fff;border-radius:3px}.book-langs-index .inner h3{margin:0}.book-langs-index .inner .languages{list-style:none;padding:20px 30px;margin-top:20px;border-top:1px solid #eee;*zoom:1}.book-langs-index .inner .languages:before,.book-langs-index .inner .languages:after{content:" ";display:table;line-height:0}.book-langs-index .inner .languages:after{clear:both}.book-langs-index .inner .languages li{width:50%;float:left;padding:10px 5px;font-size:16px}@media(max-width:600px){.book-langs-index .inner .languages li{width:100%;max-width:100%}}.book .book-header{font-family:"Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-serif;overflow:visible;height:50px;padding:0 8px;z-index:2;font-size:.85em;color:#7e888b;background:transparent}.book .book-header .btn{display:block;height:50px;padding:0 15px;border-bottom:0;color:#ccc;text-transform:uppercase;line-height:50px;-webkit-box-shadow:none!important;box-shadow:none!important;position:relative;font-size:14px}.book .book-header .btn:hover{position:relative;text-decoration:none;color:#444;background:0}.book .book-header h1{margin:0;font-size:20px;font-weight:200;text-align:center;line-height:50px;opacity:0;-webkit-transition:opacity ease .4s;-moz-transition:opacity ease .4s;-o-transition:opacity ease .4s;transition:opacity ease .4s;padding-left:200px;padding-right:200px;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;-o-transition:opacity .2s ease;transition:opacity .2s ease;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.book .book-header h1 a,.book .book-header h1 a:hover{color:inherit;text-decoration:none}@media(max-width:1000px){.book .book-header h1{display:none}}.book .book-header h1 i{display:none}.book .book-header:hover h1{opacity:1}.book.is-loading .book-header h1 i{display:inline-block}.book.is-loading .book-header h1 a{display:none}.book.color-theme-1 .book-header{color:#afa790;background:transparent}.book.color-theme-1 .book-header .btn{color:#afa790}.book.color-theme-1 .book-header .btn:hover{color:#73553c;background:0}.book.color-theme-1 .book-header h1{color:#704214}.book.color-theme-2 .book-header{color:#7e888b;background:transparent}.book.color-theme-2 .book-header .btn{color:#3b3f54}.book.color-theme-2 .book-header .btn:hover{color:#fffff5;background:0}.book.color-theme-2 .book-header h1{color:#bdcadb}.dropdown{position:relative}.dropdown-menu{position:absolute;top:100%;left:0;z-index:100;display:none;float:left;min-width:160px;padding:0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fafafa;border:1px solid rgba(0,0,0,0.07);border-radius:1px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.open{display:block}.dropdown-menu.dropdown-left{left:auto;right:4%}.dropdown-menu.dropdown-left .dropdown-caret{right:14px;left:auto}.dropdown-menu .dropdown-caret{position:absolute;top:-8px;left:14px;width:18px;height:10px;float:left;overflow:hidden}.dropdown-menu .dropdown-caret .caret-outer{position:absolute;border-left:9px solid transparent;border-right:9px solid transparent;border-bottom:9px solid rgba(0,0,0,0.1);height:auto;left:0;top:0;width:auto;display:inline-block;margin-left:-1px}.dropdown-menu .dropdown-caret .caret-inner{position:absolute;display:inline-block;margin-top:-1px;top:0;top:1px;border-left:9px solid transparent;border-right:9px solid transparent;border-bottom:9px solid #fafafa}.dropdown-menu .buttons{*zoom:1;border-bottom:1px solid rgba(0,0,0,0.07)}.dropdown-menu .buttons:before,.dropdown-menu .buttons:after{content:" ";display:table;line-height:0}.dropdown-menu .buttons:after{clear:both}.dropdown-menu .buttons:last-child{border-bottom:0}.dropdown-menu .buttons .button{border:0;background-color:transparent;color:#a6a6a6;width:100%;text-align:center;float:left;line-height:1.428571429;padding:8px 4px}.dropdown-menu .buttons .button:hover{color:#444}.dropdown-menu .buttons .button:focus,.dropdown-menu .buttons .button:hover{outline:0}.dropdown-menu .buttons .button.size-2{width:50%}.dropdown-menu .buttons .button.size-3{width:33%}.color-theme-1 .dropdown-menu{background-color:#111;border-color:rgba(0,0,0,0.07)}.color-theme-1 .dropdown-menu .dropdown-caret .caret-inner{border-bottom:9px solid #111}.color-theme-1 .dropdown-menu .buttons{border-color:rgba(0,0,0,0.07)}.color-theme-1 .dropdown-menu .button{color:#afa790}.color-theme-1 .dropdown-menu .button:hover{color:#73553c}.color-theme-2 .dropdown-menu{background-color:#2d3143;border-color:#272a3a}.color-theme-2 .dropdown-menu .dropdown-caret .caret-inner{border-bottom:9px solid #2d3143}.color-theme-2 .dropdown-menu .buttons{border-color:#272a3a}.color-theme-2 .dropdown-menu .button{color:#62677f}.color-theme-2 .dropdown-menu .button:hover{color:#f4f4f5}.alert{padding:15px;margin-bottom:20px;color:#444;background:#eee;border-bottom:5px solid #ddd}.alert-success{background:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-info{background:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-danger{background:#f2dede;border-color:#ebccd1;color:#a94442}.alert-warning{background:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.book .book-summary{font-family:"Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-serif;position:absolute;top:0;left:-300px;bottom:0;z-index:1;width:300px;color:#364149;background:#fafafa;border-right:1px solid rgba(0,0,0,0.07);-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-summary .book-search{padding:6px;background:transparent;position:absolute;top:-50px;left:0;right:0;-webkit-transition:top .5s ease;-moz-transition:top .5s ease;-o-transition:top .5s ease;transition:top .5s ease}.book .book-summary .book-search input,.book .book-summary .book-search input:focus,.book .book-summary .book-search input:hover{width:100%;background:transparent;border:1px solid transparent;-webkit-box-shadow:none;box-shadow:none;outline:0;line-height:22px;padding:7px 4px;color:inherit}.book .book-summary ul.summary{position:absolute;top:0;left:0;right:0;bottom:0;overflow-y:auto;list-style:none;margin:0;padding:0;-webkit-transition:top .5s ease;-moz-transition:top .5s ease;-o-transition:top .5s ease;transition:top .5s ease}.book .book-summary ul.summary li{list-style:none}.book .book-summary ul.summary li.divider{height:1px;margin:7px 0;overflow:hidden;background:rgba(0,0,0,0.07)}.book .book-summary ul.summary li i.fa-check{display:none;position:absolute;right:9px;top:16px;font-size:9px;color:#3c3}.book .book-summary ul.summary li.done>a{color:#364149;font-weight:normal}.book .book-summary ul.summary li.done>a i{display:inline}.book .book-summary ul.summary li a,.book .book-summary ul.summary li span{display:block;padding:10px 15px;border-bottom:0;color:#364149;background:transparent;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative}.book .book-summary ul.summary li span{cursor:not-allowed;opacity:.3;filter:alpha(opacity=30)}.book .book-summary ul.summary li.active>a,.book .book-summary ul.summary li a:hover{color:#008cff;background:transparent;text-decoration:none}.book .book-summary ul.summary li ul{padding-left:20px}@media(max-width:600px){.book .book-summary{width:calc(100% - 60px);bottom:0;left:-100%}}.book.with-summary .book-summary{left:0}.book.without-animation .book-summary{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.book.with-search .book-summary .book-search{top:0}.book.with-search .book-summary ul.summary{top:50px}.book.color-theme-1 .book-summary{color:#afa790;background:#111;border-right:1px solid rgba(0,0,0,0.07)}.book.color-theme-1 .book-summary .book-search{background:transparent}.book.color-theme-1 .book-summary .book-search input,.book.color-theme-1 .book-summary .book-search input:focus{border:1px solid transparent}.book.color-theme-1 .book-summary ul.summary li.divider{background:rgba(0,0,0,0.07);box-shadow:none}.book.color-theme-1 .book-summary ul.summary li i.fa-check{color:#3c3}.book.color-theme-1 .book-summary ul.summary li.done>a{color:#877f6a}.book.color-theme-1 .book-summary ul.summary li a,.book.color-theme-1 .book-summary ul.summary li span{color:#877f6a;background:transparent;font-weight:normal}.book.color-theme-1 .book-summary ul.summary li.active>a,.book.color-theme-1 .book-summary ul.summary li a:hover{color:#704214;background:transparent;font-weight:normal}.book.color-theme-2 .book-summary{color:#bcc1d2;background:#2d3143;border-right:0}.book.color-theme-2 .book-summary .book-search{background:transparent}.book.color-theme-2 .book-summary .book-search input,.book.color-theme-2 .book-summary .book-search input:focus{border:1px solid transparent}.book.color-theme-2 .book-summary ul.summary li.divider{background:#272a3a;box-shadow:none}.book.color-theme-2 .book-summary ul.summary li i.fa-check{color:#3c3}.book.color-theme-2 .book-summary ul.summary li.done>a{color:#62687f}.book.color-theme-2 .book-summary ul.summary li a,.book.color-theme-2 .book-summary ul.summary li span{color:#c1c6d7;background:transparent;font-weight:600}.book.color-theme-2 .book-summary ul.summary li.active>a,.book.color-theme-2 .book-summary ul.summary li a:hover{color:#f4f4f5;background:#252737;font-weight:600}.book-header #font-settings-wrapper #enlarge-font-size,.book-header #font-settings-wrapper #reduce-font-size{line-height:30px}.book-header #font-settings-wrapper #enlarge-font-size{font-size:1.4em}.book-header #font-settings-wrapper #reduce-font-size{font-size:1em}.book{position:relative;width:100%;height:100%}.book .book-body{position:absolute;top:0;right:0;left:0;bottom:0;overflow-y:auto;color:#000;background:#fff;-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-body .body-inner{position:absolute;top:0;right:0;left:0;bottom:0;overflow-y:auto}.book .book-body .page-wrapper{position:relative;outline:0}.book .book-body .page-wrapper .page-inner{max-width:800px;margin:0 auto;padding:20px 0 40px 0}.book .book-body .page-wrapper .page-inner section{margin:0;padding:5px 15px;background:#fff;border-radius:2px;line-height:1.6;font-size:1.6rem}.book .book-body .page-wrapper .page-inner .btn-group .btn{border-radius:0;background:#eee;border:0}@media(max-width:1240px){.book .book-body{-webkit-transition:-webkit-transform 250ms ease;-moz-transition:-moz-transform 250ms ease;-o-transition:-o-transform 250ms ease;transition:transform 250ms ease;padding-bottom:20px}.book .book-body .body-inner{position:static;min-height:calc(100% - 50px)}}@media(min-width:600px){.book.with-summary .book-body{left:300px}}@media(max-width:600px){.book.with-summary{overflow:hidden}.book.with-summary .book-body{-webkit-transform:translate(calc(100% - 60px),0px);-moz-transform:translate(calc(100% - 60px),0px);-ms-transform:translate(calc(100% - 60px),0px);-o-transform:translate(calc(100% - 60px),0px);transform:translate(calc(100% - 60px),0px)}}.book.without-animation .book-body{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.book.color-theme-1 .book-body{color:#704214;background:#f3eacb}.book.color-theme-1 .book-body .page-wrapper .page-inner section{background:#f3eacb}.book.color-theme-2 .book-body{color:#bdcadb;background:#1c1f2b}.book.color-theme-2 .book-body .page-wrapper .page-inner section{background:#1c1f2b}.book.font-size-0 .book-body .page-inner section{font-size:1.2rem}.book.font-size-1 .book-body .page-inner section{font-size:1.4rem}.book.font-size-2 .book-body .page-inner section{font-size:1.6rem}.book.font-size-3 .book-body .page-inner section{font-size:2.2rem}.book.font-size-4 .book-body .page-inner section{font-size:4rem}.book.font-family-0{font-family:"Merriweather",Georgia,serif}.book.font-family-1{font-family:"Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.buttons{*zoom:1}.buttons:before,.buttons:after{content:" ";display:table;line-height:0}.buttons:after{clear:both}.button{border:0;background-color:transparent;background:#eee;color:#666;width:100%;text-align:center;float:left;line-height:1.428571429;padding:8px 4px}.button:hover{color:#444}.button:focus,.button:hover{outline:0}.button.size-2{width:50%}.button.size-3{width:33%}.book .book-body .page-wrapper .page-inner section{display:none}.book .book-body .page-wrapper .page-inner section.normal{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.4;color:#333;overflow:hidden;line-height:1.6;word-wrap:break-word;display:block}.book .book-body .page-wrapper .page-inner section.normal>*:first-child{margin-top:0!important}.book .book-body .page-wrapper .page-inner section.normal>*:last-child{margin-bottom:0!important}.book .book-body .page-wrapper .page-inner section.normal a{background:transparent}.book .book-body .page-wrapper .page-inner section.normal a:active,.book .book-body .page-wrapper .page-inner section.normal a:hover{outline:0}.book .book-body .page-wrapper .page-inner section.normal strong{font-weight:bold}.book .book-body .page-wrapper .page-inner section.normal h1{font-size:2em;margin:.67em 0}.book .book-body .page-wrapper .page-inner section.normal img{border:0}.book .book-body .page-wrapper .page-inner section.normal hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}.book .book-body .page-wrapper .page-inner section.normal pre{overflow:auto}.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal pre{font-family:monospace,monospace;font-size:1em}.book .book-body .page-wrapper .page-inner section.normal table{border-collapse:collapse;border-spacing:0}.book .book-body .page-wrapper .page-inner section.normal td,.book .book-body .page-wrapper .page-inner section.normal th{padding:0}.book .book-body .page-wrapper .page-inner section.normal *{-moz-box-sizing:border-box;box-sizing:border-box}.book .book-body .page-wrapper .page-inner section.normal a{color:#4183c4;text-decoration:none}.book .book-body .page-wrapper .page-inner section.normal a:hover,.book .book-body .page-wrapper .page-inner section.normal a:focus,.book .book-body .page-wrapper .page-inner section.normal a:active{text-decoration:underline}.book .book-body .page-wrapper .page-inner section.normal hr{height:0;margin:15px 0;overflow:hidden;background:transparent;border:0;border-bottom:1px solid #ddd}.book .book-body .page-wrapper .page-inner section.normal hr:before,.book .book-body .page-wrapper .page-inner section.normal hr:after{display:table;content:" "}.book .book-body .page-wrapper .page-inner section.normal hr:after{clear:both}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal h6{margin-top:15px;margin-bottom:15px;line-height:1.1}.book .book-body .page-wrapper .page-inner section.normal h1{font-size:30px}.book .book-body .page-wrapper .page-inner section.normal h2{font-size:21px}.book .book-body .page-wrapper .page-inner section.normal h3{font-size:16px}.book .book-body .page-wrapper .page-inner section.normal h4{font-size:14px}.book .book-body .page-wrapper .page-inner section.normal h5{font-size:12px}.book .book-body .page-wrapper .page-inner section.normal h6{font-size:11px}.book .book-body .page-wrapper .page-inner section.normal blockquote{margin:0}.book .book-body .page-wrapper .page-inner section.normal ul,.book .book-body .page-wrapper .page-inner section.normal ol{padding:0;margin-top:0;margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal ol ol{list-style-type:lower-roman}.book .book-body .page-wrapper .page-inner section.normal dd{margin-left:0}.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}.book .book-body .page-wrapper .page-inner section.normal pre{margin-top:0;margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal .markdown-body>*:first-child{margin-top:0!important}.book .book-body .page-wrapper .page-inner section.normal .markdown-body>*:last-child{margin-bottom:0!important}.book .book-body .page-wrapper .page-inner section.normal .anchor{position:absolute;top:0;bottom:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.book .book-body .page-wrapper .page-inner section.normal .anchor:focus{outline:0}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:bold;line-height:1.4}.book .book-body .page-wrapper .page-inner section.normal h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.book .book-body .page-wrapper .page-inner section.normal h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.book .book-body .page-wrapper .page-inner section.normal h3{font-size:1.5em;line-height:1.43}.book .book-body .page-wrapper .page-inner section.normal h4{font-size:1.25em}.book .book-body .page-wrapper .page-inner section.normal h5{font-size:1em}.book .book-body .page-wrapper .page-inner section.normal h6{font-size:1em;color:#777}.book .book-body .page-wrapper .page-inner section.normal p,.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal ul,.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal dl,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal pre{margin-top:0;margin-bottom:16px}.book .book-body .page-wrapper .page-inner section.normal hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.book .book-body .page-wrapper .page-inner section.normal ul,.book .book-body .page-wrapper .page-inner section.normal ol{padding-left:2em}.book .book-body .page-wrapper .page-inner section.normal ol ol,.book .book-body .page-wrapper .page-inner section.normal ol ul,.book .book-body .page-wrapper .page-inner section.normal ul ul{margin-top:0;margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal dl{padding:0}.book .book-body .page-wrapper .page-inner section.normal dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:bold}.book .book-body .page-wrapper .page-inner section.normal dl dd{padding:0 16px;margin-bottom:16px}.book .book-body .page-wrapper .page-inner section.normal blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.book .book-body .page-wrapper .page-inner section.normal blockquote>:first-child{margin-top:0}.book .book-body .page-wrapper .page-inner section.normal blockquote>:last-child{margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal table{display:block;width:100%;overflow:auto}.book .book-body .page-wrapper .page-inner section.normal table th{font-weight:bold}.book .book-body .page-wrapper .page-inner section.normal table th,.book .book-body .page-wrapper .page-inner section.normal table td{padding:6px 13px;border:1px solid #ddd}.book .book-body .page-wrapper .page-inner section.normal table tr{background-color:#fff;border-top:1px solid #ccc}.book .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n){background-color:#f8f8f8}.book .book-body .page-wrapper .page-inner section.normal img{max-width:100%;-moz-box-sizing:border-box;box-sizing:border-box;page-break-inside:avoid}.book .book-body .page-wrapper .page-inner section.normal code{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:#f7f7f7;border-radius:3px}.book .book-body .page-wrapper .page-inner section.normal code:before,.book .book-body .page-wrapper .page-inner section.normal code:after{letter-spacing:-0.2em;content:"\00a0"}.book .book-body .page-wrapper .page-inner section.normal pre>code{padding:0;margin:0;font-size:100%;white-space:pre;background:transparent;border:0}.book .book-body .page-wrapper .page-inner section.normal .highlight pre,.book .book-body .page-wrapper .page-inner section.normal pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border:0;border-radius:3px}.book .book-body .page-wrapper .page-inner section.normal pre{word-wrap:normal}.book .book-body .page-wrapper .page-inner section.normal pre code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.book .book-body .page-wrapper .page-inner section.normal pre code:before,.book .book-body .page-wrapper .page-inner section.normal pre code:after{content:normal}.book .book-body .page-wrapper .page-inner section.normal .highlight{background:#fff}.book .book-body .page-wrapper .page-inner section.normal pre .hljs-comment,.book .book-body .page-wrapper .page-inner section.normal code .hljs-comment,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .hljs-title{color:#8e908c}.book .book-body .page-wrapper .page-inner section.normal pre .hljs-variable,.book .book-body .page-wrapper .page-inner section.normal code .hljs-variable,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute,.book .book-body .page-wrapper .page-inner section.normal code .hljs-attribute,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-tag,.book .book-body .page-wrapper .page-inner section.normal code .hljs-tag,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp,.book .book-body .page-wrapper .page-inner section.normal code .hljs-regexp,.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant,.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant,.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title,.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi,.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi,.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype,.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype,.book .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype,.book .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype,.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id,.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-id,.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class,.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-class,.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo,.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo{color:#c82829}.book .book-body .page-wrapper .page-inner section.normal pre .hljs-number,.book .book-body .page-wrapper .page-inner section.normal code .hljs-number,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor,.book .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma,.book .book-body .page-wrapper .page-inner section.normal code .hljs-pragma,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in,.book .book-body .page-wrapper .page-inner section.normal code .hljs-built_in,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-literal,.book .book-body .page-wrapper .page-inner section.normal code .hljs-literal,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-params,.book .book-body .page-wrapper .page-inner section.normal code .hljs-params,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-constant,.book .book-body .page-wrapper .page-inner section.normal code .hljs-constant{color:#f5871f}.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title,.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute,.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute{color:#eab700}.book .book-body .page-wrapper .page-inner section.normal pre .hljs-string,.book .book-body .page-wrapper .page-inner section.normal code .hljs-string,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-value,.book .book-body .page-wrapper .page-inner section.normal code .hljs-value,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance,.book .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance,.book .book-body .page-wrapper .page-inner section.normal pre .hljs-header,.book .book-body .page-wrapper .page-inner section.normal code .hljs-header,.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol,.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol,.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata,.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata{color:#718c00}.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor,.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor{color:#3e999f}.book .book-body .page-wrapper .page-inner section.normal pre .hljs-function,.book .book-body .page-wrapper .page-inner section.normal code .hljs-function,.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator,.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator,.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-title,.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title,.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword,.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword,.book .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub,.book .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub,.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title,.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title,.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title{color:#4271ae}.book .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword,.book .book-body .page-wrapper .page-inner section.normal code .hljs-keyword,.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function,.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function{color:#8959a8}.book .book-body .page-wrapper .page-inner section.normal pre .hljs,.book .book-body .page-wrapper .page-inner section.normal code .hljs{display:block;background:white;color:#4d4d4c;padding:.5em}.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript,.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript,.book .book-body .page-wrapper .page-inner section.normal pre .javascript .xml,.book .book-body .page-wrapper .page-inner section.normal code .javascript .xml,.book .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula,.book .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula,.book .book-body .page-wrapper .page-inner section.normal pre .xml .javascript,.book .book-body .page-wrapper .page-inner section.normal code .xml .javascript,.book .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript,.book .book-body .page-wrapper .page-inner section.normal code .xml .vbscript,.book .book-body .page-wrapper .page-inner section.normal pre .xml .css,.book .book-body .page-wrapper .page-inner section.normal code .xml .css,.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata,.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata{opacity:.5}.book .book-body .page-wrapper .page-inner section.normal .glossary-term{cursor:help;text-decoration:underline}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal{color:#704214}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a{color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6{color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2{border-color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6{color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr{background-color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote{border-color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code{background:#fdf6e3;color:#657b83;border-color:#f8df9c}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs{display:block;padding:.5em;background:#fdf6e3;color:#657b83}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-comment,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-template_comment,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-template_comment,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-header,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-header,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-doctype,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-doctype,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pi,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pi,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-string,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-string,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-javadoc,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-javadoc{color:#93a1a1}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-winutils,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-winutils,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .method,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .method,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-addition,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-addition,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-tag,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-tag,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-request,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-request,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-status,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-status,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .nginx .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .nginx .hljs-title{color:#859900}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-number,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-number,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-command,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-command,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-string,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-string,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag .hljs-value,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-tag .hljs-value,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-rules .hljs-value,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-rules .hljs-value,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-phpdoc,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-phpdoc,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-hexcolor,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-hexcolor,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_url,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_url{color:#2aa198}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-localvars,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-localvars,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-chunk,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-chunk,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-decorator,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-decorator,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-identifier,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-identifier,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .vhdl .hljs-literal,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .vhdl .hljs-literal,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-id,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-id,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-function,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-function{color:#268bd2}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-variable,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-body,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-body,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .smalltalk .hljs-number,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .smalltalk .hljs-number,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-constant,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-class .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-class .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-parent,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-parent,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .haskell .hljs-type,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .haskell .hljs-type,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_reference,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_reference{color:#b58900}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor .hljs-keyword,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor .hljs-keyword,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-shebang,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-shebang,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol .hljs-string,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol .hljs-string,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-change,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-change,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-special,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-special,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attr_selector,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attr_selector,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-subst,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-subst,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-cdata,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-cdata,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .clojure .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .clojure .hljs-title,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-header,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-header{color:#cb4b16}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-deletion,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-important,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-important{color:#dc322f}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_label,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_label{color:#6c71c4}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula{background:#eee8d5}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight{background-color:inherit}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th,.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td{border-color:#f5d06c}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr{color:inherit;background-color:#fdf6e3;border-color:#444}.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n){background-color:#fbeecb}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal{color:#bdcadb}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a{color:#3eb1d0}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6{color:#fffffa}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2{border-color:#373b4e}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6{color:#373b4e}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr{background-color:#373b4e}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote{border-color:#373b4e}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code{color:#9dbed8;background:#2d3143;border-color:#2d3143}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-comment,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-title{color:#969896}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-variable,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-tag,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-id,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-class,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo{color:#d54e53}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-number,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-number,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-literal,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-literal,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-params,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-params,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-constant{color:#e78c45}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute{color:#e7c547}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-string,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-string,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-value,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-value,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-header,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-header,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata{color:#b9ca4a}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor{color:#70c0b1}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-function,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-function,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title{color:#7aa6da}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function{color:#c397d8}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs{display:block;background:black;color:#eaeaea;padding:.5em}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .xml,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .xml,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .javascript,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .javascript,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .vbscript,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .css,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .css,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata{opacity:.5}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight{background-color:#282a39}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th,.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td{border-color:#3b3f54}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr{color:#b6c2d2;background-color:#2d3143;border-color:#3b3f54}.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n){background-color:#35394b}.book .book-body .navigation{position:absolute;top:50px;bottom:0;margin:0;max-width:150px;min-width:90px;display:flex;justify-content:center;align-content:center;flex-direction:column;font-size:40px;color:#ccc;text-align:center;-webkit-transition:all 350ms ease;-moz-transition:all 350ms ease;-o-transition:all 350ms ease;transition:all 350ms ease}.book .book-body .navigation:hover{text-decoration:none;color:#444}.book .book-body .navigation.navigation-next{right:0}.book .book-body .navigation.navigation-prev{left:0}@media(max-width:1240px){.book .book-body .navigation{position:static;top:auto;max-width:50%;width:50%;display:inline-block;float:left}.book .book-body .navigation.navigation-unique{max-width:100%;width:100%}}.book.color-theme-1 .book-body .navigation{color:#afa790}.book.color-theme-1 .book-body .navigation:hover{color:#73553c}.book.color-theme-2 .book-body .navigation{color:#383f52}.book.color-theme-2 .book-body .navigation:hover{color:#fffff5}.book .book-body .page-wrapper .page-inner section.glossary{margin-bottom:40px}.book .book-body .page-wrapper .page-inner section.glossary h2 a,.book .book-body .page-wrapper .page-inner section.glossary h2 a:hover{color:inherit;text-decoration:none}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index{list-style:none;margin:0;padding:0}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none;-webkit-touch-callout:none;-webkit-font-smoothing:antialiased}a{text-decoration:none}html,body{height:100%}html{font-size:62.5%}body{text-rendering:optimizeLegibility;font-smoothing:antialiased;font-family:"Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px} \ No newline at end of file From 7de347c572d35c53f5b2f223756f53c7c3ef9423 Mon Sep 17 00:00:00 2001 From: Marcus Gadbem Date: Thu, 21 Jan 2016 21:15:58 -0200 Subject: [PATCH 34/61] [docs] Updating Gitbook and plugins versions - removed unused plugins; - added github, sitemap and hotjar plugins; - updated Azkfile; - updated README instructions; - updated .env.sample; --- CHANGELOG.md | 1 + docs/.env.sample | 3 +- docs/Azkfile.js | 27 ++++++++++------- docs/README.md | 32 +++++++-------------- docs/content/book.json | 40 ++++++++++++++++++-------- docs/content/common/styles/website.css | 14 +++++++++ docs/package.json | 16 +++++------ 7 files changed, 80 insertions(+), 53 deletions(-) create mode 100644 docs/content/common/styles/website.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a301dcc..9e6bb7a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * [Code] Adding github issue and PR templates; * [Docs] The instructions for use `azk` and `Wordpress` have been improved; * [Manifest] Adding `resolve` options to `path`, with default `true` value; + * [Docs] Updating whole docs structure: Azkfile, Gitbook version and plugins; ## v0.17.0 - (2016-02-19) diff --git a/docs/.env.sample b/docs/.env.sample index 0a8649cf..3a53f153 100644 --- a/docs/.env.sample +++ b/docs/.env.sample @@ -3,4 +3,5 @@ AWS_SECRET_KEY=XXXXXXXXXXXXXXXXX AWS_BUCKET_PROD=[bucket to prod deploy] AWS_BUCKET_STAGE=[bucket to stage deploy] GA_UA=UA-XXXXXXXX-X -GA_COOKIE_DOMAIN=azk.io \ No newline at end of file +GA_LEGACY_COOKIE_DOMAIN=azk.io +HOTJAR_ID=XXXXXXX diff --git a/docs/Azkfile.js b/docs/Azkfile.js index b733439d..29c1f962 100644 --- a/docs/Azkfile.js +++ b/docs/Azkfile.js @@ -10,28 +10,35 @@ systems({ // Steps to execute before running instances provision: [ "npm i", - "node node_modules/.bin/gitbook install content", + "gitbook install content", ], workdir: "/azk/#{manifest.dir}", shell: "/bin/bash", - command: "node_modules/.bin/gitbook serve --port $HTTP_PORT content", - wait: {"retry": 20, "timeout": 2000}, + command: "gitbook serve --port $HTTP_PORT content --lrport $LIVERELOAD_PORT", + wait: {"retry": 20, "timeout": 3000}, mounts: { - '/azk/#{manifest.dir}': path("."), - '/azk/CONTRIBUTING.md': path("../.github/CONTRIBUTING.md"), - '/azk/node_modules': persistent("node_modules"), + '/azk/#{manifest.dir}' : sync("."), + '/azk/CONTRIBUTING.md' : path("../.github/CONTRIBUTING.md"), + '/azk/package.json' : path("./package.json"), + '/azk/#{manifest.dir}/content/pt-BR/styles' : sync("./content/common/styles"), + '/azk/#{manifest.dir}/content/en/styles' : sync("./content/common/styles"), + '/azk/#{manifest.dir}/node_modules' : persistent("node_modules"), + '/azk/#{manifest.dir}/content/_book' : persistent("book"), + '/azk/#{manifest.dir}/content/node_modules' : persistent("content/node_modules"), + '/root/.npm' : persistent("npm-cache"), + '/root/.gitbook' : persistent("gitbook-versions"), }, scalable: {"default": 1}, http: { // docs-azk. domains: [ "#{system.name}.#{azk.default_domain}" ] }, + envs: { + PATH: "/azk/#{manifest.dir}/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + }, ports: { http: "5000/tcp", - livereload: "35729:35729/tcp", + livereload: "35730:35730/tcp", }, }, }); - - - diff --git a/docs/README.md b/docs/README.md index cd821b8a..793116e6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,39 +1,27 @@ # azk documentation -This documentation is made using Gitbook. +`azk` documentation was built upon Gitbook. -To start contributing: - -### Prepare your environment - -```sh -azk nvm npm install -azk nvm gitbook install content -azk nvm gitbook build content -``` - -### Start server +### Starting Gitbook server with azk ```sh -# if you have node and gitbook installed -gitbook serve content - -# this will work if you have azk instaled -azk nvm gitbook serve content +azk start -o && azk logs --follow ``` -Now you can open [http://localhost:4000](http://localhost:4000). :) +After a few minutes, `azk` docs will be up and running on [docs-azk.dev.azk.io](docs-azk.dev.azk.io). :) ## Deploying -Before deploying you must create a file named `.env` or copy and update `.env.sample` +Before deploying you must create a file called `.env` or copy and update `.env.sample` ```ini AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX AWS_SECRET_KEY=XXXXXXXXXXXXXXXXX AWS_BUCKET_PROD=[bucket to prod deploy] AWS_BUCKET_STAGE=[bucket to stage deploy] -MIXPANEL_TOKEN=XXXXXXXXXXXXXXXXX +GA_UA=UA-XXXXXXXX-X +GA_LEGACY_COOKIE_DOMAIN=azk.io +HOTJAR_ID=XXXXXXX ``` ### azk buckets @@ -49,8 +37,8 @@ docs.azk.io ### Build and deploy ```sh -azk nvm gulp deploy-stage -azk nvm gulp deploy-prod +azk shell -- gulp deploy --stage +azk shell -- gulp deploy --prod ``` #### [! danger !] to remove all files from a bucket use s3cmd diff --git a/docs/content/book.json b/docs/content/book.json index d339697c..4ffca688 100644 --- a/docs/content/book.json +++ b/docs/content/book.json @@ -1,21 +1,43 @@ { "title": "azk Docs Gitbook", "description": "Gitbook documentation for azk", - "version": "1.0.0", + "version": "2.6.4", "azkVersion": "0.13.0", "releaseNotes": "http://......", "plugins": [ - "scrollablecode", - "anchors@0.4.0", - "include", - "ga@0.2.1" + "ga", + "hotjar", + "include", + "scrollablecode", + "-sharing", + "github", + "sitemap", + "anchors" ], "pluginsConfig": { + "hotjar": { + "hjid": 12345678901 + }, "ga": { "token": "GA_UA_ID", "configuration": { - "cookieDomain": "GA_COOKIE_DOMAIN" + "legacyCookieDomain": "GA_LEGACY_COOKIE_DOMAIN" } + }, + "sharing": { + "facebook": true, + "twitter": true, + "google": false, + "weibo": false, + "instapaper": false, + "vk": false, + "all": [] + }, + "github": { + "url": "https://github.com/azukiapp/azk" + }, + "sitemap": { + "hostname": "http://docs.azk.io/" } }, "links": { @@ -24,12 +46,6 @@ "theme": "node_modules/gitbook-theme-clarity/lib", "custom": { "custom": "http://azukiapp.com" - }, - "sharing": { - "google": false, - "facebook": null, - "twitter": null, - "all": false } } } diff --git a/docs/content/common/styles/website.css b/docs/content/common/styles/website.css new file mode 100644 index 00000000..5f5d431c --- /dev/null +++ b/docs/content/common/styles/website.css @@ -0,0 +1,14 @@ +.book .book-body .page-wrapper .page-inner section.normal { + overflow: visible; +} + +a.plugin-anchor span { + font-size: 17px !important; + position: relative; + left: -10px; + top: -3px; +} + +a.plugin-anchor:hover span { + color: rgba(66,139,202,.8); +} diff --git a/docs/package.json b/docs/package.json index f74209a5..52e7b8d6 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,11 +1,10 @@ { "name": "azk-docs", "description": "azk documentation gitbook repo", - "devDependencies": { + "dependencies": { "concurrent-transform": "^1.0.0", "del": "^1.1.1", "dotenv": "^0.4.0", - "gitbook": "^1.0.0", "gulp": "^3.8.10", "gulp-awspublish": "0.0.24", "gulp-newer": "^0.5.0", @@ -16,7 +15,12 @@ "minimist": "^1.1.0", "run-sequence": "^1.0.2", "stream-combiner2": "^1.0.2", - "vinyl-paths": "^1.0.0" + "vinyl-paths": "^1.0.0", + "gitbook-cli": "^1.0.1", + "gulp-awspublish": "^3.0.1", + "gulp-awspublish-router": "bvanvugt/gulp-awspublish-router", + "gulp-if": "^1.2.5", + "yargs": "^3.32.0" }, "repository": { "type": "git", @@ -26,9 +30,5 @@ "bugs": { "url": "https://github.com/azukiapp/azk-docs/issues" }, - "homepage": "http://docs.azk.io", - "dependencies": { - "gulp-awspublish-router": "bvanvugt/gulp-awspublish-router", - "gulp-if": "^1.2.5" - } + "homepage": "http://docs.azk.io" } From ad305441519aa4a15b1d85d57d52477e6066f897 Mon Sep 17 00:00:00 2001 From: Marcus Gadbem Date: Thu, 21 Jan 2016 21:16:37 -0200 Subject: [PATCH 35/61] [docs] Updating gulp-awspublisher version and deploy task --- docs/gulpfile.js | 144 +++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 86 deletions(-) diff --git a/docs/gulpfile.js b/docs/gulpfile.js index 5030ca90..cd9c0e02 100644 --- a/docs/gulpfile.js +++ b/docs/gulpfile.js @@ -8,27 +8,6 @@ var runSequence = require('run-sequence'); var dotenv = require('dotenv'); dotenv.load(); -// Configs for deploy -var configs = { - deploy: { - bucket: process.env.AWS_BUCKET_STAGE - } -}; - -gulp.task('del-wrong-gitbook-folder', function (cb) { - return del([ - 'content/_book/gitbook', - ], cb); -}); - -gulp.task('del-all-gitbook-folders', function (cb) { - return del([ - 'content/_book/gitbook', - 'content/_book/en/gitbook', - 'content/_book/pt-BR/gitbook', - ], cb); -}); - gulp.task('replace-font-path-pt-BR', function(){ return gulp.src(['./content/_book/pt-BR/gitbook/print.css', './content/_book/pt-BR/gitbook/style.css',]) @@ -52,52 +31,91 @@ gulp.task('replace-style.css-path-on-index', function(){ gulp.task('replace-ga-tokens', function() { return gulp.src(['./content/_book/**/*.html']) .pipe(replace(/GA_UA_ID/gm, process.env.GA_UA)) - .pipe(replace(/GA_COOKIE_DOMAIN/gm, process.env.GA_COOKIE_DOMAIN)) + .pipe(replace(/GA_LEGACY_COOKIE_DOMAIN/gm, process.env.GA_LEGACY_COOKIE_DOMAIN)) + .pipe(gulp.dest('./content/_book')); +}); + +gulp.task('replace-hotjar-token', function() { + return gulp.src(['./content/_book/**/*.html']) + .pipe(replace(/12345678901/gm, process.env.HOTJAR_ID)) .pipe(gulp.dest('./content/_book')); }); -gulp.task('copy-readme-to-index', function() { +gulp.task('copy-index-to-readme', function() { var rename = require("gulp-rename"); - return gulp.src(['./content/_book/**/README.html']) + return gulp.src(['./content/_book/**/index.html']) .pipe(rename({ - basename: 'index' + basename: 'README' })) .pipe(gulp.dest('./content/_book')); }); -gulp.task('replace-readme-to-index', function() { - return gulp.src([ - './content/_book/**/*.html', - './content/_book/**/*.js', - './content/_book/**/*.json' - ]) - .pipe(replace(/README\.html/gm, '')) +gulp.task('build-gitbook', shell.task([ + 'gitbook build content' +])); + +gulp.task('override-landingpage', function(callback){ + return gulp.src('./content-override/index.html') .pipe(gulp.dest('./content/_book')); }); +gulp.task('build', function(callback) { + runSequence( + 'build-gitbook', + 'copy-index-to-readme', + 'replace-font-path-pt-BR', + 'replace-font-path-en', + 'override-landingpage', + callback); +}); + // Deploying zipped files -gulp.task('publish-stage-gz', function() { +// gulp.task('deploy', ['build'], function() { +gulp.task('deploy', function() { var awspublish = require("gulp-awspublish"); var parallelize = require("concurrent-transform"); var awspublishRouter = require("gulp-awspublish-router"); + var gulpif = require('gulp-if'); + + // Select bucket + var yargs = require('yargs'); + var production = yargs.argv.production; + var bucket = process.env[ + "AWS_BUCKET_" + (production ? "PROD" : "STAGE") + ]; // create a new publisher var publisher = awspublish.create({ - key: process.env.AWS_ACCESS_KEY_ID, - secret: process.env.AWS_SECRET_KEY, - bucket: configs.deploy.bucket, + params: { + Bucket: bucket, + }, + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_KEY, region: 'sa-east-1', }); - var src = './content/_book/**/*.*'; + var src = gulp.src(['./content/_book/**/*.*', '!./content/_book/gitbook/**']); + if (!production) { + src = src + // Replacing analytics ua-code + .pipe( + gulpif(/.*\.html/, replace(/GA_UA_ID/, process.env.UA_CODE)) + ) + .pipe( + gulpif(/.*\.html/, replace(/GA_LEGACY_COOKIE_DOMAIN/, process.env.GA_LEGACY_COOKIE_DOMAIN)) + ) + // Replacing hotjar id + .pipe( + gulpif(/.*\.html/, replace(/hjid:12345678901/, 'hjid:' + process.env.HOTJAR_ID)) + ); + } - return gulp.src(src) - .pipe(awspublishRouter({ + return src.pipe(awspublishRouter({ routes: { "^(.*)README\.html$": { headers: { "Content-Type": "text/html", - "x-amz-website-redirect-location": "/$1" + "WebsiteRedirectLocation": "/$1" } }, "^.+$": { @@ -121,51 +139,5 @@ gulp.task('publish-stage-gz', function() { .pipe(awspublish.reporter()); }); -gulp.task('build-gitbook', shell.task([ - 'azk nvm gitbook build content' -])); - -gulp.task('override-landingpage', function(callback){ - return gulp.src('./content-override/index.html') - .pipe(gulp.dest('./content/_book')); -}); - -gulp.task('deploy-prod', function(callback) { - configs.deploy.bucket = process.env.AWS_BUCKET_PROD; - - runSequence('build-gitbook', - 'copy-readme-to-index', - 'replace-readme-to-index', - 'del-wrong-gitbook-folder', - 'replace-font-path-pt-BR', - 'replace-font-path-en', - 'override-landingpage', - 'replace-ga-tokens', - 'publish-stage-gz', - callback); -}); - -gulp.task('deploy-stage', function(callback) { - runSequence('build-gitbook', - 'copy-readme-to-index', - 'replace-readme-to-index', - 'del-wrong-gitbook-folder', - 'replace-font-path-pt-BR', - 'replace-font-path-en', - 'override-landingpage', - 'replace-ga-tokens', - 'publish-stage-gz', - callback); -}); - -gulp.task('build', function(callback) { - runSequence( - 'build-gitbook', - 'copy-readme-to-index', - 'replace-readme-to-index', - 'override-landingpage', - callback); -}); - gulp.task('default', ['build']); From a809699ebc0ecf619d8f94c75fb73fcc30a15de2 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 17:13:56 -0300 Subject: [PATCH 36/61] [docs] Building docs before deploying it --- docs/gulpfile.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/gulpfile.js b/docs/gulpfile.js index cd9c0e02..623664c3 100644 --- a/docs/gulpfile.js +++ b/docs/gulpfile.js @@ -70,8 +70,7 @@ gulp.task('build', function(callback) { }); // Deploying zipped files -// gulp.task('deploy', ['build'], function() { -gulp.task('deploy', function() { +gulp.task('deploy', ['build'], function() { var awspublish = require("gulp-awspublish"); var parallelize = require("concurrent-transform"); var awspublishRouter = require("gulp-awspublish-router"); From 0333ed2bed8510b5e6380d60985a4dde120050e2 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 18:04:10 -0300 Subject: [PATCH 37/61] [docs] Replacing GA/Hotjar IDs for production deployment + removing orphaned gulp tasks --- docs/gulpfile.js | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/docs/gulpfile.js b/docs/gulpfile.js index 623664c3..433a16ba 100644 --- a/docs/gulpfile.js +++ b/docs/gulpfile.js @@ -22,25 +22,6 @@ gulp.task('replace-font-path-en', function(){ .pipe(gulp.dest('./content/_book/en/gitbook/')); }); -gulp.task('replace-style.css-path-on-index', function(){ - return gulp.src(['./content/_book/index.html']) - .pipe(replace(/gitbook\/style\.css/gm, 'pt-BR/gitbook/style.css')) - .pipe(gulp.dest('./content/_book/')); -}); - -gulp.task('replace-ga-tokens', function() { - return gulp.src(['./content/_book/**/*.html']) - .pipe(replace(/GA_UA_ID/gm, process.env.GA_UA)) - .pipe(replace(/GA_LEGACY_COOKIE_DOMAIN/gm, process.env.GA_LEGACY_COOKIE_DOMAIN)) - .pipe(gulp.dest('./content/_book')); -}); - -gulp.task('replace-hotjar-token', function() { - return gulp.src(['./content/_book/**/*.html']) - .pipe(replace(/12345678901/gm, process.env.HOTJAR_ID)) - .pipe(gulp.dest('./content/_book')); -}); - gulp.task('copy-index-to-readme', function() { var rename = require("gulp-rename"); return gulp.src(['./content/_book/**/index.html']) @@ -94,7 +75,7 @@ gulp.task('deploy', ['build'], function() { }); var src = gulp.src(['./content/_book/**/*.*', '!./content/_book/gitbook/**']); - if (!production) { + if (production) { src = src // Replacing analytics ua-code .pipe( From 1f00e31bb5afd3d7d5af2054d0ef3a87adead5ef Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 18:04:45 -0300 Subject: [PATCH 38/61] [docs] Adding azk's changelog into book.json --- docs/content/book.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/book.json b/docs/content/book.json index 4ffca688..00bee865 100644 --- a/docs/content/book.json +++ b/docs/content/book.json @@ -3,7 +3,7 @@ "description": "Gitbook documentation for azk", "version": "2.6.4", "azkVersion": "0.13.0", - "releaseNotes": "http://......", + "releaseNotes": "https://github.com/azukiapp/azk/blob/master/CHANGELOG.md", "plugins": [ "ga", "hotjar", From 87700f8272a1a703ae9787e9e71bcad57689a55e Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 18:14:54 -0300 Subject: [PATCH 39/61] [docs] Fixing GA env var name in .env.sample and README.md --- docs/.env.sample | 2 +- docs/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/.env.sample b/docs/.env.sample index 3a53f153..99ed542d 100644 --- a/docs/.env.sample +++ b/docs/.env.sample @@ -2,6 +2,6 @@ AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX AWS_SECRET_KEY=XXXXXXXXXXXXXXXXX AWS_BUCKET_PROD=[bucket to prod deploy] AWS_BUCKET_STAGE=[bucket to stage deploy] -GA_UA=UA-XXXXXXXX-X +UA_CODE=UA-XXXXXXXX-X GA_LEGACY_COOKIE_DOMAIN=azk.io HOTJAR_ID=XXXXXXX diff --git a/docs/README.md b/docs/README.md index 793116e6..900e69f9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,7 +19,7 @@ AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX AWS_SECRET_KEY=XXXXXXXXXXXXXXXXX AWS_BUCKET_PROD=[bucket to prod deploy] AWS_BUCKET_STAGE=[bucket to stage deploy] -GA_UA=UA-XXXXXXXX-X +UA_CODE=UA-XXXXXXXX-X GA_LEGACY_COOKIE_DOMAIN=azk.io HOTJAR_ID=XXXXXXX ``` From 63c60a156a637f19b2ff80959196fc3fdc97ea5f Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 19:22:21 -0300 Subject: [PATCH 40/61] [docs] Moving CONTRIBUTING.md into .github folder --- docs/Azkfile.js | 10 +++++----- docs/content/en/contributing/README.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Azkfile.js b/docs/Azkfile.js index 29c1f962..58b16b00 100644 --- a/docs/Azkfile.js +++ b/docs/Azkfile.js @@ -17,11 +17,11 @@ systems({ command: "gitbook serve --port $HTTP_PORT content --lrport $LIVERELOAD_PORT", wait: {"retry": 20, "timeout": 3000}, mounts: { - '/azk/#{manifest.dir}' : sync("."), - '/azk/CONTRIBUTING.md' : path("../.github/CONTRIBUTING.md"), - '/azk/package.json' : path("./package.json"), - '/azk/#{manifest.dir}/content/pt-BR/styles' : sync("./content/common/styles"), - '/azk/#{manifest.dir}/content/en/styles' : sync("./content/common/styles"), + '/azk/#{manifest.dir}' : sync(".", {shell: true}), + '/azk/.github/CONTRIBUTING.md' : path("../.github/CONTRIBUTING.md"), + '/azk/package.json' : path("./package.json"), + '/azk/#{manifest.dir}/content/pt-BR/styles' : sync("./content/common/styles", {shell: true}), + '/azk/#{manifest.dir}/content/en/styles' : sync("./content/common/styles", {shell: true}), '/azk/#{manifest.dir}/node_modules' : persistent("node_modules"), '/azk/#{manifest.dir}/content/_book' : persistent("book"), '/azk/#{manifest.dir}/content/node_modules' : persistent("content/node_modules"), diff --git a/docs/content/en/contributing/README.md b/docs/content/en/contributing/README.md index 069558fa..2cf24def 120000 --- a/docs/content/en/contributing/README.md +++ b/docs/content/en/contributing/README.md @@ -1 +1 @@ -../../../../CONTRIBUTING.md \ No newline at end of file +../../../../.github/CONTRIBUTING.md \ No newline at end of file From 8ca8775c22dbf356a7be771569df82fdf202d8a3 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 19:31:34 -0300 Subject: [PATCH 41/61] [Sync] During sync, fixing ownership of the symlink itself, not of the target file. --- CHANGELOG.md | 3 +++ src/system/run.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a301dcc..64e094c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). * [Docs] The instructions for use `azk` and `Wordpress` have been improved; * [Manifest] Adding `resolve` options to `path`, with default `true` value; +* Bug + * [Sync] Fixing ownership of the symlink itself, not of the target file; + ## v0.17.0 - (2016-02-19) * Enhancements diff --git a/src/system/run.js b/src/system/run.js index fecac561..a768723d 100644 --- a/src/system/run.js +++ b/src/system/run.js @@ -462,7 +462,7 @@ var Run = { var mounted_sync_folders = '/sync_folders'; var current_sync_folder = path.join(mounted_sync_folders, system.manifest.namespace, system.name, host_folder); - var find_exec = `-exec chown ${uid}:${gid} '{}' \\;`; + var find_exec = `-exec chown -h ${uid}:${gid} '{}' \\;`; var find_args = `${current_sync_folder} \\( -not -user ${uid} -or -not -group ${gid} \\) ${find_exec}`; // Script to fix sync folder From 40bbf410f21c7f83ce347e57360fc6a01fff9ae8 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Tue, 1 Mar 2016 17:33:38 -0300 Subject: [PATCH 42/61] [Package] Updating regexp to extract 'azk version' --- Makefile | 4 ++-- shared/scripts/install.sh | 3 +-- src/libexec/package-tools/arch/generate.sh | 2 +- src/libexec/package-tools/mac/generate.sh | 2 +- src/libexec/package-tools/mac/test.sh | 4 ++-- src/libexec/package-tools/pack.sh | 4 ++-- src/libexec/package-tools/test-container.sh | 2 +- src/libexec/package-tools/test.sh | 2 +- src/libexec/package-tools/ubuntu/generate.sh | 2 +- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 6e9dc62f..0bca2930 100644 --- a/Makefile +++ b/Makefile @@ -160,13 +160,13 @@ package_clean: @echo "task: $@" @rm -Rf ${AZK_PACKAGE_PREFIX}/..?* ${AZK_PACKAGE_PREFIX}/.[!.]* ${AZK_PACKAGE_PREFIX}/* -check_version: NEW_AZK_VERSION=$(shell ${PATH_USR_LIB_AZK}/bin/azk version | sed -e 's/^azk version\ //; s/,.*//') +check_version: NEW_AZK_VERSION=$(shell ${PATH_USR_LIB_AZK}/bin/azk version | sed -e 's/^azk //; s/^version //; s/,.*//' ) check_version: @echo "task: $@" @if [ ! "${AZK_VERSION}" = "${NEW_AZK_VERSION}" ] ; then \ echo 'Error to run: ${PATH_USR_LIB_AZK}/bin/azk version'; \ echo 'Expect: azk ${AZK_VERSION}'; \ - echo 'Output: ${NEW_AZK_VERSION}'; \ + echo 'Output: azk ${NEW_AZK_VERSION}'; \ exit 1; \ fi diff --git a/shared/scripts/install.sh b/shared/scripts/install.sh index 0f34f053..e47bc4b3 100755 --- a/shared/scripts/install.sh +++ b/shared/scripts/install.sh @@ -422,9 +422,8 @@ check_azk_installation() { } azk_is_up_to_date() { + AZK_CURRENT_VERSION=$(azk version | sed -e 's/^azk //; s/^version //; s/,.*//') AZK_TAGS_URL="https://api.github.com/repos/azukiapp/azk/tags" - AZK_VERSIONS=$(curl -sSL ${AZK_TAGS_URL} | grep name) - AZK_CURRENT_VERSION=$(azk version | sed -e 's/^azk version\ //; s/,.*//') AZK_LATEST_VERSION=$( curl -sSL ${AZK_TAGS_URL} | \ grep name | \ head -1 | \ diff --git a/src/libexec/package-tools/arch/generate.sh b/src/libexec/package-tools/arch/generate.sh index 74c601a9..3426054c 100755 --- a/src/libexec/package-tools/arch/generate.sh +++ b/src/libexec/package-tools/arch/generate.sh @@ -21,7 +21,7 @@ abs_dir() { export AZK_ROOT_PATH=`cd \`abs_dir ${BASH_SOURCE:-$0}\`/../../..; pwd` export PATH=${AZK_ROOT_PATH}/bin:$PATH -export VERSION=${1:-$( azk version | sed -e 's/^azk version\ //; s/,.*//' )} +export VERSION=${1:-$( azk version | sed -e 's/^azk //; s/^version //; s/,.*//' )} export AUR_REPO_DIR=${2:-"/tmp/aur-azk"} RELEASE_CHANNEL=$( echo "${VERSION}" | sed s/[^\\-]*// | sed s/^\\-// | sed s/\\..*// ) diff --git a/src/libexec/package-tools/mac/generate.sh b/src/libexec/package-tools/mac/generate.sh index e3b7e80f..ef758a11 100755 --- a/src/libexec/package-tools/mac/generate.sh +++ b/src/libexec/package-tools/mac/generate.sh @@ -12,7 +12,7 @@ if [[ -z "${MAC_REPO_STAGE_BRANCH}" ]]; then exit 2 fi -export VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) +export VERSION=$( azk version | sed -e 's/^azk //; s/^version //; s/,.*//' ) SHA256=$(shasum -a 256 shasum -a 256 "package/brew/azk_${VERSION}.tar.gz" | awk '{print $1}') diff --git a/src/libexec/package-tools/mac/test.sh b/src/libexec/package-tools/mac/test.sh index 551cab80..51ab6c45 100755 --- a/src/libexec/package-tools/mac/test.sh +++ b/src/libexec/package-tools/mac/test.sh @@ -2,7 +2,7 @@ set -x -export VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) +export VERSION=$( azk version | sed -e 's/^azk //; s/^version //; s/,.*//' ) BASE_DIR=$( pwd ) SHA256=$(shasum -a 256 shasum -a 256 "package/brew/azk_${VERSION}.tar.gz" | awk '{print $1}') @@ -82,7 +82,7 @@ fi cd $FORMULA_DIR git checkout $FORMULA_FILE -BAZK_VERSION=$(bazk version | sed -e 's/^azk version\ //; s/,.*//') +BAZK_VERSION=$(bazk version | sed -e 's/^azk //; s/^version //; s/,.*//') if [[ "${BAZK_VERSION}" == "${VERSION}" ]]; then echo "azk ${VERSION} has been successfully installed." else diff --git a/src/libexec/package-tools/pack.sh b/src/libexec/package-tools/pack.sh index 6c009e15..d19397ee 100755 --- a/src/libexec/package-tools/pack.sh +++ b/src/libexec/package-tools/pack.sh @@ -288,7 +288,7 @@ if [[ $BUILD_DEB == true ]]; then step_run "Cleaning environment" rm -Rf package/deb package/public fi - step_run "Downloading libnss-resolver" \ + step_run "Downloading libnss-resolver" --exit \ mkdir -p package/deb \ && wget -q "${LIBNSS_RESOLVER_REPO}/ubuntu12-libnss-resolver_${LIBNSS_RESOLVER_VERSION}_amd64.deb" -O "package/deb/precise-libnss-resolver_${LIBNSS_RESOLVER_VERSION}_amd64.deb" \ && wget -q "${LIBNSS_RESOLVER_REPO}/ubuntu14-libnss-resolver_${LIBNSS_RESOLVER_VERSION}_amd64.deb" -O "package/deb/trusty-libnss-resolver_${LIBNSS_RESOLVER_VERSION}_amd64.deb" \ @@ -337,7 +337,7 @@ if [[ $BUILD_RPM == true ]]; then [[ ${CLEAN_REPO} == true ]] && step_run "Cleaning environment" rm -Rf package/rpm package/fedora20 package/fedora23 - step_run "Downloading libnss-resolver" \ + step_run "Downloading libnss-resolver" --exit \ mkdir -p package/rpm \ && wget "${LIBNSS_RESOLVER_REPO}/fedora20-libnss-resolver-${LIBNSS_RESOLVER_VERSION}-1.x86_64.rpm" -O "package/rpm/fedora20-libnss-resolver-${LIBNSS_RESOLVER_VERSION}-1.x86_64.rpm" \ && wget "${LIBNSS_RESOLVER_REPO}/fedora23-libnss-resolver-${LIBNSS_RESOLVER_VERSION}-1.x86_64.rpm" -O "package/rpm/fedora23-libnss-resolver-${LIBNSS_RESOLVER_VERSION}-1.x86_64.rpm" diff --git a/src/libexec/package-tools/test-container.sh b/src/libexec/package-tools/test-container.sh index add53409..58f28114 100755 --- a/src/libexec/package-tools/test-container.sh +++ b/src/libexec/package-tools/test-container.sh @@ -69,7 +69,7 @@ setup() { run_test() { set -e - DETECTED_VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) + DETECTED_VERSION=$( azk version | sed -e 's/^azk //; s/^version //; s/,.*//' ) if [[ "${DETECTED_VERSION}" != "${VERSION}" ]]; then echo "Version check failed." diff --git a/src/libexec/package-tools/test.sh b/src/libexec/package-tools/test.sh index 2ec43991..55fe7ce3 100755 --- a/src/libexec/package-tools/test.sh +++ b/src/libexec/package-tools/test.sh @@ -15,7 +15,7 @@ set -e BASE_DIR=$( echo $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) | sed s#$(pwd)/##g ) -export VERSION=$( bin/azk version | sed -e 's/^azk version\ //; s/,.*//' ) +export VERSION=$( bin/azk version | sed -e 's/^azk //; s/^version //; s/,.*//' ) export SO=$1 if [[ $# == 2 ]]; then diff --git a/src/libexec/package-tools/ubuntu/generate.sh b/src/libexec/package-tools/ubuntu/generate.sh index 8b6be755..0d4741bb 100755 --- a/src/libexec/package-tools/ubuntu/generate.sh +++ b/src/libexec/package-tools/ubuntu/generate.sh @@ -28,7 +28,7 @@ set -e export PATH=`pwd`/bin:$PATH DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -export VERSION=$( azk version | sed -e 's/^azk version\ //; s/,.*//' ) +export VERSION=$( azk version | sed -e 's/^azk //; s/^version //; s/,.*//' ) export SECRET_KEY=$1 export LIBNSS_RESOLVER_VERSION=$2 export DISTRO=$3 && export REPO=azk-${DISTRO} From 5e3a3a26f95bff9e624e652d8c1c60889e043c1e Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Fri, 1 Apr 2016 12:41:39 -0300 Subject: [PATCH 43/61] [Spec] Fixing tests for 'azk version' --- spec/cmds/init_spec.js | 10 ++++++++++ spec/integration/common/cmds/docker.bats | 2 +- spec/integration/common/cmds/shell.bats | 2 +- spec/integration/common/cmds/version.bats | 2 +- src/cmds/init.js | 9 +++++---- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/spec/cmds/init_spec.js b/spec/cmds/init_spec.js index 24a1b7a5..fb85ea76 100644 --- a/spec/cmds/init_spec.js +++ b/spec/cmds/init_spec.js @@ -15,6 +15,16 @@ describe('Azk cli, init controller', function() { var doc_opts = { exit: false }; var run_options = { ui: ui }; + it("should return manifest filename", function() { + doc_opts.argv = ['init', '--filename']; + var options = cli.docopt(doc_opts); + return cli.run(doc_opts, run_options).then((code) => { + h.expect(options).to.have.property('--filename', true); + h.expect(code).to.equal(0); + h.expect(outputs[0]).to.equal(config('manifest')); + }); + }); + describe("run in a project already has a manifest", function() { var message = t("commands.init.already_exists", manifest); diff --git a/spec/integration/common/cmds/docker.bats b/spec/integration/common/cmds/docker.bats index e35ee39e..0eb00297 100644 --- a/spec/integration/common/cmds/docker.bats +++ b/spec/integration/common/cmds/docker.bats @@ -28,7 +28,7 @@ image_name="azukiapp/azktcl" run azk docker -- build -t $image . assert_success - assert_match "Step 0 : FROM ${image_name}:${image_tag}" + assert_match "Step 1 : FROM ${image_name}:${image_tag}" run azk docker rmi $image assert_success diff --git a/spec/integration/common/cmds/shell.bats b/spec/integration/common/cmds/shell.bats index 477f1184..15cf7f1a 100644 --- a/spec/integration/common/cmds/shell.bats +++ b/spec/integration/common/cmds/shell.bats @@ -17,7 +17,7 @@ image_name="azukiapp/azktcl" @test "$test_label run shell with shell-args" { cmd="ls -l /; echo 'foo bar'; uname" - run azk --log=debug shell --image "${image_name}:${image_tag}" --shell /bin/bash -- -c "${cmd}" + run azk --log=debug shell --image "${image_name}:${image_tag}" --shell /bin/bash -- "${cmd}" assert_success assert_match "d.*bin" assert_match "d.*etc" diff --git a/spec/integration/common/cmds/version.bats b/spec/integration/common/cmds/version.bats index c60e1249..47259952 100644 --- a/spec/integration/common/cmds/version.bats +++ b/spec/integration/common/cmds/version.bats @@ -8,5 +8,5 @@ load ../../test_helper run azk version assert_success - assert_output "azk $version" + assert_match "azk version $version, build .*, date .*" } diff --git a/src/cmds/init.js b/src/cmds/init.js index 32bb8c8a..3771cc4a 100644 --- a/src/cmds/init.js +++ b/src/cmds/init.js @@ -1,6 +1,6 @@ import { CliController } from 'cli-router'; import { _, config, fsAsync, path, lazy_require, log } from 'azk'; -import { async, promiseResolve } from 'azk/utils/promises'; +import { async } from 'azk/utils/promises'; var lazy = lazy_require({ Generator : ['azk/generator'], @@ -11,7 +11,8 @@ export default class Init extends CliController { index(params) { return async(this, function* () { if (params.filename) { - return this.showFilename(); + this.showFilename(); + return 0; } var generator = new lazy.Generator(this.ui); @@ -22,7 +23,7 @@ export default class Init extends CliController { var manifest_exists = yield fsAsync.exists(file); if (manifest_exists && !params.force) { this.ui.fail(this.ui.tKeyPath(this.name, "already_exists"), manifest); - return promiseResolve(1); + return 1; } var systemsData = yield generator.findSystems(cwd); @@ -42,7 +43,7 @@ export default class Init extends CliController { this.ui.ok(this.ui.tKeyPath(this.name, 'github')); } - return promiseResolve(0); + return 0; }); } From 0b1191ad2ddbbd3cd4316ca0553b60d4bc68bfad Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 31 Mar 2016 15:37:53 -0300 Subject: [PATCH 44/61] [Code] Avoiding /docs folder to be synced for azk's containers --- Azkfile.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Azkfile.js b/Azkfile.js index 34748c3b..7ae2a394 100644 --- a/Azkfile.js +++ b/Azkfile.js @@ -3,10 +3,11 @@ */ var mounts = { - "/azk/#{manifest.dir}": sync('./', { shell: true }), - "/azk/#{manifest.dir}/package": path("./package"), - "/azk/#{manifest.dir}/node_modules": persistent('node_modules-#{system.name}'), - "/azk/#{manifest.dir}/.package-envs": path("./.package-envs"), + "/azk/#{manifest.dir}" : sync('./', { shell: true }), + "/azk/#{manifest.dir}/package" : path("./package"), + "/azk/#{manifest.dir}/docs" : path("./docs"), + "/azk/#{manifest.dir}/node_modules" : persistent('node_modules-#{system.name}'), + "/azk/#{manifest.dir}/.package-envs" : path("./.package-envs"), "/azk/demos" : path("../demos"), "/azk/build" : persistent('build-#{system.name}'), "/azk/lib" : persistent('lib-#{system.name}'), From 3afb496ab3547e18aa378ee0bd37c6f4a2b10a84 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 7 Mar 2016 19:12:55 -0300 Subject: [PATCH 45/61] [Docs] Adding Arch Linux as supported distribution --- docs/content/en/installation/linux.md | 18 +++++++++++++++--- docs/content/links.md | 1 + docs/content/pt-BR/installation/linux.md | 17 +++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/content/en/installation/linux.md b/docs/content/en/installation/linux.md index 6323488f..8439aad7 100644 --- a/docs/content/en/installation/linux.md +++ b/docs/content/en/installation/linux.md @@ -4,9 +4,9 @@ ## Requirements -* **Distributions (tested)**: Ubuntu 12.04/14.04/15.10 and Fedora 20/21/22 +* **Distributions (tested)**: Ubuntu 12.04/14.04/15.10, Fedora 20/21/22 and Arch Linux * **Architecture**: 64-bits -* [Docker][docker] 1.8.1 +* [Docker][docker] 1.8.1 or later * Not running any services on ports `80` and `53` **Important**: If you are running any service on port `80` and/or `53` you must customize the configuration of `azk` setting the following variables `AZK_BALANCER_PORT` and `AZK_DNS_PORT` respectively, before running `azk agent start`. @@ -27,6 +27,7 @@ There are two ways to install Docker: - [Ubuntu][docker_ubuntu_installation] - [Fedora][docker_fedora_installation] + - [Arch Linux][docker_arch_installation] ## Granting access to Docker service to your user @@ -38,7 +39,7 @@ To avoid having to use sudo when you use the docker command, create a Unix group To create the docker group and add your user: -1. Log into Ubuntu as a user with sudo privileges; +1. Log in as a user with sudo privileges; 2. Create the docker group and add your user @@ -147,6 +148,17 @@ The easiest way to install `azk` is to use the script below. It will identify yo 4. You can [start the azk agent](../getting-started/starting-agent.md) now, but, **make sure that the Docker service is running**; +### Arch Linux + +> You'll need `yaourt` or some other tool to get packages from [AUR](https://aur.archlinux.org/). To learn how to install `yaourt`, check [these instructions](https://archlinux.fr/yaourt-en). + +1. Install `azk` and its dependencies: + + ```bash + $ yaourt -S azk + ``` + +2. You can [start the azk agent](../getting-started/starting-agent.md) now, but, **make sure that the Docker service is running**; ### Other distributions diff --git a/docs/content/links.md b/docs/content/links.md index 7e84352f..6072d4dd 100644 --- a/docs/content/links.md +++ b/docs/content/links.md @@ -7,6 +7,7 @@ [docker_install]: https://docs.docker.com/installation/#installation [docker_ubuntu_installation]: http://docs.docker.com/engine/installation/ubuntulinux/ [docker_fedora_installation]: http://docs.docker.com/engine/installation/fedora/ +[docker_arch_installation]: http://docs.docker.com/engine/installation/linux/archlinux/ [docker_daemon_attack_surface]: https://docs.docker.com/engine/articles/security/#docker-daemon-attack-surface [postgres]: http://www.postgresql.org/ diff --git a/docs/content/pt-BR/installation/linux.md b/docs/content/pt-BR/installation/linux.md index c5b58b7a..eb0ed3cb 100644 --- a/docs/content/pt-BR/installation/linux.md +++ b/docs/content/pt-BR/installation/linux.md @@ -4,9 +4,9 @@ ## Requisitos -* **Distribuições (testadas)**: Ubuntu 12.04/14.04/15.10 e Fedora 20/21/22 +* **Distribuições (testadas)**: Ubuntu 12.04/14.04/15.10, Fedora 20/21/22 e Arch Linux * **Arquitetura**: 64-bits -* [Docker][docker] 1.8.1 +* [Docker][docker] 1.8.1 ou mais recente * Não estar rodando nenhum serviço nas portas `80` e `53` **Importante**: Se você estiver rodando algum serviço nas portas `80` e/ou `53` você deve customizar a configuração do `azk` definindo as seguintes variáveis `AZK_BALANCER_PORT` e `AZK_DNS_PORT` respectivamente, antes de executar o `azk agent start`. @@ -27,6 +27,7 @@ Existem duas formas de instalação do Docker: - [Ubuntu][docker_ubuntu_installation] - [Fedora][docker_fedora_installation] + - [Arch Linux][docker_arch_installation] ## Dando acesso ao serviço do Docker para o seu usuário @@ -147,6 +148,18 @@ A forma mais fácil de instalar o `azk` é utilizar o script abaixo. Ele vai ide 4. Você pode [iniciar o agent](../getting-started/starting-agent.md) agora, porém, **tenha certeza de que o serviço do Docker está rodando**; +### Arch Linux + +> Você precisará do `yaourt` ou de alguma outra ferramenta para obter pacotes do [AUR](https://aur.archlinux.org/). Para aprender como instalar o `yaourt`, [siga essas instruções](https://archlinux.fr/yaourt-en) como instalar o `yaourt`. + +1. Instale o `azk` e suas dependências: + + ```bash + $ yaourt -S azk + ``` + +2. Você pode [iniciar o agent](../getting-started/starting-agent.md) agora, porém, **tenha certeza de que o serviço do Docker está rodando**; + ### Outras distribuições Em breve... From e9cdc1b72cce8ecf2ff3a2c5f888113ef867466c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?E=CC=81verton=20Ribeiro?= Date: Fri, 1 Apr 2016 19:21:00 -0300 Subject: [PATCH 46/61] [Cli] Enabling back deprecation warnings. --- CHANGELOG.md | 1 + shared/locales/en-US.js | 3 ++- src/cli/helpers.js | 25 ++++++++++++++----------- src/config.js | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51652017..d4968eb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Bug * [Sync] Fixing ownership of the symlink itself, not of the target file; + * [Cli] Enabling back deprecation warnings; ## v0.17.0 - (2016-02-19) diff --git a/shared/locales/en-US.js b/shared/locales/en-US.js index ef6d6576..853fb40a 100644 --- a/shared/locales/en-US.js +++ b/shared/locales/en-US.js @@ -292,7 +292,8 @@ module.exports = { system_name_invalid: "The system name `%(system)s` is not valid.", required_path : "Manifest class require a project path", validate : { - deprecated : "The `%(option)s` used in `%(system)s` is deprecated, check the documentation for `%(new_option)s`", + deprecated_title: "${red}List of deprecations:${red.close}", + deprecated : "- The `%(option)s` used in `%(system)s` is deprecated, check the documentation for `%(new_option)s`", no_system_set: "No system has been set yet, check the documentation", invalid_option_value: [ "Invalid value for `%(option)s`. Value: `%(value)s`.", diff --git a/src/cli/helpers.js b/src/cli/helpers.js index fc3eb719..b648d786 100644 --- a/src/cli/helpers.js +++ b/src/cli/helpers.js @@ -42,18 +42,23 @@ var Helpers = { }); }, - manifestValidate(cmd, manifest) { + manifestValidate(ui, manifest) { var validation_errors = manifest.validate(); if (validation_errors.length === 0) { return; } // has deprecate errors? - if (config('flags:show_deprecate')) { + if (!config('flags:hide_deprecate')) { var deprecate_val_errors = _.filter(validation_errors, function (item) { return item.level === 'deprecate'; }); - _.each(deprecate_val_errors, (deprecate_val_error) => { - cmd.deprecate(`manifest.validate.${deprecate_val_error.key}`, deprecate_val_error); - }); + if (deprecate_val_errors.length > 0) { + ui.output(""); + ui.deprecate("manifest.validate.deprecated_title"); + _.each(deprecate_val_errors, (deprecate_val_error) => { + ui.deprecate(`manifest.validate.${deprecate_val_error.key}`, deprecate_val_error); + }); + ui.output(""); + } } // has fails level errors? @@ -61,12 +66,10 @@ var Helpers = { return item.level === 'fail'; }); - if (config('flags:show_deprecate')) { - _.each(val_errors, (val_error) => { - var msg = t(`manifest.validate.${val_error.key}`, val_error); - throw new ManifestError(this.file, msg); - }); - } + _.each(val_errors, (val_error) => { + var msg = t(`manifest.validate.${val_error.key}`, val_error); + throw new ManifestError(this.file, msg); + }); }, vmStartProgress(cmd) { diff --git a/src/config.js b/src/config.js index 302889c9..34b12e4e 100644 --- a/src/config.js +++ b/src/config.js @@ -55,7 +55,7 @@ var options = mergeConfig({ azk_last_commit_id: envs("AZK_LAST_COMMIT_ID"), azk_last_commit_date: envs("AZK_LAST_COMMIT_DATE"), flags : { - show_deprecate: envs('AZK_HIDE_DEPRECATE', false), + hide_deprecate: envs('AZK_HIDE_DEPRECATE', false), require_accept_use_terms: envs('AZK_REQUIRE_TERMS', true), force_color: envs('AZK_FORCE_COLOR', envs('FORCE_COLOR', null)), }, From 2ed3cee986d93abc3b6d66d8a5d3282e57e800e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Fri, 1 Apr 2016 19:28:08 -0300 Subject: [PATCH 47/61] [Manifest] Deprecating template expander tokens --- CHANGELOG.md | 3 ++ shared/locales/en-US.js | 1 + spec/manifest/validate_spec.js | 51 +++++++++++++++++++++++++++++ src/manifest/index.js | 60 +++++++++++++++++++--------------- src/manifest/validate.js | 39 ++++++++++++++++++++-- 5 files changed, 125 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4968eb9..2e7f2b4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). * [Manifest] Adding `resolve` options to `path`, with default `true` value; * [Docs] Updating whole docs structure: Azkfile, Gitbook version and plugins; +* Deprecations + * [Manifest] Deprecating template expander tokens `${}` and `<%%>`; + * Bug * [Sync] Fixing ownership of the symlink itself, not of the target file; * [Cli] Enabling back deprecation warnings; diff --git a/shared/locales/en-US.js b/shared/locales/en-US.js index 853fb40a..d10f9f2b 100644 --- a/shared/locales/en-US.js +++ b/shared/locales/en-US.js @@ -294,6 +294,7 @@ module.exports = { validate : { deprecated_title: "${red}List of deprecations:${red.close}", deprecated : "- The `%(option)s` used in `%(system)s` is deprecated, check the documentation for `%(new_option)s`", + deprecated_token: "- `%(token_open)s%(token_close)s` is deprecated, use `${green}%(suggestion)s${green.close}` instead of `${red}%(original)s${red.close}` on `${yellow}%(system)s.%(option)s${yellow.close}`", no_system_set: "No system has been set yet, check the documentation", invalid_option_value: [ "Invalid value for `%(option)s`. Value: `%(value)s`.", diff --git a/spec/manifest/validate_spec.js b/spec/manifest/validate_spec.js index 8111e220..41f20ff0 100644 --- a/spec/manifest/validate_spec.js +++ b/spec/manifest/validate_spec.js @@ -22,6 +22,57 @@ describe("Azk manifest class, validate set", function() { h.expect(valid_errors[0]).to.have.property("level", "warning"); }); + it("should return deprecate string interpolation that are different #{}", function() { + var content = ` + system('system1', { + image: { docker: "any" }, + http : { domains: ["foo.\${azk.default_domain}"] }, + command: ["echo", "\$HTTP_PORT"], + workdir: "#{manifest.dirname}", + }); + system('system2', { + image: { docker: "any" }, + workdir: "\${-manifest.dirname}", + command: "bundle exec <%=system.name%>", + mounts: { + "/azk/\${system.name}": path("./"), + } + }); + `; + + return h.mockManifestWithContent(content).then((mf) => { + var valid_errors = mf.validate(); + + h.expect(valid_errors).to.instanceof(Array); + h.expect(valid_errors).to.length(4); + + var key = "deprecated_token"; + var level = "deprecate"; + h.expect(valid_errors).to.containSubset([ + { + key, level, + original: "${azk.default_domain}", suggestion: "#{azk.default_domain}", + token_open: "${", token_close: "}", option: "http.domains", system: "system1" + }, + { + key, level, + original: "${-manifest.dirname}", suggestion: "#{manifest.dirname}", + token_open: "${-", token_close: "}", option: "workdir", system: "system2" + }, + { + key, level, + original: "<%=system.name%>", suggestion: "#{system.name}", + token_open: "<%=", token_close: "%>", option: "command", system: "system2" + }, + { + key, level, + original: "${system.name}", suggestion: "#{system.name}", + token_open: "${", token_close: "}", option: "mounts", system: "system2" + }, + ]); + }); + }); + it("should return deprecate use http hostname", function() { var content = ` system('system1', { diff --git a/src/manifest/index.js b/src/manifest/index.js index 099af499..e1fa0e39 100644 --- a/src/manifest/index.js +++ b/src/manifest/index.js @@ -34,14 +34,14 @@ var ManifestDsl = { }, // Systems - system(name, data) { - this.addSystem(name, data); + system(name, properties) { + this.addSystem(name, properties); }, systems(allSystems) { this.extendsSystems(allSystems); - _.each(allSystems, (data, name) => { - this.addSystem(name, data); + _.each(allSystems, (properties, name) => { + this.addSystem(name, properties); }); }, @@ -134,17 +134,19 @@ export class Manifest { } extendsSystems(allSystems) { - _.each(allSystems, (data, name) => { - if (!(data instanceof System)) { - if (data.extends) { + _.each(allSystems, (properties, name) => { + if (!(properties instanceof System)) { + if (properties.extends) { + let raw = _.cloneDeep(properties); + // validate is extends system exists - if (!allSystems[data.extends]) { - var msg = t("manifest.extends_system_invalid", { system_source: data.extends, + if (!allSystems[properties.extends]) { + var msg = t("manifest.extends_system_invalid", { system_source: properties.extends, system_to_extend: name }); throw new ManifestError(this.file, msg); } - var sourceSystem = _.cloneDeep(allSystems[data.extends]); + var sourceSystem = _.cloneDeep(allSystems[properties.extends]); var destinationSystem = allSystems[name]; // if "depends" or "image" is null ignore these properties @@ -158,6 +160,9 @@ export class Manifest { // get all from sourceSystem but override with destinationSystem _.assign(sourceSystem, destinationSystem); allSystems[name] = sourceSystem; + + // Set raw data + allSystems[name].raw = raw; } } }); @@ -165,15 +170,16 @@ export class Manifest { return allSystems; } - addSystem(name, data) { - if (!(data instanceof System)) { - this._system_validate(name, data); - var image = data.image; - delete data.image; - data = new System(this, name, image, data); + addSystem(name, properties) { + if (!(properties instanceof System)) { + properties.raw = properties.raw || _.cloneDeep(properties); + this._system_validate(name, properties); + var image = properties.image; + delete properties.image; + properties = new System(this, name, image, properties); } - this.systems[name] = data; + this.systems[name] = properties; if (!this._default) { this._default = name; } @@ -182,39 +188,39 @@ export class Manifest { } // TODO: refactoring to use validate - _system_validate(name, data) { + _system_validate(name, properties) { var msg, opts; // system_name must not contain anything not valid in docker container name if (!name.match(/^[a-zA-Z0-9-]+$/)) { msg = t("manifest.system_name_invalid", { system: name }); throw new ManifestError(this.file, msg); } - if (data.extends === name) { + if (properties.extends === name) { msg = t("manifest.cannot_extends_itself", { system: name }); throw new ManifestError(this.file, msg); } - if (_.isEmpty(data.image)) { + if (_.isEmpty(properties.image)) { msg = t("manifest.image_required", { system: name }); throw new ManifestError(this.file, msg); } - if (!_.isEmpty(data.balancer)) { + if (!_.isEmpty(properties.balancer)) { msg = t("manifest.balancer_deprecated", { system: name }); throw new ManifestError(this.file, msg); } - if (!_.isEmpty(data.mount_folders)) { + if (!_.isEmpty(properties.mount_folders)) { opts = { option: 'mount_folders', system: name, manifest: this.file }; msg = t("manifest.mount_and_persistent_deprecated", opts); throw new ManifestError(this.file, msg); } - if (!_.isEmpty(data.persistent_folders)) { + if (!_.isEmpty(properties.persistent_folders)) { opts = { option: 'persistent_folders', system: name, manifest: this.file }; msg = t("manifest.mount_and_persistent_deprecated", opts); throw new ManifestError(this.file, msg); } // Not support docker_extra.start and docker_extra.create - var extra = data.docker_extra; - if (_.has(extra, "start") || _.has(data, "create")) { + var extra = properties.docker_extra; + if (_.has(extra, "start") || _.has(properties, "create")) { var option = _.has(extra, "start") ? "start" : "create"; opts = { option: `docker_extra.${option}`, system: name, manifest: this.file }; msg = t("manifest.extra_docker_start_deprecated", opts); @@ -269,9 +275,9 @@ export class Manifest { var result = tsort(edges); if (result.error) { - var data = result.error.message.match(/^(.*?)\s.*\s(.*)$/); + var properties = result.error.message.match(/^(.*?)\s.*\s(.*)$/); var msg = t("manifest.circular_dependency", { - system1: data[1], system2: data[2] + system1: properties[1], system2: properties[2] }); throw new ManifestError(this.file, msg); } diff --git a/src/manifest/validate.js b/src/manifest/validate.js index cc7b837c..090d678a 100644 --- a/src/manifest/validate.js +++ b/src/manifest/validate.js @@ -8,10 +8,10 @@ export class Validate { var validations = [ this._have_systems(manifest), this._have_old_http_hostname(manifest), this._have_old_image_definition(manifest), - this._validate_wait_option(manifest)]; + this._validate_wait_option(manifest), + this._have_dollar_sign_string_interpotation(manifest)]; validations.forEach(function(validation) { - if (validation && validation.length > 0) { errors = errors.concat(validation); } @@ -47,6 +47,41 @@ export class Validate { }, []); } + static _have_dollar_sign_string_interpotation(manifest) { + // https://regex101.com/r/eZ4mQ6/3 + var dollar_check = /((?:[$]{|<%|#{-|#{=)[=|-]?)(.*\..*)(}|%>)/; + return _.reduce(manifest.systems, (errors, system) => { + var raw = system.options.raw; + var check = (value, key) => { + if (_.isString(value)) { + let match = value.match(dollar_check); + if (match) { + errors.push( + this._entry('deprecate', 'deprecated_token', manifest, { + token_open: match[1], + token_close: match[3], + original: match[0], + suggestion: `#{${match[2]}}`, + option: key, + value: value, + system: system.name, + }) + ); + } + } else if (_.isArray(value)) { + _.each(value, (v) => check(v, key)); + } else if (_.isObject(value)) { + _.each(value, (v, k) => { + check(k, key); + check(v, `${key}.${k}`); + }); + } + }; + _.each(raw, check); + return errors; + }, []); + } + static _validate_wait_option(manifest) { return _.reduce(manifest.systems, (errors, system) => { From e566a26b40432c11c558ac85f6e579e130edf0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?E=CC=81verton=20Ribeiro?= Date: Fri, 1 Apr 2016 20:57:36 -0300 Subject: [PATCH 48/61] [Manifest] Expanding envs vars before running systems --- docs/Azkfile.js | 2 +- docs/content/links.md | 4 + docs/content/pt-BR/SUMMARY.md | 10 +- docs/content/pt-BR/adding-services/README.md | 2 +- .../azkfilejs/environment_variables.md | 122 ++++++++++++++++++ spec/fixtures/test-app/bin/test-app | 3 + spec/spec_helpers/mock_manifest.js | 7 +- spec/system/index_spec.js | 34 ++--- spec/system/run_spec.js | 11 ++ src/system/index.js | 37 ++++-- 10 files changed, 199 insertions(+), 33 deletions(-) create mode 100644 docs/content/pt-BR/reference/azkfilejs/environment_variables.md mode change 100644 => 100755 spec/fixtures/test-app/bin/test-app diff --git a/docs/Azkfile.js b/docs/Azkfile.js index 58b16b00..4065f0b2 100644 --- a/docs/Azkfile.js +++ b/docs/Azkfile.js @@ -15,7 +15,7 @@ systems({ workdir: "/azk/#{manifest.dir}", shell: "/bin/bash", command: "gitbook serve --port $HTTP_PORT content --lrport $LIVERELOAD_PORT", - wait: {"retry": 20, "timeout": 3000}, + wait: 20, mounts: { '/azk/#{manifest.dir}' : sync(".", {shell: true}), '/azk/.github/CONTRIBUTING.md' : path("../.github/CONTRIBUTING.md"), diff --git a/docs/content/links.md b/docs/content/links.md index 7e84352f..06208dc7 100644 --- a/docs/content/links.md +++ b/docs/content/links.md @@ -19,3 +19,7 @@ [libnss-resolver]: https://github.com/azukiapp/libnss-resolver [digital_ocean]: http://digitalocean.com/ + + +[environment_variable]: https://en.wikipedia.org/wiki/Environment_variable +[dotenv_ref]: https://github.com/bkeepers/dotenv diff --git a/docs/content/pt-BR/SUMMARY.md b/docs/content/pt-BR/SUMMARY.md index 4b526260..82451ca6 100644 --- a/docs/content/pt-BR/SUMMARY.md +++ b/docs/content/pt-BR/SUMMARY.md @@ -23,9 +23,12 @@ /*****************************************************/ * [Começando uma aplicação do zero](starting-from-scratch/README.md) * [Node.js](starting-from-scratch/nodejs.md)* - * [PHP / Laravel](starting-from-scratch/php-laravel.md)* - * [PHP / Wordpress / phpMyAdmin](starting-from-scratch/php-wordpress-phpmyadmin.md)* - * [Python / Django](starting-from-scratch/python-django.md)* + * PHP + * [PHP / Laravel](starting-from-scratch/php-laravel.md)* + * [PHP / CakePHP](starting-from-scratch/php-cakephp.md)* + * [PHP / Wordpress / phpMyAdmin](starting-from-scratch/php-wordpress-phpmyadmin.md)* + * Python + * [Python / Django](starting-from-scratch/python-django.md)* * [Ruby on Rails](starting-from-scratch/ruby-rails.md)* /*****************************************************/ * [Adicionando serviços](adding-services/README.md) @@ -51,6 +54,7 @@ * [Azkfile.js](reference/azkfilejs/README.md) * [Nome de sistema](reference/azkfilejs/system_name.md) * [Propriedades expansíveis](reference/azkfilejs/expandable_properties.md) + * [Variáveis de ambiente](reference/azkfilejs/environment_variables.md) * [command](reference/azkfilejs/command.md) * [depends](reference/azkfilejs/depends.md) * [dns_servers](reference/azkfilejs/dns_servers.md) diff --git a/docs/content/pt-BR/adding-services/README.md b/docs/content/pt-BR/adding-services/README.md index 51982a4a..a7fdc77e 100644 --- a/docs/content/pt-BR/adding-services/README.md +++ b/docs/content/pt-BR/adding-services/README.md @@ -1,6 +1,6 @@ # Adicionando serviços -Antes de começar, certifique-se de ter o [azk instalado](../instalação/README.md). +Antes de começar, certifique-se de ter o [azk instalado](../installation/README.md). Este tutorial assume que você tenha seguido a seção anterior [Começando](../getting-started/README.md), e que já está um pouco familiarizado com o `azk` e o `Azkfile.js`. diff --git a/docs/content/pt-BR/reference/azkfilejs/environment_variables.md b/docs/content/pt-BR/reference/azkfilejs/environment_variables.md new file mode 100644 index 00000000..abc86305 --- /dev/null +++ b/docs/content/pt-BR/reference/azkfilejs/environment_variables.md @@ -0,0 +1,122 @@ + +# Variáveis de ambiente + +Em geral, quase toda aplicação requer alguma forma de configuração. O `azk` utiliza a abordagem de [variáveis de ambiente][environment_variable] para configuração dos sistemas. + +Variáveis de ambiente podem ser definidas em 4 lugares diferentes ou, em alguns casos especiais, podem ser definidas automaticamente. + +Abaixo descrevemos em detalhe quais são estes lugares, em ordem de processamento (se uma variável de ambiente estiver definida em mais de um desses lugares, prevalece aquela processada por último). + +## A partir das imagens + +Conforme descrito [aqui](image.md) um sistema sempre requer uma imagem. Seja ela vinda de um repositório externo ou a partir de um arquivo de receita, ela pode conter definições de variáveis. + +No exemplo abaixo temos um Dockerfile que define uma variável de ambiente: + +```Dockerfile +FROM azukiapp/alpine +ENV NAME=david +``` + +## No `Azkfile` + +No [Azkfile](../../azkfilejs/README.md), conforme descrito [aqui](envs.md), cada sistema declarado pode conter uma propriedade chamada [envs](envs.md), conforme o exemplo abaixo: + +```js +systems({ + web: { + image: { dockerfile: "./" }, + envs: { + NAME: "joe", + APP_ENV: "development", + } + } +}); +``` + +Nesse exemplo, estamos sobrescrevendo a variável `NAME` declarada no exemplo anterior e adicionando uma nova variável chamada `APP_ENV`. + +## No arquivo `.env` + +Conforme a referência da propriedade [envs](envs.md) para o `Azkfile`, não é aconselhado a adição de informações sensíveis (senhas, chaves de api, etc.) diretamente na propriedade `envs` do `Azkfile`. + +Como alternativa deve-se utilizar um arquivo `.env` no mesmo diretório do `Azkfile`, conforme descrito nesta [biblioteca][dotenv_ref]: + +```sh +echo "API_KEY=FCB12" >> .env +echo "NAME=mike" >> .env +``` + +Nesse exemplo, estamos sobrescrevendo a variável `NAME` declarada no `Azkfile` do exemplo anterior. + +## Auto inserida + +Atualmente temos dois casos onde variáveis de ambiente são adicionadas automaticamente no sistema: + +### Portas + +Uma série de variáveis de ambiente são criadas a partir das definições de portas do sistema, que podem estar no [próprio Azkfile](ports.md) ou na imagem utilizada. + +O padrão de nomenclatura para essas variáveis de ambiente é `[PORT_NAME|PORT_NUMBER]_PORT`, onde: + +- `PORT_NAME` é o nome da porta definido no `Azkfile`; +- `PORT_NUMBER` é o número da porta (utilizado no caso das portas definidas na imagem mas não nomeadas no `Azkfile`); + +### Porta http + +Uma variável chamada `HTTP_PORT` é inserida quando a propriedade [http](http.md) é definida em um sistema. + +O valor padrão dessa variável é `5000`, mas é possível mudar isso adicionando uma porta nomeada como `http` na propriedade `ports` do `Azkfile`. + +### Vindas de outros sistemas + +Quando você declara que um sistema **A** depende do outro **B**, utilizando a propriedade [depends](depends.md) no sistema **A**, as seguintes variáveis de ambiente são inseridas no sistema **A**: + +- **port e host**: quatro variáveis para cada porta exposta do sistema **B**, no padrão: + - `[B_SYSTEM_NAME]_[PORT_NAME]_PORT` + - `[B_SYSTEM_NAME]_[PORT_NUMBER]_PORT` + - `[B_SYSTEM_NAME]_[PORT_NAME]_HOST` + - `[B_SYSTEM_NAME]_[PORT_NUMBER]_HOST` + +- **export_envs**: as variáveis de ambiente definidas na propriedade [export_envs][export_envs] do sistema **B** são inseridas no sistema **A**. + +## No shell + +Ao executar o comando [azk shell](../cli/shell.md), é possível informar uma nova variável de ambiente ou sobrescrever qualquer uma das variáveis definidas acima. + +``` +azk shell web -e NAME=gullit -e FOO=bar +``` + +----------------------------- + +## Auto expansão de variáveis + +No processo de parse e execução de um sistema, as variáveis de ambiente utilizadas na declaração das propriedades do sistema são expandidas para os seus valores (conforme a ordem de processamento descrita anteriormente): + +```js +systems({ + web: { + image: { dockerfile: "./" }, + command: ["start.sh", "--name", "$NAME", "--port", "${HTTP_PORT}"], + http: { + domain: ["#{system.name}.#{azk.default_domain}"], + }, + ports: { + http: "3000/tcp", + }, + envs: { + NAME: "joe", + APP_ENV: "development", + } + } +}); +``` + +Nesse caso, o `command` resultante será: + +```sh +start.sh --name joe --port 3000 +``` + +!INCLUDE "../../../links.md" diff --git a/spec/fixtures/test-app/bin/test-app b/spec/fixtures/test-app/bin/test-app old mode 100644 new mode 100755 index e69de29b..86fa5e54 --- a/spec/fixtures/test-app/bin/test-app +++ b/spec/fixtures/test-app/bin/test-app @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "$@" diff --git a/spec/spec_helpers/mock_manifest.js b/spec/spec_helpers/mock_manifest.js index e2354b65..5976686e 100644 --- a/spec/spec_helpers/mock_manifest.js +++ b/spec/spec_helpers/mock_manifest.js @@ -182,7 +182,6 @@ export function extend(h) { image: { docker: default_img }, provision: [ "system.name: #{system.name}", - "system.persistent_folders: #{system.persistent_folders}", "manifest.dir: #{manifest.dir}", "manifest.path: #{manifest.path}", "manifest.project_name: #{manifest.project_name}", @@ -191,8 +190,10 @@ export function extend(h) { "azk.default_dns: #{azk.default_dns}", "azk.balancer_port: #{azk.balancer_port}", "azk.balancer_ip: #{azk.balancer_ip}", - "env.FOO: #{env.FOO}", - "env.BAR: #{env.BAR}", + "env.FOO (host): #{env.FOO}", + "env.BAR (host): #{env.BAR}", + "PORT: ${PORT}", + "HOST_DOMAIN: ${HOST_DOMAIN}", ], }, 'example-sync': { diff --git a/spec/system/index_spec.js b/spec/system/index_spec.js index 04223874..7f8d8aea 100644 --- a/spec/system/index_spec.js +++ b/spec/system/index_spec.js @@ -72,18 +72,20 @@ describe("Azk system class, main set", function() { var manifest = mf; var system = manifest.system('expand-test'); - var provision = system.options.provision; - h.expect(provision).to.include(`system.name: ${system.name}`); - h.expect(provision).to.include(`manifest.dir: ${manifest.manifestDirName}`); - h.expect(provision).to.include(`manifest.path: ${manifest.manifestPath}`); - h.expect(provision).to.include(`manifest.project_name: ${manifest.manifestDirName}`); - h.expect(provision).to.include(`azk.version: ${version}`); - h.expect(provision).to.include(`azk.default_domain: ${config('agent:balancer:host')}`); - h.expect(provision).to.include(`azk.default_dns: ${net.nameServers().toString()}`); - h.expect(provision).to.include(`azk.balancer_port: ${config('agent:balancer:port').toString()}`); - h.expect(provision).to.include(`azk.balancer_ip: ${config('agent:balancer:ip')}`); - h.expect(provision).to.include(`env.FOO: ${process.env.FOO}`); - h.expect(provision).to.include(`env.BAR: `); + h.expect(system.options.provision).to.containSubset([ + `system.name: ${system.name}`, + `manifest.dir: ${manifest.manifestDirName}`, + `manifest.path: ${manifest.manifestPath}`, + `manifest.project_name: ${manifest.manifestDirName}`, + `azk.version: ${version}`, + `azk.default_domain: ${config('agent:balancer:host')}`, + `azk.default_dns: ${net.nameServers().toString()}`, + `azk.balancer_port: ${config('agent:balancer:port').toString()}`, + `azk.balancer_ip: ${config('agent:balancer:ip')}`, + `env.FOO (host): ${process.env.FOO}`, + `env.BAR (host): `, + `PORT: \${PORT}`, + ]); }).finally(() => { process.env.FOO = undefined; }); @@ -241,7 +243,7 @@ describe("Azk system class, main set", function() { }); it("should prepend shell before command if image doesn't have entrypoint nor cmd", function() { - var cmd = ["/bin/sh", "-c", system.command]; + var cmd = ["/bin/sh", "-c", system.command.replace(/\${HTTP_PORT}/, "5000")]; h.expect(options).to.have.property("command").and.eql(cmd); }); @@ -249,7 +251,7 @@ describe("Azk system class, main set", function() { var options = system.daemonOptions({}, { Entrypoint: ["/entry.sh"] }); - var cmd = utils.splitCmd(system.command); + var cmd = utils.splitCmd(system.command.replace(/\${HTTP_PORT}/, "5000")); h.expect(options).to.have.property("command").and.eql(cmd); }); @@ -257,7 +259,7 @@ describe("Azk system class, main set", function() { var options = system.daemonOptions({}, { Cmd: ["bash"] }); - var cmd = ["/bin/sh", "-c", system.command]; + var cmd = ["/bin/sh", "-c", system.command.replace(/\${HTTP_PORT}/, "5000")]; h.expect(options).to.have.property("command").and.eql(cmd); }); @@ -283,7 +285,7 @@ describe("Azk system class, main set", function() { it("should return only system.command if image entrypoint and cmd and system.command aren't empty", function() { var options = system.daemonOptions({}, { Entrypoint: ["/entry.sh"], Cmd: ["ls -l"]}); - var cmd = utils.splitCmd(system.command); + var cmd = utils.splitCmd(system.command.replace(/\${HTTP_PORT}/, "5000")); h.expect(options).to.have.property("command").and.eql(cmd); }); diff --git a/spec/system/run_spec.js b/spec/system/run_spec.js index cf62b0de..32267d86 100644 --- a/spec/system/run_spec.js +++ b/spec/system/run_spec.js @@ -155,6 +155,17 @@ describe("Azk system class, run set", function() { it("load from .env file", function() { h.expect(envs).to.include.something.that.match(/FROM_DOT_ENV=azk is beautiful/); }); + + it("should expand envs in properties", function*() { + var system = manifest.system('example'); + var exitResult = yield system.runShell({ + shell: "./bin/test-app", + command: ["echo", "${ECHO_DATA}"], + stdout: mocks.stdout, stderr: mocks.stderr + }); + h.expect(exitResult).to.have.property("code", 0); + h.expect(outputs).to.have.property("stdout").match(/-c echo data/); + }); }); describe("run mutiple same system and type", function() { diff --git a/src/system/index.js b/src/system/index.js index de278e35..86a47860 100644 --- a/src/system/index.js +++ b/src/system/index.js @@ -14,6 +14,9 @@ var regex_port = new XRegExp( "(?[0-9]{1,})(:(?[0-9]{1,})){0,1}(/(?tcp|udp)){0,1}", "x" ); +// https://regex101.com/r/rK1eJ0/2 +var regex_envs = /(?:\${[=|-]?([A-Z|\d|_]+)})|(?:\$[=|-]?([A-Z|\d|_]+))/g; + export class System { constructor(manifest, name, image, options = {}) { this.manifest = manifest; @@ -29,9 +32,12 @@ export class System { // Options this.__options = {}; - this.options = _.merge({}, this.default_options, options); - this.options = this._expand_template(this.options); + + var raw = this.options.raw; + delete this.options.raw; + this.options = this._expand_template(this.options); + this.options.raw = raw; } get image_name_suggest() { @@ -351,6 +357,7 @@ export class System { interactive: false, }); + options.command = this._shell_command(options); var opts = this._make_options(false, options, image_conf); // Shell extra options @@ -360,7 +367,6 @@ export class System { ); _.assign(opts, { - command: this._shell_command(options), tty : options.interactive ? options.stdout.isTTY : false, stdout : options.stdout, stderr : options.stderr || options.stdout, @@ -425,10 +431,8 @@ export class System { verbose: options.verbose, ports: ports, ports_orderly: ports_orderly, - stdout: options.stdout, volumes: mounts, working_dir: options.workdir || this.workdir, - env: envs, dns: dns_servers, extra: options.docker || this.options.docker_extra || {}, annotations: { azk: { @@ -439,6 +443,15 @@ export class System { }} }; + // Expand envs + var template = JSON.stringify(finalOptions); + template = template.replace(regex_envs, "$${envs.$1$2}"); + finalOptions = JSON.parse(utils.template(template, { envs })); + + // Not expand and not stringify + finalOptions.env = envs; + finalOptions.stdout = options.stdout; + return finalOptions; } @@ -535,8 +548,8 @@ export class System { _expand_template(options) { var data = { - _keep_key(key) { - return "#{" + key + "}"; + _keep_key(key, token = "#") { + return `${token}{${key}}`; }, system: { name: this.name, @@ -562,8 +575,14 @@ export class System { } _replace_keep_keys(template) { - var regex = /(?:(?:[#|$]{|<%)[=|-]?)\s*((?:envs|net)\.[\S]+?)\s*(?:}|%>)/g; - return template.replace(regex, "#{_keep_key('$1')}"); + // https://regex101.com/r/gF4uT4/1 + let regexes = { + net_envs: /(?:(?:[#|$]{|<%)[=|-]?)\s*((?:envs|net)\.[\S]+?)\s*(?:}|%>)/g, + envs : regex_envs, + }; + return template + .replace(regexes.net_envs, "#{_keep_key('$1')}") + .replace(regexes.envs , "#{_keep_key('$1$2', '$$')}"); } _resolved_path(mount_path) { From 1097a1a55c6a451fce4d73baeff20c702bfcfddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Mon, 4 Apr 2016 09:35:02 -0300 Subject: [PATCH 49/61] [Manifest] Updating documentation about envs expansion. --- .../azkfilejs/expandable_properties.md | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md b/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md index 9c7bc47f..2c4d7aad 100644 --- a/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md +++ b/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md @@ -2,6 +2,8 @@ Podemos utilizar algumas propriedades dentro de _strings_ no `Azkfile.js`. Essas propriedades especiais são substituídas para seu respectivos valores em tempo de execução. +O formato para essas propriedades é `#{grup.name}` para propriedades em geral e `${VAR_NAME}` para variáveis de ambiente. + ## Índice: 1. [Propriedades Expansíveis Gerais](#propriedades-expansíveis-gerais) @@ -19,6 +21,7 @@ Podemos utilizar algumas propriedades dentro de _strings_ no `Azkfile.js`. Essas 1. [Propriedades Expansíveis do Load Balancer](#propriedades-expansíveis-do-load-balancer) 1. [#{azk.balancer_ip}](#azkbalancer_ip) 1. [#{azk.balancer_port}](#azkbalancer_port) +1. [Variáveis de ambiente](#variáveis-de-ambiente) ## Propriedades Expansíveis Gerais: @@ -179,7 +182,6 @@ Objeto com as variáveis de ambiente disponíveis na máquina local. Use com not __Alerta de Segurança:__ Observe que, como o `Azkfile.js` é parte do código, dados confidenciais (como senhas e tokens privados) não devem ser colocados aqui. Ao invés disso, use um arquivo `.env` e não adicione-o ao seu sistema de controle de versão. - _Exemplo_: ```js @@ -295,3 +297,25 @@ $ azk shell -c 'env' BALANCER_IP=172.17.0.1 BALANCER_PORT=80 ``` + +## Variáveis de ambiente + +Além das propriedades expansíveis que permitem ter acesso as configurações do `azk` é possível usar em propriedades como [command](./command.md) e [provision](./provision.md) variáveis de ambiente. + +Mas diferentes das outras propriedades expansíveis o formato para variáveis de ambiente é `${VAR_NAME}` ou ainda `$VAR_NAME`. + +**Obs**: Não confunda essa opção com as propriedades `#{env}` e `#{envs}` descritas mais acima e que tem outro tipo de uso. + +Exemplo de uso de variável de ambiente: + +```js +systems({ + web: { + image: { docker: "azukiapp/ruby" }, + command: ["bundle", "exec", "rails", "-p", "$HTTP_PORT"], + envs: { + HTTP_PORT: "8080", + }, + }, +}); +``` From 5ccf136a2acbcbd27ffa84519a3769d5232659e9 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 4 Apr 2016 15:37:36 -0300 Subject: [PATCH 50/61] [Docs] Adding EN translation for environment variables section --- docs/content/en/SUMMARY.md | 1 + docs/content/en/reference/azkfilejs/README.md | 1 + .../azkfilejs/environment_variables.md | 121 ++++++++++++++++++ .../azkfilejs/expandable_properties.md | 26 ++++ .../pt-BR/reference/azkfilejs/README.md | 1 + .../azkfilejs/environment_variables.md | 9 +- .../azkfilejs/expandable_properties.md | 6 +- 7 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 docs/content/en/reference/azkfilejs/environment_variables.md diff --git a/docs/content/en/SUMMARY.md b/docs/content/en/SUMMARY.md index 67f6c630..e3d93b53 100644 --- a/docs/content/en/SUMMARY.md +++ b/docs/content/en/SUMMARY.md @@ -54,6 +54,7 @@ * [Azkfile.js](reference/azkfilejs/README.md) * [System name](reference/azkfilejs/system_name.md) * [Expandable properties](reference/azkfilejs/expandable_properties.md) + * [Environment variables](reference/azkfilejs/environment_variables.md) * [command](reference/azkfilejs/command.md) * [depends](reference/azkfilejs/depends.md) * [dns_servers](reference/azkfilejs/dns_servers.md) diff --git a/docs/content/en/reference/azkfilejs/README.md b/docs/content/en/reference/azkfilejs/README.md index 55d338b1..83778bc0 100644 --- a/docs/content/en/reference/azkfilejs/README.md +++ b/docs/content/en/reference/azkfilejs/README.md @@ -5,6 +5,7 @@ The **Azkfile.js** is the backbone of the functioning of `azk`. Its main functio As suggested by the `.js` extension the **Azkfile.js** is written in JavaScript, but no advanced knowledge of JavaScript is needed to edit it. Listed below are all supported parameters: - [Expandable properties](expandable_properties.md) +- [Environment variables](environment_variables.md) - [command](command.md) - [depends](depends.md) - [dns_servers](dns_servers.md) diff --git a/docs/content/en/reference/azkfilejs/environment_variables.md b/docs/content/en/reference/azkfilejs/environment_variables.md new file mode 100644 index 00000000..1ffd1032 --- /dev/null +++ b/docs/content/en/reference/azkfilejs/environment_variables.md @@ -0,0 +1,121 @@ +# Environment variables + +Generally, almost every application demands some sort of configuration. `azk` uses the [environment variables][environment_variable] (a.k.a. env vars) approach to configure systems. + +Environment variables can be defined on 4 different places or, in some special cases, they can be automatically defined. + +Below, those 4 places are described in order of processing. If a same environment variable is defined in more than one of those places, the latter definition prevails. + +## From the images + +As described [here](image.md), a system always requires an image. Either from an external repository or from a recipe file, an image can contain environment variables defined on it. + +In the example below we have a Dockerfile that defines the environment variable `NAME`: + +```Dockerfile +FROM azukiapp/alpine +ENV NAME=david +``` + +## In the `Azkfile` + +In the [Azkfile](../../azkfilejs/README.md), each declared system can contain a property called [envs](envs.md), as in the example below: + +```js +systems({ + web: { + image: { dockerfile: "./" }, + envs: { + NAME: "joe", + APP_ENV: "development", + } + } +}); +``` + +In this example, we're overriding the environment variable `NAME` declared in the previous example and adding a new one called `APP_ENV`. + +## In the file `.env` + +As per the reference of the property [envs](envs.md) of the `Azkfile`, it isn't recommended to put sensitive data (such as passwords, API keys, etc.) directly in the property `envs`. + +As alternative, a `.env` file should be used in the folder that contains the `Azkfile`, as described in this [lib][dotenv_ref]; + +```sh +echo "API_KEY=FCB12" >> .env +echo "NAME=mike" >> .env +``` + +In this example, we're overriding the property `NAME` declared in the `Azkfile` of the previous example. + +## Autoinserted + +Currently, we have two cases in which environment variables are automatically inserted in the systems: + +### Ports + +Several environment variables are created from the system ports definition, which can be in the [Azkfile](ports.md) and/or in the used image. + +The naming pattern for those variables is `[PORT_NAME|PORT_NUMBER]_PORT`, where: + +- `PORT_NAME` is the name of the port defined in the `Azkfile`; +- `PORT_NUMBER` is the number of the port (used in the case where the port is defined in the image but isn't named in the `Azkfile`); + +### HTTP port + +The environment variable `HTTP_PORT` is inserted when the property [http](http.md) is defined in a system. + +The default value of this variable is `5000`, but it's possible to change it by adding a port named `http` in the property `ports` of the `Azkfile`. + +### From other systems + +When you declare a system **A** depending on another system **B** (using the property [depends](depends.md)), the following environment variables are inserted in the system **A**: + +- **port and host**: four variables for each port exposed by system **B**, in the following naming pattern: + - `[B_SYSTEM_NAME]_[PORT_NAME]_PORT` + - `[B_SYSTEM_NAME]_[PORT_NUMBER]_PORT` + - `[B_SYSTEM_NAME]_[PORT_NAME]_HOST` + - `[B_SYSTEM_NAME]_[PORT_NUMBER]_HOST` + +- **export_envs**: all the environment variables defined in the property [export_envs](export_envs.md) of the system **B** are inserted in the system **A**; + +## On the shell + +Running the command [azk shell](../cli/shell.md), you can pass new environment variables and/or override any of the defined above. + +``` +azk shell web -e NAME=gullit -e FOO=bar +``` + +----------------------------- + +## Auto expansion of variables + +During the process of parsing and running of a system, the environment variables used in the declaration of the properties of a system are expanded to its values (according to the processing order previously described): + +```js +systems({ + web: { + image: { dockerfile: "./" }, + command: ["start.sh", "--name", "$NAME", "--port", "${HTTP_PORT}"], + http: { + domain: ["#{system.name}.#{azk.default_domain}"], + }, + ports: { + http: "3000/tcp", + }, + envs: { + NAME: "joe", + APP_ENV: "development", + } + } +}); +``` + +In this case, the resulting `command` will be: + +```sh +start.sh --name joe --port 3000 +``` + +!INCLUDE "../../../links.md" diff --git a/docs/content/en/reference/azkfilejs/expandable_properties.md b/docs/content/en/reference/azkfilejs/expandable_properties.md index d708dfaa..0e800a5c 100644 --- a/docs/content/en/reference/azkfilejs/expandable_properties.md +++ b/docs/content/en/reference/azkfilejs/expandable_properties.md @@ -2,6 +2,8 @@ We can use some properties inside _strings_ in `Azkfile.js`. These special properties are replaced with runtime values. +The pattern used for these properties is `#{group.name}` for general properties and `${VAR_NAME}` for environment variables. + ## Table of contents: 1. [General Expandable Properties](#general-expandable-properties) @@ -19,6 +21,7 @@ We can use some properties inside _strings_ in `Azkfile.js`. These special prope 1. [Load Balancer Expandable Properties](#load-balancer-expandable-properties) 1. [#{azk.balancer_ip}](#azkbalancer_ip) 1. [#{azk.balancer_port}](#azkbalancer_port) +1. [Environment variables](#environment-variables) ## General Expandable Properties: @@ -295,3 +298,26 @@ $ azk shell -c 'env' BALANCER_IP=172.17.0.1 BALANCER_PORT=80 ``` + + +## Environment variables + +Besides the expandable properties, which allow us to access `azk` configuration values, it's possible to use environment variables in properties such as [command](./command.md) and [provision](./provision.md). + +Unlike expandable properties, the pattern used for environment variables is `${VAR_NAME}` or `$VAR_NAME`. + +**Obs**: Don't mistake this option with the properties `#{env}` and `#{envs}` described above, which have a different kind of use. + +Examples of usage of environment variables: + +```js +systems({ + web: { + image: { docker: "azukiapp/ruby" }, + command: ["bundle", "exec", "rails", "-p", "$HTTP_PORT"], + envs: { + HTTP_PORT: "8080", + }, + }, +}); +``` diff --git a/docs/content/pt-BR/reference/azkfilejs/README.md b/docs/content/pt-BR/reference/azkfilejs/README.md index 417ec35b..46804119 100644 --- a/docs/content/pt-BR/reference/azkfilejs/README.md +++ b/docs/content/pt-BR/reference/azkfilejs/README.md @@ -5,6 +5,7 @@ O **Azkfile.js** é a espinha dorsal do funcionamento do `azk`. Sua principal fu Como sugere a extensão `.js` o **Azkfile.js** é escrito em JavaScript, mas nenhum conhecimento avançado de JavaScript é necessário para edita-lo. Abaixo estão os parâmetros suportados no `azk`: - [Propriedades expansíveis](expandable_properties.md) +- [Variáveis de ambiente](environment_variables.md) - [command](command.md) - [depends](depends.md) - [dns_servers](dns_servers.md) diff --git a/docs/content/pt-BR/reference/azkfilejs/environment_variables.md b/docs/content/pt-BR/reference/azkfilejs/environment_variables.md index abc86305..dd4acad4 100644 --- a/docs/content/pt-BR/reference/azkfilejs/environment_variables.md +++ b/docs/content/pt-BR/reference/azkfilejs/environment_variables.md @@ -1,4 +1,3 @@ - # Variáveis de ambiente Em geral, quase toda aplicação requer alguma forma de configuração. O `azk` utiliza a abordagem de [variáveis de ambiente][environment_variable] para configuração dos sistemas. @@ -20,7 +19,7 @@ ENV NAME=david ## No `Azkfile` -No [Azkfile](../../azkfilejs/README.md), conforme descrito [aqui](envs.md), cada sistema declarado pode conter uma propriedade chamada [envs](envs.md), conforme o exemplo abaixo: +No [Azkfile](../../azkfilejs/README.md), cada sistema declarado pode conter uma propriedade chamada [envs](envs.md), conforme o exemplo abaixo: ```js systems({ @@ -38,7 +37,7 @@ Nesse exemplo, estamos sobrescrevendo a variável `NAME` declarada no exemplo an ## No arquivo `.env` -Conforme a referência da propriedade [envs](envs.md) para o `Azkfile`, não é aconselhado a adição de informações sensíveis (senhas, chaves de api, etc.) diretamente na propriedade `envs` do `Azkfile`. +Conforme a referência da propriedade [envs](envs.md) para o `Azkfile`, não é aconselhado a adição de informações sensíveis (senhas, chaves de API, etc.) diretamente na propriedade `envs` do `Azkfile`. Como alternativa deve-se utilizar um arquivo `.env` no mesmo diretório do `Azkfile`, conforme descrito nesta [biblioteca][dotenv_ref]: @@ -51,7 +50,7 @@ Nesse exemplo, estamos sobrescrevendo a variável `NAME` declarada no `Azkfile` ## Auto inserida -Atualmente temos dois casos onde variáveis de ambiente são adicionadas automaticamente no sistema: +Atualmente, temos dois casos onde variáveis de ambiente são adicionadas automaticamente no sistema: ### Portas @@ -78,7 +77,7 @@ Quando você declara que um sistema **A** depende do outro **B**, utilizando a p - `[B_SYSTEM_NAME]_[PORT_NAME]_HOST` - `[B_SYSTEM_NAME]_[PORT_NUMBER]_HOST` -- **export_envs**: as variáveis de ambiente definidas na propriedade [export_envs][export_envs] do sistema **B** são inseridas no sistema **A**. +- **export_envs**: as variáveis de ambiente definidas na propriedade [export_envs](export_envs.md) do sistema **B** são inseridas no sistema **A**. ## No shell diff --git a/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md b/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md index 2c4d7aad..2734c04a 100644 --- a/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md +++ b/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md @@ -300,13 +300,13 @@ BALANCER_PORT=80 ## Variáveis de ambiente -Além das propriedades expansíveis que permitem ter acesso as configurações do `azk` é possível usar em propriedades como [command](./command.md) e [provision](./provision.md) variáveis de ambiente. +Além das propriedades expansíveis, que permitem ter acesso as configurações do `azk`, é possível usar em propriedades como [command](./command.md) e [provision](./provision.md) variáveis de ambiente. -Mas diferentes das outras propriedades expansíveis o formato para variáveis de ambiente é `${VAR_NAME}` ou ainda `$VAR_NAME`. +Mas, diferente das outras propriedades expansíveis, o formato para variáveis de ambiente é `${VAR_NAME}` ou ainda `$VAR_NAME`. **Obs**: Não confunda essa opção com as propriedades `#{env}` e `#{envs}` descritas mais acima e que tem outro tipo de uso. -Exemplo de uso de variável de ambiente: +Exemplo de uso de variáveis de ambiente: ```js systems({ From 38fb462048f5aaba810ae403b1fe94edf7632096 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 4 Apr 2016 16:11:02 -0300 Subject: [PATCH 51/61] [Docs] Fixing typo --- docs/content/pt-BR/reference/azkfilejs/expandable_properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md b/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md index 2734c04a..62967052 100644 --- a/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md +++ b/docs/content/pt-BR/reference/azkfilejs/expandable_properties.md @@ -2,7 +2,7 @@ Podemos utilizar algumas propriedades dentro de _strings_ no `Azkfile.js`. Essas propriedades especiais são substituídas para seu respectivos valores em tempo de execução. -O formato para essas propriedades é `#{grup.name}` para propriedades em geral e `${VAR_NAME}` para variáveis de ambiente. +O formato para essas propriedades é `#{group.name}` para propriedades em geral e `${VAR_NAME}` para variáveis de ambiente. ## Índice: From fd573541223fe2421b9132c574932d5031deda19 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 4 Apr 2016 16:57:13 -0300 Subject: [PATCH 52/61] Fixing changelog -- `azk version` output change is in azk v0.18.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51652017..b4f605b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * [Docs] The instructions for use `azk` and `Wordpress` have been improved; * [Manifest] Adding `resolve` options to `path`, with default `true` value; * [Docs] Updating whole docs structure: Azkfile, Gitbook version and plugins; + * [Cli] `azk version` changes to display the first 7 digits of the commit hash and the date of the corresponding commit version; * Bug * [Sync] Fixing ownership of the symlink itself, not of the target file; @@ -17,7 +18,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## v0.17.0 - (2016-02-19) * Enhancements - * [Cli] `azk version` changes to display the first 7 digits of the commit hash and the date of the corresponding commite version; * [Cli] Updating `i18n-cli`: now, it supports color syntax highlight; * [Cli] Replacing `colors` for `chalk` and adding `--no-color` option that outputs in only one color; * [Cli] Improving `ui.isInteractive()`: now, not only the existence of a `tty` is checked but also the parameter `--quiet`; From 1f93c01d414d1227c168fe19b67b1401e99ca94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Mon, 4 Apr 2016 11:09:42 -0300 Subject: [PATCH 53/61] [Cli] Deprecating `azk doctor` and improving `azk version` instead. --- .github/ISSUE_TEMPLATE.md | 2 + CHANGELOG.md | 4 + docs/content/en/reference/cli/doctor.md | 16 +--- docs/content/en/reference/cli/version.md | 16 +++- docs/content/pt-BR/reference/cli/doctor.md | 16 +--- docs/content/pt-BR/reference/cli/version.md | 16 +++- shared/locales/en-US.js | 3 + shared/locales/usage-en-US.txt | 4 +- spec/index_spec.js | 6 ++ src/agent/vm.js | 4 + src/cli/ui.js | 4 + src/cli/views/version_view.js | 58 ++++++++++++++ src/cmds/doctor.js | 84 +++------------------ src/cmds/version.js | 65 +++++++++++++--- src/index.js | 18 +++++ src/utils/index.js | 11 +++ src/utils/tracker.js | 11 +-- 17 files changed, 207 insertions(+), 131 deletions(-) create mode 100644 src/cli/views/version_view.js diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 29dd912c..0a4e1990 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -30,6 +30,8 @@ a. Which OS / distro? b. Uses VirtualBox? Which version? c. Can you provide us your Azkfile.js? +**ProTip**: run `azk version --full` to gather those info. + 5. Do you have any suggestions on how to tackle this? [Optional] diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f605b5..897ac870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). * [Manifest] Adding `resolve` options to `path`, with default `true` value; * [Docs] Updating whole docs structure: Azkfile, Gitbook version and plugins; * [Cli] `azk version` changes to display the first 7 digits of the commit hash and the date of the corresponding commit version; + * [Cli] Adding options `--full` and `--logo` the command `azk version`, to show more information (old `azk doctor`); + +* Deprecations + * [Cli] Deprecating `azk doctor` command, now use `azk version --full`; * Bug * [Sync] Fixing ownership of the symlink itself, not of the target file; diff --git a/docs/content/en/reference/cli/doctor.md b/docs/content/en/reference/cli/doctor.md index 38e829a6..2663ca44 100644 --- a/docs/content/en/reference/cli/doctor.md +++ b/docs/content/en/reference/cli/doctor.md @@ -1,18 +1,4 @@ ## azk doctor - Shows an analysis of azk's health. +`azk doctor` is deprecated, use [azk version](./version.md) instead. -#### Usage: - - $ azk doctor [--logo -q -h -l=] [-v]... - - #### Options: - -``` - --logo Shows the azk logo before showing health information. - --no-color Remove colors from output - --quiet, -q Never prompt. - --help, -h Shows help usage. - --log=, -l Sets log level (default: error). - --verbose, -v Sets the level of detail - multiple supported (-vv == --verbose 2) [default: 0]. -``` diff --git a/docs/content/en/reference/cli/version.md b/docs/content/en/reference/cli/version.md index 75322e9c..632bb66a 100644 --- a/docs/content/en/reference/cli/version.md +++ b/docs/content/en/reference/cli/version.md @@ -1,15 +1,27 @@ ## azk version - Displays the current version of `azk` +Displays the current version of `azk` #### Usage: $ azk version $ azk --version + $ azk version --full + $ azk version --full --logo #### Example: -``` +```sh $ azk version azk 0.13.1 ``` + +```sh +# azk version --full +Version : azk version 0.17.0, build a95c79d, date 2016-03-31 +OS : OS X Yosemite - [x64], memory: 8192MB +Agent : up +Docker : 1.9.1 +Use vm : yes, ip: 192.168.50.4 +VirtualBox: 5.0.4r102546 +``` diff --git a/docs/content/pt-BR/reference/cli/doctor.md b/docs/content/pt-BR/reference/cli/doctor.md index 5119441d..6824be20 100644 --- a/docs/content/pt-BR/reference/cli/doctor.md +++ b/docs/content/pt-BR/reference/cli/doctor.md @@ -1,18 +1,4 @@ ## azk doctor - Exibi uma análise de saúde do azk. +`azk doctor` está deprecado, utilize [azk version](./version.md) como alternativa. -#### Uso: - - $ azk doctor [--logo -q -h -l=] [-v]... - - #### Opções: - -``` - --logo Mostra a logo do azk junto com as informações. - --no-color Remove cores na saída padrão - --quiet, -q Nunca perguntar. - --help, -h Mostrar ajuda de uso. - --log=, -l Defini o nível de log (padrão: error). - --verbose, -v Defini o nível de detalhes da saída - suporta múltiplos (-vv == --verbose 2) [padrão: 0]. -``` diff --git a/docs/content/pt-BR/reference/cli/version.md b/docs/content/pt-BR/reference/cli/version.md index da389df3..ebb8a9d3 100644 --- a/docs/content/pt-BR/reference/cli/version.md +++ b/docs/content/pt-BR/reference/cli/version.md @@ -1,15 +1,27 @@ ## azk version - Exibe a atual versão do `azk`. +Exibe a atual versão do `azk`. #### Uso: $ azk version $ azk --version + $ azk version --full + $ azk version --full --logo #### Exemplos: -``` +```sh $ azk version azk 0.13.1 ``` + +```sh +# azk version --full +Version : azk version 0.17.0, build a95c79d, date 2016-03-31 +OS : OS X Yosemite - [x64], memory: 8192MB +Agent : up +Docker : 1.9.1 +Use vm : yes, ip: 192.168.50.4 +VirtualBox: 5.0.4r102546 +``` diff --git a/shared/locales/en-US.js b/shared/locales/en-US.js index ef6d6576..4a471959 100644 --- a/shared/locales/en-US.js +++ b/shared/locales/en-US.js @@ -531,6 +531,9 @@ module.exports = { system_not_balanceable : "The system " + "${red}%(name)s${red.close}" + " does not have ports http to open.", default_system_not_balanceable: "The default system " + "${red}%(name)s${red.close}" + " does not have ports http to open.", }, + doctor: { + deprecated: "Command `azk doctor` is deprecated, use `azk version --full` instead.", + }, }, docker: { diff --git a/shared/locales/usage-en-US.txt b/shared/locales/usage-en-US.txt index f15f1227..0ff5d128 100644 --- a/shared/locales/usage-en-US.txt +++ b/shared/locales/usage-en-US.txt @@ -22,8 +22,9 @@ Usage: azk status [] [--long --short --text] [-qh --no-color -l ] [-v]... azk stop [] [--no-remove] [-qh --no-color -l ] [-v]... azk vm (ssh|start|status|installed|stop|remove) [--force] [-qh --no-color -l ] [-v]... [-- ...] - azk [agent|config|vm|version] [--help] [-qh --no-color -l ] [-v]... + azk [agent|config|vm] [--help] [-qh --no-color -l ] [-v]... azk help [] [-q --no-color -l ] [-v]... + azk version [--full --logo] [-qh --no-color -l ] [-v]... azk [--version] [-qh --no-color -l ] [-v]... Commands: @@ -89,6 +90,7 @@ Options: --image=, -i Defines the image in which the command will be executed. --log=, -l Sets log level (default: error). --logo Shows the azk logo before showing health information. + --full Shows full version information. --long Show all columns. --mount=, -m Additional mounting points - multiple supported (`-m ~/Home:/azk/user -m ~/data:/var/data`). --lines=, -n Outputs the specified number of lines at the end of logs [default: all]. diff --git a/spec/index_spec.js b/spec/index_spec.js index 33308af2..0218a18f 100644 --- a/spec/index_spec.js +++ b/spec/index_spec.js @@ -7,6 +7,12 @@ describe('azk main module', function() { h.expect(Azk.version).to.match(/\d+\.\d+\.\d+/); }); + it('should `fullVersion` get current version, build id and build date', function*() { + var version = yield Azk.fullVersion(); + var version_regex = /azk version \d+\.\d+\.\d+, build \w+, date \d+-\d+-\d+/; + h.expect(version).to.match(version_regex); + }); + it('should commitId() get current commit id from ENV', function() { const last_commit_id = '123'; return h.expect(Azk.commitId(last_commit_id)) diff --git a/src/agent/vm.js b/src/agent/vm.js index 5916ce0d..fc1e0d1d 100644 --- a/src/agent/vm.js +++ b/src/agent/vm.js @@ -248,6 +248,10 @@ function acpipowerbutton(name) { } var vm = { + version() { + return exec("--version"); + }, + info(vm_name) { return machine.info(vm_name).then((info) => { if (info['Forwarding(0)']) { diff --git a/src/cli/ui.js b/src/cli/ui.js index 4d412eab..2607452d 100644 --- a/src/cli/ui.js +++ b/src/cli/ui.js @@ -223,6 +223,10 @@ export class UIProxy { get userInterface() { return this.parent ? this.parent.userInterface : this.__user_interface; } + + get c() { + return this.userInterface.c; + } } _.each(_.methods(UI), (method) => { diff --git a/src/cli/views/version_view.js b/src/cli/views/version_view.js new file mode 100644 index 00000000..6da0e89f --- /dev/null +++ b/src/cli/views/version_view.js @@ -0,0 +1,58 @@ +import { _ } from 'azk'; +import { View } from './view'; + +export class VersionView extends View { + render(data, with_logo = false) { + let formated = this.format(data); + this.output(with_logo ? this.add_logo(formated) : formated.join("\n")); + } + + add_logo(data) { + let final = (` + ########## + ################## + ###################### + ######################## + ################# ####### + ################## ######## + #### ## # ### #### + ######## #### ## # ###### + ### ### ## #### ## ##### + ## # # # ### ### + ########################## + ######################## + ###################### + ################ + ########## + `).split("\n"); + + // Adding color in logo + let larger_line = 0; + final = _.map(final, (line) => { + line = this.c.blue(line); + larger_line = line.length > larger_line ? line.length : larger_line; + return line; + }); + + let start = ((final.length - data.length) / 2) | 0; + _.each(data, (line) => { + let current = final[start]; + let space = Array(larger_line - current.length + 4).join(' '); + final[start] = `${current}${space}${line}`; + start++; + }); + + return final.join("\n"); + } + + format(data) { + return [ + `${this.c.cyan("Version")} : ${this.c.blue(data.version)}`, + `${this.c.cyan("OS")} : ${this.c.blue(data.os)}`, + `${this.c.cyan("Agent")} : ${data.agent_running}`, + `${this.c.cyan("Docker")} : ${data.docker.Version}`, + `${this.c.cyan("Use vm")} : ${data.use_vm}`, + `${this.c.cyan("VirtualBox")}: ${_.trim(data.vbox_version)}`, + ]; + } +} diff --git a/src/cmds/doctor.js b/src/cmds/doctor.js index 319e1792..3eec1a4c 100644 --- a/src/cmds/doctor.js +++ b/src/cmds/doctor.js @@ -1,80 +1,14 @@ import { CliTrackerController } from 'azk/cli/cli_tracker_controller.js'; -import { Helpers } from 'azk/cli/helpers'; -import { config, lazy_require } from 'azk'; -import { async } from 'azk/utils/promises'; -import Azk from 'azk'; - -var lazy = lazy_require({ - Client: ['azk/agent/client'], -}); +import { config } from 'azk'; export default class Doctor extends CliTrackerController { - get docker() { - return require('azk/docker').default; - } - - index(opts) { - return async(this, function* () { - // Get agent status - var agent = yield lazy.Client.status(); - var require_vm = config("agent:requires_vm"); - - // Load configs from agent - if (agent.agent) { - yield Helpers.requireAgent(this.ui); - } - - // Mount data to render - var data = { - version: Azk.version, - docker: require_vm && !agent.agent ? { Version: this.ui.c.red("down") } : yield this.docker.version(), - use_vm: require_vm ? this.ui.c.green("yes") : this.ui.c.yellow("no"), - agent_running: agent.agent ? this.ui.c.green("up") : this.ui.c.red("down"), - }; - - if (require_vm && agent.agent) { - var ip = config('agent:vm:ip'); - data.use_vm = data.use_vm + ', ip: ' + this.ui.c.yellow(ip); - } - - var render = opts.logo ? this.render_with_logo : this.render_normal; - this.ui.output(render.apply(this, [data])); - return 0; - }); - } - - render_with_logo(data) { - var data_string = this.render_normal(data); - data_string = data_string.split("\n"); - - var azk_logo = ` - ${this.ui.c.blue("##########")} - ${this.ui.c.blue("##################")} - ${this.ui.c.blue("######################")} - ${this.ui.c.blue("########################")} ${data_string[1]} - ${this.ui.c.blue("################# #######")} ${data_string[2]} - ${this.ui.c.blue("################## ########")} ${data_string[3]} - ${this.ui.c.blue("#### ## # ### ####")} ${data_string[4]} - ${this.ui.c.blue("######## #### ## # ######")} - ${this.ui.c.blue("### ### ## #### ## #####")} - ${this.ui.c.blue("## # # # ### ###")} - ${this.ui.c.blue("##########################")} - ${this.ui.c.blue("########################")} - ${this.ui.c.blue("######################")} - ${this.ui.c.blue("################")} - ${this.ui.c.blue("##########")} - `; - - return azk_logo; - } - - render_normal(data) { - var result = ` - ${this.ui.c.cyan("Version")} : ${this.ui.c.blue(data.version)} - ${this.ui.c.cyan("Agent")} : ${data.agent_running} - ${this.ui.c.cyan("Docker")} : ${data.docker.Version} - ${this.ui.c.cyan("Use vm")} : ${data.use_vm} - `; - return result; + index() { + if (config('flags:show_deprecate')) { + this.ui.deprecate("commands.doctor.deprecated"); + } + let cmd = ["version", "--full"]; + var options = this.normalized_params.options; + if (options.log) { cmd.push("--logo"); } + return this.runShellInternally(cmd); } } diff --git a/src/cmds/version.js b/src/cmds/version.js index 72417d85..bbeb2e79 100644 --- a/src/cmds/version.js +++ b/src/cmds/version.js @@ -1,6 +1,17 @@ import { CliTrackerController } from 'azk/cli/cli_tracker_controller.js'; +import { Helpers } from 'azk/cli/helpers'; +import { config, lazy_require } from 'azk'; +import { async } from 'azk/utils/promises'; +import { deviceInfo } from 'azk/utils'; +import { VersionView } from 'azk/cli/views/version_view'; import Azk from 'azk'; -import { config } from 'azk'; + +var lazy = lazy_require({ + Client : ['azk/agent/client'], + VM : ['azk/agent/vm'], + osName : 'os-name', + docker : ['azk/docker', 'default'], +}); export default class Version extends CliTrackerController { constructor(...args) { @@ -10,17 +21,47 @@ export default class Version extends CliTrackerController { // get version, commit id and commit date to create output index() { - let versionOutput = `azk version ${Azk.version}, build `; - const azk_last_commit_id = config('azk_last_commit_id'); - return Azk.commitId(azk_last_commit_id) - .then((commitId) => { - versionOutput = versionOutput + commitId + ', date '; - const azk_last_commit_date = config('azk_last_commit_date'); - return Azk.commitDate(azk_last_commit_date); - }) - .then((commitDate) => { - versionOutput = versionOutput + commitDate; - this.ui.output(versionOutput); + var options = this.normalized_params.options; + return (options.full) ? this.full(options) : this.short(); + } + + short() { + return Azk.fullVersion().then((version) => { + this.ui.output(version); + return 0; + }); + } + + full(opts) { + return async(this, function* () { + // Get agent status + var agent = yield lazy.Client.status(); + var require_vm = config("agent:requires_vm"); + + // Load configs from agent + if (agent.agent) { + yield Helpers.requireAgent(this.ui); + } + + // Mount data to render + let device = deviceInfo(); + let data = { + os : `${device.os} - [${device.proc_arch}], memory: ${device.total_memory}MB`, + version: yield Azk.fullVersion(), + docker : require_vm && !agent.agent ? { Version: this.ui.c.red("down") } : yield lazy.docker.version(), + use_vm : require_vm ? this.ui.c.green("yes") : this.ui.c.yellow("no"), + agent_running: agent.agent ? this.ui.c.green("up") : this.ui.c.red("down"), + vbox_version : require_vm ? yield lazy.VM.version() : this.ui.c.red('not applicable'), + }; + + if (require_vm && agent.agent) { + var ip = config('agent:vm:ip'); + data.use_vm = data.use_vm + ', ip: ' + this.ui.c.yellow(ip); + } + + // Show doctor info + (new VersionView(this.ui)).render(data, opts.logo); + return 0; }); } diff --git a/src/index.js b/src/index.js index 80d48154..38e30cc3 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,23 @@ class Azk { return require('package.json').version; } + static fullVersion() { + let config = GeralLib.config; + let versionOutput = `azk version ${this.version}, build `; + + const azk_last_commit_id = config('azk_last_commit_id'); + return this.gitCommitIdAsync(azk_last_commit_id) + .then((commitId) => { + versionOutput = versionOutput + commitId + ', date '; + const azk_last_commit_date = config('azk_last_commit_date'); + return this.gitCommitDateAsync(azk_last_commit_date); + }) + .then((commitDate) => { + versionOutput = versionOutput + commitDate; + return versionOutput; + }); + } + static gitCommitIdAsync(azk_last_commit_id) { const path = GeralLib.path; const config = GeralLib.config; @@ -88,6 +105,7 @@ var GeralLib = { get fsAsync () { return require('file-async'); }, get utils () { return require('azk/utils'); }, get version () { return Azk.version; }, + fullVersion () { return Azk.fullVersion(); }, get commitId () { return Azk.gitCommitIdAsync; }, get commitDate () { return Azk.gitCommitDateAsync; }, get isBlank () { return isBlank; }, diff --git a/src/utils/index.js b/src/utils/index.js index c4a70480..3864cfb9 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -211,6 +211,17 @@ var Utils = { return _.compact(_.isArray(value) ? value : [value]); }, + deviceInfo() { + let os = require('os'); + return { + "os" : require('os-name')(), + "proc_arch" : os.arch(), + "total_memory": Math.floor(os.totalmem() / 1024 / 1024), + "cpu_info" : os.cpus()[0].model, + "cpu_count" : os.cpus().length + }; + }, + }; export default Utils; diff --git a/src/utils/tracker.js b/src/utils/tracker.js index e30efa5b..069b8dcb 100644 --- a/src/utils/tracker.js +++ b/src/utils/tracker.js @@ -4,10 +4,9 @@ import Azk from 'azk'; import { _, config, log, t, lazy_require } from 'azk'; import { meta as azkMeta } from 'azk'; import { promisify, TimeoutError, promiseResolve } from 'azk/utils/promises'; +import { deviceInfo } from 'azk/utils'; var lazy = lazy_require({ - os : 'os', - osName : 'os-name', uuid : 'node-uuid', InsightKeenIo: 'insight-keen-io', InsightKeenIoWithMeta: () => { @@ -121,13 +120,7 @@ export class Tracker { "azk_version" : Azk.version, // device config - "device_info": { - "os" : lazy.osName(), - "proc_arch" : lazy.os.arch(), - "total_memory": Math.floor(lazy.os.totalmem() / 1024 / 1024), - "cpu_info" : lazy.os.cpus()[0].model, - "cpu_count" : lazy.os.cpus().length - } + "device_info": deviceInfo(), }; log.debug(`[tracker] permission to tracker: ${this.loadTrackerPermission()}`); From 2fe660b79719438e090e0f2354eca23e4ea9d62e Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 4 Apr 2016 17:31:37 -0300 Subject: [PATCH 54/61] [Suggestions] Fixing string interpolation token for Postgres and MySQL suggestions --- src/generator/suggestions/mysql-5.6.js | 2 +- src/generator/suggestions/postgres-9.4.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator/suggestions/mysql-5.6.js b/src/generator/suggestions/mysql-5.6.js index b916e83a..b6658f57 100644 --- a/src/generator/suggestions/mysql-5.6.js +++ b/src/generator/suggestions/mysql-5.6.js @@ -39,7 +39,7 @@ export class Suggestion extends DefaultSuggestion { ], export_envs: { DATABASE_URL: "mysql2://#{envs.MYSQL_USER}:#{envs.MYSQL_PASSWORD}@#{net.host}" + - ":#{net.port.data}/${envs.MYSQL_DATABASE}", + ":#{net.port.data}/#{envs.MYSQL_DATABASE}", }, }); } diff --git a/src/generator/suggestions/postgres-9.4.js b/src/generator/suggestions/postgres-9.4.js index a1ac07f3..21e37d9d 100644 --- a/src/generator/suggestions/postgres-9.4.js +++ b/src/generator/suggestions/postgres-9.4.js @@ -40,7 +40,7 @@ export class Suggestion extends UIProxy { ], export_envs: { DATABASE_URL: "postgres://#{envs.POSTGRES_USER}:#{envs.POSTGRES_PASS}" + - "@#{net.host}:#{net.port.data}/${envs.POSTGRES_DB}", + "@#{net.host}:#{net.port.data}/#{envs.POSTGRES_DB}", }, }); } From 1e356b45e8fe587f871fa97dc5dee8d2a984aa3f Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 4 Apr 2016 17:42:08 -0300 Subject: [PATCH 55/61] [Suggestions] Increasing default timeout (50), MySQL(150) and Postgres(150) --- src/generator/rules.js | 2 +- src/generator/suggestions/mysql-5.6.js | 2 +- src/generator/suggestions/postgres-9.4.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/generator/rules.js b/src/generator/rules.js index e8ba15c2..b92af9f1 100644 --- a/src/generator/rules.js +++ b/src/generator/rules.js @@ -9,7 +9,7 @@ var example_system = { shell : '/bin/bash', image : { docker: '[repository]:[tag]' }, workdir : '/azk/#{manifest.dir}', - wait: 20, + wait : 50, balancer: true, command : '# command to run app', mounts : { diff --git a/src/generator/suggestions/mysql-5.6.js b/src/generator/suggestions/mysql-5.6.js index b6658f57..11fb76b8 100644 --- a/src/generator/suggestions/mysql-5.6.js +++ b/src/generator/suggestions/mysql-5.6.js @@ -26,7 +26,7 @@ export class Suggestion extends DefaultSuggestion { mounts: { '/var/lib/mysql': {type: 'persistent', value: '#{manifest.dir}/mysql'}, }, - wait: 25, + wait: 150, envs: { // set instances variables MYSQL_USER : "azk", diff --git a/src/generator/suggestions/postgres-9.4.js b/src/generator/suggestions/postgres-9.4.js index 21e37d9d..6cea886c 100644 --- a/src/generator/suggestions/postgres-9.4.js +++ b/src/generator/suggestions/postgres-9.4.js @@ -27,6 +27,7 @@ export class Suggestion extends UIProxy { '/var/lib/postgresql/data' : {type: 'persistent', value: 'postgresql'}, '/var/log/postgresql' : {type: 'path', value: './log/postgresql'}, }, + wait: 150, envs: { // set instances variables // Move this to .env file From d5d9089e05b9b40c61acc781aa04b4b7fd6c6355 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Mon, 4 Apr 2016 18:30:41 -0300 Subject: [PATCH 56/61] [Spec] Fixing suggestion tests --- .../generator/rules/generation/mysql_and_postgres_gen_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/generator/rules/generation/mysql_and_postgres_gen_spec.js b/spec/generator/rules/generation/mysql_and_postgres_gen_spec.js index 74a0010e..3ddce144 100644 --- a/spec/generator/rules/generation/mysql_and_postgres_gen_spec.js +++ b/spec/generator/rules/generation/mysql_and_postgres_gen_spec.js @@ -97,7 +97,7 @@ describe('Azk generator db', function() { // __options h.expect(system).to.have.deep.property('options.depends').and.to.eql([]); h.expect(system).to.have.deep.property('options.shell', '/bin/bash'); - h.expect(system).to.have.deep.property('options.wait', 25); + h.expect(system).to.have.deep.property('options.wait', 150); h.expect(system).to.not.have.deep.property('options.workdir'); h.expect(system).to.have.deep.property('options.mounts').and.to.eql( { '/var/lib/mysql': { type: 'persistent', @@ -124,7 +124,7 @@ describe('Azk generator db', function() { // __options h.expect(system).to.have.deep.property('options.depends').and.to.eql([]); h.expect(system).to.have.deep.property('options.shell', '/bin/bash'); - h.expect(system).to.have.deep.property('options.wait', 20); + h.expect(system).to.have.deep.property('options.wait', 150); h.expect(system).to.not.have.deep.property('options.workdir'); h.expect(system).to.have.deep.property('options.mounts').and.to.eql( { '/var/lib/postgresql/data': { type: 'persistent', value: 'postgresql', options: {} }, From 731e500e6dcfce3f1a10bc55873d24511a3b408a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Ribeiro?= Date: Tue, 5 Apr 2016 16:51:10 -0300 Subject: [PATCH 57/61] [Cli] Adding more options to `azk info` --- CHANGELOG.md | 1 + docs/content/en/reference/cli/info.md | 14 ++++- docs/content/pt-BR/reference/cli/info.md | 14 +++-- shared/locales/usage-en-US.txt | 7 ++- src/cmds/info.js | 69 +++++++++++++++++++----- 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d0331e4..40f70864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * [Docs] Updating whole docs structure: Azkfile, Gitbook version and plugins; * [Cli] `azk version` changes to display the first 7 digits of the commit hash and the date of the corresponding commit version; * [Cli] Adding options `--full` and `--logo` the command `azk version`, to show more information (old `azk doctor`); + * [Cli] Adding more options to `azk info`, now have a `--filter` and `--json`; * Deprecations * [Cli] Deprecating `azk doctor` command, now use `azk version --full`; diff --git a/docs/content/en/reference/cli/info.md b/docs/content/en/reference/cli/info.md index 0c191ce2..56ec795c 100644 --- a/docs/content/en/reference/cli/info.md +++ b/docs/content/en/reference/cli/info.md @@ -4,14 +4,24 @@ #### Usage: - azk info [options] + azk info [] [options] + +#### Examples: + +``` +azk info +azk info web +azk info web,worker --filter=mounts,env +``` #### Options: ``` - --no-color Remove colors from output + --json Outputs in json format. + --filter= Filter system properties [default: all]. --quiet, -q Never prompt. --help, -h Shows help usage. + --no-color Remove colors from output --log=, -l Sets log level (default: error). --verbose, -v Sets the level of detail - multiple supported (-vv == --verbose 2) [default: 0]. ``` diff --git a/docs/content/pt-BR/reference/cli/info.md b/docs/content/pt-BR/reference/cli/info.md index b8c82cd2..63aba4bc 100644 --- a/docs/content/pt-BR/reference/cli/info.md +++ b/docs/content/pt-BR/reference/cli/info.md @@ -2,18 +2,26 @@ Exite informações dos sistemas do atual `Azkfile.js`. -#### Uso: +#### Usage: + + azk info [] [options] + +#### Examples: ``` - azk info [options] +azk info +azk info web +azk info web,worker --filter=mounts,env ``` #### Opções: ``` - --no-color Remove cores na saída padrão + --json Gera uma saída no formato json. + --filter= Filtras quais as propriedades [padrão: all]. --quiet, -q Nunca perguntar. --help, -h Mostrar ajuda de uso. + --no-color Remove cores na saída padrão --log=, -l Defini o nível de log (padrão: error). --verbose, -v Defini o nível de detalhes da saída - suporta múltiplos (-vv == --verbose 2) [padrão: 0]. ``` diff --git a/shared/locales/usage-en-US.txt b/shared/locales/usage-en-US.txt index 0ff5d128..6bf449a0 100644 --- a/shared/locales/usage-en-US.txt +++ b/shared/locales/usage-en-US.txt @@ -10,7 +10,7 @@ Usage: azk deploy rollback [] [-qh --no-color -l ] [-v]... azk deploy [-qh --no-color -l ] [-v]... azk docker [-qh --no-color -l ] [-v]... [-- ...] - azk info [-qh --no-color -l ] [-v]... + azk info [] [--json --filter=] [-qh --no-color -l ] [-v]... azk init [] [--filename --force] [-qh --no-color -l ] [-v]... azk logs [ ] [--no-timestamps --follow --lines=] [-qh --no-color -l ] [-v]... azk open [] [--open-with=] [-qh --no-color -l ] [-v]... @@ -84,15 +84,19 @@ Options: --cwd=, -C Sets the current working directory. --env=, -e Additional environment variables - multiple supported (`-e HTTP_PORT=5000 -e PORT=5000`). --filename Shows the manifest filename. + --filter= Filter system properties [default: all]. --force, -F Force mode on. --follow, -f Follows log output. + --git-ref= Git branch, tag or commit to clone --help, -h Shows help usage. --image=, -i Defines the image in which the command will be executed. + --json Outputs in json format. --log=, -l Sets log level (default: error). --logo Shows the azk logo before showing health information. --full Shows full version information. --long Show all columns. --mount=, -m Additional mounting points - multiple supported (`-m ~/Home:/azk/user -m ~/data:/var/data`). + --mounts Only show mounts options. --lines=, -n Outputs the specified number of lines at the end of logs [default: all]. --no-daemon Runs `azk agent` in foreground. --no-reload-vm Do not reload Virtual Machine settings. @@ -110,7 +114,6 @@ Options: --tty, -t Forces pseudo-tty allocation. --text Shows output in plain text mode. --verbose, -v Sets the level of detail - multiple supported (-vv == --verbose 2) [default: 0]. - --git-ref= Git branch, tag or commit to clone --silent Prevents any log message about command execution. It's useful when using the `-c` option and the output is used as input to another command using the pipe `|` operator. --version Shows azk version. diff --git a/src/cmds/info.js b/src/cmds/info.js index 99f88b6e..fb7b021a 100644 --- a/src/cmds/info.js +++ b/src/cmds/info.js @@ -11,12 +11,31 @@ var lazy = lazy_require({ export default class Info extends CliTrackerController { index() { return async(this, function* () { + let args = this.normalized_params.arguments; + let options = this.normalized_params.options; + // Requirements yield Helpers.requireAgent(this.ui); var manifest = new lazy.Manifest(this.cwd, true); + Helpers.manifestValidate(this.ui, manifest); + + let systems = args.system; + let show_manifest = _.isEmpty(systems); + if (show_manifest) { + systems = manifest.systems; + } else { + systems = manifest.getSystemsByName(systems); + } + + let filters = options.filter; + if (_.isEmpty(filters) || filters === "all" || filters === "*") { + filters = []; + } else { + filters = options.filter.split(","); + } // Mount data to show - var data = _.reduce(manifest.systems, (data, system) => { + let systems_data = _.reduce(systems, (data, system) => { var obj = {}; obj[system.image.provider] = system.image.name; var system_data = { @@ -39,22 +58,44 @@ export default class Info extends CliTrackerController { delete system_data.ports; } - data.systems[this.ui.c.yellow(system.name)] = system_data; + // Filters + if (!_.isEmpty(filters)) { + system_data = _.reduce(system_data, (data, value, key) => { + if (filters.indexOf(key) > -1) { + data[key] = value; + } + return data; + }, {}); + } + + data[this.ui.c.yellow(system.name)] = system_data; return data; - }, { - manifest_id : manifest.namespace, - manifest : manifest.file, - cache_dir : manifest.cache_dir, - default_system: manifest.systemDefault.name, - systems : {} - }); + }, {}); + + // Include manifest global infos + var data = systems_data; + if (show_manifest) { + data = { + manifest_id : manifest.namespace, + manifest : manifest.file, + cache_dir : manifest.cache_dir, + default_system: manifest.systemDefault.name, + systems : data, + }; + } // Show result - this.ui.output(lazy.prettyjson.render(data, { - noColor: !this.ui.useColours(), - dashColor: "magenta", - stringColor: "blue", - })); + if (options.json) { + let keys = _.keys(data); + if (keys.length === 1) { data = data[keys[0]]; } + this.ui.output(JSON.stringify(data)); + } else { + this.ui.output(lazy.prettyjson.render(data, { + noColor: !this.ui.useColours(), + dashColor: "magenta", + stringColor: "blue", + })); + } return 0; }); From 77f85e9d0ca6f386a493f0e29fa0fb73380c1b50 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Tue, 5 Apr 2016 18:30:26 -0300 Subject: [PATCH 58/61] [Cli] Improving `azk version --full` output + retrieving docker version from CLI as fallback --- spec/cmds/doctor_spec.js | 4 ++-- src/cli/ui.js | 10 ++++++++-- src/cli/views/version_view.js | 6 +++--- src/cmds/version.js | 31 ++++++++++++++++++++++++++----- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/spec/cmds/doctor_spec.js b/spec/cmds/doctor_spec.js index 51dc8556..18fc32d4 100644 --- a/spec/cmds/doctor_spec.js +++ b/spec/cmds/doctor_spec.js @@ -20,7 +20,7 @@ describe('Azk cli, doctor controller', function() { h.expect(code).to.equal(0); h.expect(options).to.have.property('doctor', true); h.expect(outputs[0]).to.match(RegExp(`Version.*\:.*${h.escapeRegExp(Azk.version)}`)); - h.expect(outputs[0]).to.match(RegExp(`Agent.*\:.*up`)); + h.expect(outputs[0]).to.match(RegExp(`Agent.*\:.*Running`)); }); }); @@ -31,7 +31,7 @@ describe('Azk cli, doctor controller', function() { h.expect(code).to.equal(0); h.expect(options).to.have.property('doctor', true); h.expect(outputs[0]).to.match(RegExp(`Version.*\:.*${h.escapeRegExp(Azk.version)}`)); - h.expect(outputs[0]).to.match(RegExp(`Agent.*\:.*up`)); + h.expect(outputs[0]).to.match(RegExp(`Agent.*\:.*Running`)); }); }); }); diff --git a/src/cli/ui.js b/src/cli/ui.js index 2607452d..91699405 100644 --- a/src/cli/ui.js +++ b/src/cli/ui.js @@ -154,9 +154,15 @@ var UI = { if (callback) { return lazy.execShLib(command, options, callback); } else { - var result = (err) => { return (err) ? err.code : 0; }; var execShLib = promisify(lazy.execShLib, { multiArgs: true }); - return execShLib(command, options).spread(result).catch(result); + return execShLib(command, options) + .spread((stdout, stderr) => { + if (options === true) { + return { stdout, stderr }; + } + return 0; + }) + .catch((err) => err.code); } }, diff --git a/src/cli/views/version_view.js b/src/cli/views/version_view.js index 6da0e89f..2a4fcc72 100644 --- a/src/cli/views/version_view.js +++ b/src/cli/views/version_view.js @@ -47,11 +47,11 @@ export class VersionView extends View { format(data) { return [ - `${this.c.cyan("Version")} : ${this.c.blue(data.version)}`, - `${this.c.cyan("OS")} : ${this.c.blue(data.os)}`, + `${this.c.cyan("Version")} : ${data.version}`, + `${this.c.cyan("OS")} : ${data.os}`, `${this.c.cyan("Agent")} : ${data.agent_running}`, `${this.c.cyan("Docker")} : ${data.docker.Version}`, - `${this.c.cyan("Use vm")} : ${data.use_vm}`, + `${this.c.cyan("Uses VM")} : ${data.use_vm}`, `${this.c.cyan("VirtualBox")}: ${_.trim(data.vbox_version)}`, ]; } diff --git a/src/cmds/version.js b/src/cmds/version.js index bbeb2e79..5da693d3 100644 --- a/src/cmds/version.js +++ b/src/cmds/version.js @@ -38,20 +38,28 @@ export default class Version extends CliTrackerController { var agent = yield lazy.Client.status(); var require_vm = config("agent:requires_vm"); + let docker_version = null; + // Load configs from agent if (agent.agent) { yield Helpers.requireAgent(this.ui); + docker_version = yield lazy.docker.version(); } // Mount data to render let device = deviceInfo(); + + if (device.os.match(/^Linux\ /) && !docker_version) { + docker_version = yield this._getDockerVersionByCli(); + } + let data = { - os : `${device.os} - [${device.proc_arch}], memory: ${device.total_memory}MB`, + os : `${device.os} (${device.proc_arch}), Memory: ${device.total_memory}MB`, version: yield Azk.fullVersion(), - docker : require_vm && !agent.agent ? { Version: this.ui.c.red("down") } : yield lazy.docker.version(), - use_vm : require_vm ? this.ui.c.green("yes") : this.ui.c.yellow("no"), - agent_running: agent.agent ? this.ui.c.green("up") : this.ui.c.red("down"), - vbox_version : require_vm ? yield lazy.VM.version() : this.ui.c.red('not applicable'), + docker : docker_version || { Version: "Down" }, + use_vm : require_vm ? "Yes" : "No", + agent_running: agent.agent ? "Running" : "Stopped", + vbox_version : require_vm ? yield lazy.VM.version() : "N/A", }; if (require_vm && agent.agent) { @@ -65,4 +73,17 @@ export default class Version extends CliTrackerController { return 0; }); } + + _getDockerVersionByCli() { + let cmd = 'docker --version'; + let regex = /^Docker\ version\ (\d+.\d+.\d+)/; + + return this.ui.execSh(cmd, true).then((result) => { + if (!result.stdout) { + return null; + } + let match = result.stdout.match(regex); + return match ? { Version: `${match[1]} (CLI)` } : null; + }); + } } From 91a2c4a3bd272ec203a98ac900d5457807801d18 Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Tue, 5 Apr 2016 18:26:19 -0300 Subject: [PATCH 59/61] [Package] Don't track events on packaging and testing --- src/libexec/package-tools/mac/test.sh | 3 +++ src/libexec/package-tools/pack.sh | 13 +++++++++++++ src/libexec/package-tools/test-container.sh | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/src/libexec/package-tools/mac/test.sh b/src/libexec/package-tools/mac/test.sh index 51ab6c45..d56e046d 100755 --- a/src/libexec/package-tools/mac/test.sh +++ b/src/libexec/package-tools/mac/test.sh @@ -20,7 +20,10 @@ setup_test() { cd $TEST_DIR rm -Rf Azkfile.js .azk/ + + export AZK_ENV=development bazk config set terms_of_use.accepted 1 > /dev/null 2>&1 + bazk init ls Azkfile.js > /dev/null 2>&1 bazk start --reprovision diff --git a/src/libexec/package-tools/pack.sh b/src/libexec/package-tools/pack.sh index d19397ee..4cb075d3 100755 --- a/src/libexec/package-tools/pack.sh +++ b/src/libexec/package-tools/pack.sh @@ -23,6 +23,7 @@ Options: --publish, -p Publish the generated packages after build --no-version Don't create a new commit bumping azk version into package.json (adding release channel and date) --no-tag Don't create git version tag to last commit + --dry Don't run the steps and set -x --verbose, -v Displays more detailed info about each building and packaging step --help, -h Show this message " @@ -91,6 +92,8 @@ while [[ $# -gt 0 ]]; do PUBLISH=true;; --no-tag ) NO_TAG=true;; + --dry ) + DRY_RUNNING=true;; --verbose | -v ) VERBOSE=true;; --help | -h ) @@ -99,6 +102,10 @@ while [[ $# -gt 0 ]]; do esac done +if [[ ${DRY_RUNNING} == true ]]; then + set -x +fi + PACKAGE_SUFFIX="-${RELEASE_CHANNEL}" case $RELEASE_CHANNEL in @@ -192,6 +199,11 @@ step_done() { } step_run() { + if [[ ${DRY_RUNNING} == true ]]; then + step_skip "${@}" + return + fi + step $1; shift STEP_EXIT="" @@ -228,6 +240,7 @@ start_agent() { > /dev/null 2>&1 fi + export AZK_ENV=development azk config set terms_of_use.accepted 1 > /dev/null 2>&1 azk agent stop diff --git a/src/libexec/package-tools/test-container.sh b/src/libexec/package-tools/test-container.sh index 58f28114..bf4ca465 100755 --- a/src/libexec/package-tools/test-container.sh +++ b/src/libexec/package-tools/test-container.sh @@ -40,7 +40,9 @@ fail() { start_agent() { azk config set terms_of_use.accepted 1 > /dev/null 2>&1 + azk agent start --no-daemon > $AZK_AGENT_LOG_FILE 2>&1 & + AGENT_PID="$!" tail -F $AZK_AGENT_LOG_FILE & TAIL_PID="$!" @@ -55,6 +57,8 @@ start_agent() { setup() { set -e /usr/local/bin/wrapdocker + export AZK_ENV=development + ${BASE_DIR}/${DISTRO}/install.sh ${CODENAME} ${PKG_SUFFIX} start_agent From 8b9f66c377e2f2c20b1faf88137b12ace53e0c7c Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Wed, 6 Apr 2016 03:35:39 -0300 Subject: [PATCH 60/61] Bumping version to azk v0.18.0 --- npm-shrinkwrap.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f38212c5..545dfc2e 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "azk", - "version": "0.17.0", + "version": "0.18.0", "dependencies": { "archiver": { "version": "0.21.0", diff --git a/package.json b/package.json index 61c68d0c..8d21658a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azk", - "version": "0.17.0", + "version": "0.18.0", "description": "Orchestrate development environments with agility and automation", "main": "index.js", "scripts": { From 9f100beb0799b9342053f6763f1f3f78864e86ae Mon Sep 17 00:00:00 2001 From: Felipe Arenales Date: Thu, 7 Apr 2016 02:40:48 -0300 Subject: [PATCH 61/61] Updating changelog --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f70864..d40d82d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## dev +## v0.18.0 - (2016-04-07) * Enhancements * [Code] Adding github issue and PR templates; @@ -16,8 +16,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Deprecations * [Cli] Deprecating `azk doctor` command, now use `azk version --full`; - -* Deprecations * [Manifest] Deprecating template expander tokens `${}` and `<%%>`; * Bug