向部署者任务添加参数和选项
几个月来,我一直在向我的自定义Deployer脚本中添加内容,现在我使用它来做的不仅仅是部署我的网站。由于它充当到我的网站服务器的连接,我一直在使用Deployer来执行其他任务,例如创建备份和清除Drupal缓存,而无需登录服务器来执行此操作。在这里对我有帮助的是,我以非常模块化的方式设置了部署任务,因此尽管我的部署运行了数据库备份,但没有什么可以阻止我在不进行完整部署的情况下自行运行数据库备份命令。
这让我意识到我有时需要控制这些任务的运行方式。例如,如果我想对我的Drupal站点进行完整备份,我可能需要使用不同的选项备份我的数据库,以便我包含缓存表。我的文件中有许多自定义任务deploy.php,但我不想有几乎重复的任务只是为了运行稍微不同的数据库备份风格。
这让我考虑向Deployer添加选项和参数。尽管在Deployer站点上从技术上记录了添加参数,但我在弄清楚示例的含义时遇到了一些问题,因此我决定创建一个帖子以添加到文档中。
如果您还不知道,自定义任务很容易编写。您需要做的就是在task()函数中定义您的任务,传入任务名称和以下内容之一。
定义要运行的命令的字符串。
在任务运行时运行的闭包。
列出系统内其他任务的数组。
如果您使用闭包,则任务范围内有许多函数可用于运行命令或将输出返回到屏幕。作为一个简单的例子,这是我的自定义任务,用于清除Drupal站点上的缓存。
task('drush:cr', function () { $output = run('{{deploy_path}}/current/vendor/bin/drush cr'); writeln('' . $output . ' '); });
我可以使用字符串参数来运行这个命令,但我喜欢看到正在生成的输出,因此使用闭包来打印输出。我倾向于这样命名任务,所以我所有基于drush的函数都具有“drush:command”格式,这也有助于将命令组合在一起。在上面的示例中,该run()命令在远程服务器上运行命令并返回输出。该writeln()函数将打印出命令运行的输出。运行dep命令将在其他命令列表中显示此任务可用。
$ ../vendor/bin/dep Deployer v6.8.0 Usage: command [options] [arguments] Options: -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question -f, --file[=FILE] Specify Deployer file -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: autocomplete Install command line autocompletion capabilities help Displays help for a command init Initialize deployer in your project list Lists commands run Run any arbitrary command on hosts ssh Connect to host through ssh debug debug:task Display the task-tree for a given task drush drush:cr
要在部署之前在我的站点上运行备份,我有一个自定义任务。这包装了drushsql-dump命令,但还有一些其他设置来排除对灾难恢复不重要的常见缓存表、会话表和看门狗表。
task('drush:backup', function () { if (test('test -f {{deploy_path}}/current/vendor/bin/drush')) { //备份数据库。 $file = 'hashbangcode-' . date('d-m-Y-Hi') . '.sql'; $output = run('{{deploy_path}}/current/vendor/bin/drush sql-dump --gzip --structure-tables-list=cache,cache_bootstrap,cache_config,cache_container,cache_data,cache_default,cache_discovery,cache_filter,cache_menu,cache_page,history,search_dataset,search_index,search_total,sessions,watchdog --result-file=~/backup/' . $file); writeln('' . $output . ' '); //(重新)创建符号链接。 $symlink = '~/backup/latest.sql.gz'; if (test('test -f ' . $symlink)) { run('rm -f ' . $symlink); } $output = run('ln -s ~/backup/' . $file . '.gz ' . $symlink); writeln('' . $output . ' '); } });
这里的最后一步是创建备份的符号链接,这样我就可以下载最新的备份,而无需记住或弄清楚备份文件的名称。这意味着我可以只rsync文件latest.sql.gz而不是试图找出在备份期间创建的文件的名称。
要在运行的命令之外侦听用户输入,有两个选项,这两个选项都是全局定义的。这是理解它们如何工作的关键,因为您在deploy.php文件顶部定义了用户输入,并且所有任务都可以访问潜在地侦听该输入。我们有两种类型的输入是“选项”和“参数”。我现在将逐一讨论,因为它们都有差异。
争论
参数的工作原理是在命令行的末尾附加其他参数,在运行的任务名称之后。
例如,如果我们想使用参数更改数据库函数,我们可以deploy.php像这样在文件中定义它。
use Symfony\Component\Console\Input\InputArgument; argument('include-cache-backup', InputArgument::OPTIONAL, 'Adds the cache tables to the backup command.', false);
正如您可能知道的那样,Deployer使用Symfony组件。这意味着该argument()函数是创建InputArgument对象的包装器。该argument()函数的大部分参数都是不言自明的,但这里的最后一个参数是默认选项,如果用户未输入参数,则设置该选项。
请注意,Deployer已经有一个名为“stage”的参数,这意味着需要在命令行中首先设置该参数。stage参数是您要部署到的服务器的名称,如您的host()函数中所定义,在部署到不同服务器时非常有用。例如,假设您设置了两台服务器,一台称为prod,另一台称为stage。您可以这样定义它们。
host('prod') ->hostname('stage.domain.com') ->set('deploy_path', '/var/www/html/website') host('stage') ->hostname('domain.com') ->set('deploy_path', '/var/www/html/website')
有了这个,您就可以提供“stage”参数以部署到相关主机。例如,要部署到“prod”,您需要向命令提供“prod”参数。
../vendor/bin/depdeployprod
通过添加额外的参数,您需要在要部署到的服务器之前引入它们,否则您定义的参数将收到文本“prod”,该文本用于第一个参数。前面的命令,加上include-cache-backup将成为这个。
../vendor/bin/depdeployyesprod
这会将“yes”的值传递给include-cache-backup参数,将“prod”的值传递给stage参数。在命令行输出中也绝对没有信息表明您向脚本添加了附加参数。因此,记录添加到Deploy脚本的任何其他参数非常重要。否则,您将必须阅读代码才能确定'prod'参数被传递给了错误的参数。
要侦听发送到命令的参数,您需要使用该input()函数来提取来自用户的传入输入。在以下示例中,我们从提供的参数中提取数据。参数没有命名,只是按照它们定义的顺序提取,所以include-cache-backup将映射到发送到命令的第一个参数。
task('test:argument', function () { $includeCacheBackup = null; if (input()->hasArgument('include-cache-backup')) { $includeCacheBackup = input()->getArgument('include-cache-backup'); } writeln('include-cache-backup ' . var_export($includeCacheBackup, true)); });
在针对此任务运行上述示例时,这将在命令行上打印出“yes”。显然,在尝试使用它之前,您需要仔细检查以确保传入的参数是正确的。该hasArgument()方法非常重要,因为它意味着您可以在没有任何参数的情况下运行您的命令。我发现最好的做法是假设一些合理的默认值,而不是在参数不存在时抛出错误。由于我们在设置时为我们的参数提供了默认值,如果参数不存在,我们将始终收到“false”。
这可能不是用于更改任务操作方式的开关的正确输入类型。
选项
一个选项通过侦听您添加到命令中的标志来工作,并且与参数输入类型完全分开工作。例如,如果我们想使用一个选项更改数据库备份任务,我们在deploy.php文件中像这样定义它。
use Symfony\Component\Console\Input\InputOption; option('include-cache-backup', 'i', InputOption::VALUE_OPTIONAL, 'Adds the cache tables to the backup command.', false);
有了这个,Deploy将我们的自定义选项添加到命令行输出的选项列表中。同样,参数列表末尾的“false”是选项的默认值。在没有任何输入的情况下运行dep命令会在选项列表中显示这一点。
$ ../vendor/bin/dep Deployer v6.8.0 Usage: command [options] [arguments] Options: -h, --help Display this help message ... snip ... -i, --include-cache-backup[=INCLUDE-CACHE-BACKUP] Adds the cache tables to the backup command. [default: false]
要使用新选项,我们只需将其作为标志添加到命令中即可。这是在“阶段”参数之前还是之后都没有关系,因为它不会干扰参数。
$../vendor/bin/depdeploy--include-cache-backup=true
请注意,该option()函数的第二个参数是快捷方式。这可用于简化命令行上的选项。以下命令等效于上面的示例。
$../vendor/bin/depdeploy-itrue
要侦听与命令一起使用的选项,您需要使用该input()函数从用户那里提取传入的输入。在以下示例中,我们从提供的名为“include-cache-backup”的选项中提取数据。选项已命名,以便在定义时可以通过选项集的名称提取它们。
task('test:option', function () { $includeCacheBackup = null; if (input()->hasOption('include-cache-backup')) { $includeCacheBackup = input()->getOption('include-cache-backup'); } writeln('include-cache-backup ' . var_export($includeCacheBackup, true)); });
在针对此任务运行上述示例时,这将打印出字符串'true'。同样,在尝试使用传入参数之前,您可能应该确保它们的格式正确。该选项作为字符串传递给任务,因此您需要进行自己的转换以确保它是正确的类型。本质上,不要相信用户输入。
由于选项不会干扰Deployer的默认运行,并且在命令行上进行自我记录,因此在查看参数之前使用它们是有意义的。事实上,我强烈建议你不要使用参数,除非你有很好的理由这样做。在接受用户对Deployer脚本的输入时,Options应该是您的首选。
新的备份功能
掌握了这些知识后,让我们看看重新创建我的数据库备份任务,并选择包含或排除缓存表。默认情况下,我不希望脚本在备份中包含缓存表,因此默认选项为false。如果用户添加了--include-cache-backup选项,那么他们可以传入'yes'或根本不传入,因为我们也检测到该选项。这意味着只需将标志传递给脚本就足以启用该选项并关闭缓存表备份。
请注意,我没有直接使用来自用户的传入值,我只是故意使用它来检测我稍后应该在脚本中做什么。这在远程服务器上运行原始命令时尤其重要,这实际上就是我们在这里所做的。
use Symfony\Component\Console\Input\InputOption; option('include-cache-backup', 'i', InputOption::VALUE_OPTIONAL, 'Adds the cache tables to the backup command.', false); task('drush:backup', function () { if (test('test -f {{deploy_path}}/current/vendor/bin/drush')) { //定义文件名。 $file = 'hashbangcode-' . date('d-m-Y-Hi') . '.sql'; //检测选项。 $includeCacheBackup = false; if (input()->hasOption('include-cache-backup')) { $includeCacheBackupInput = input()->getOption('include-cache-backup'); if ($includeCacheBackupInput == 'yes' || $includeCacheBackupInput == '') { $includeCacheBackup = true; } } //对选项做出反应。 if ($includeCacheBackup === TRUE) { $structureTablesList = ''; } else { $structureTablesList = '--structure-tables-list=cache,cache_bootstrap,cache_config,cache_container,cache_data,cache_default,cache_discovery,cache_filter,cache_menu,cache_page,history,search_dataset,search_index,search_total,sessions,watchdog '; } //备份数据库。 $output = run('{{deploy_path}}/current/vendor/bin/drush sql-dump ' . $structureTablesList . '--gzip --result-file=~/backup/' . $file); writeln('' . $output . ' '); //(重新)创建符号链接。 $symlink = '~/backup/latest.sql.gz'; if (test('test -f ' . $symlink)) { run('rm -f ' . $symlink); } $output = run('ln -s ~/backup/' . $file . '.gz ' . $symlink); writeln('' . $output . ' '); } });
这非常有效。我可以改进这一点,尽管更好地生成备份命令而不是仅仅将字符串插入在一起。另外,因为--include-cache-backup选项被定义为一个全局选项,所以无论现在调用drush:backup任务都可以使用它。这意味着我可以通过调用drush:backup直接使用它,也可以通过默认的drush任务间接使用它。