Compare commits

..

335 commits

Author SHA1 Message Date
Arnout Boks
64b0f60368 Removed unused 'standard' option for PhpCpd plugin (#1249) 2016-08-17 12:35:21 +01:00
Dan Cryer
42ca1c6527 Fix for handling old unserialize mode. 2016-06-29 10:13:27 +01:00
Dan Cryer
4d0911f2a9 Bug fixes
Signed-off-by: Dan Cryer <dan@block8.co.uk>
2016-06-23 11:30:41 +01:00
Dan Cryer
3cdaef8fa9 Update README.md 2016-06-03 17:30:45 +01:00
David Rimbault
db93f55427 [FIX] Running Complete stage even on Exception catch. (#1186) 2016-04-27 16:59:29 +01:00
Dan Cryer
49db1a26ba
Make sure we always show the correct error count on the build errors tab. 2016-04-27 16:40:55 +01:00
Dan Cryer
77e9710d09
Testing PHPMD error supression 2016-04-27 15:45:24 +01:00
Dan Cryer
7027055711
Merge branch 'feature/bitbucket-webhooks' 2016-04-27 15:35:10 +01:00
Dan Cryer
21ba39e6ff
Fixes 2016-04-27 15:27:27 +01:00
Dan Cryer
6418fde928 Defaulting to using neither --prefer-source or --prefer-dist for composer. Allow user to specify either. 2016-04-27 15:20:20 +01:00
Dan Cryer
21d5f4954f
Adding a debug mode to the worker so you can see what commands are being run. 2016-04-27 14:56:52 +01:00
Dan Cryer
1f035463e4 Merge branch 'patch-4' of https://github.com/wodka/PHPCI into feature/bitbucket-webhooks 2016-04-27 14:26:44 +01:00
Dan Cryer
5ead42a7c2
README tweaks. 2016-04-27 14:18:40 +01:00
Dan Cryer
1e13538c28
Updating README 2016-04-27 14:13:42 +01:00
Dan Cryer
424f1c5c4b Removing Test from README 2016-04-27 14:08:01 +01:00
Dan Cryer
dc9aa195d1 Merge branch 'master' of github.com:Block8/PHPCI 2016-04-27 12:35:17 +01:00
Dan Cryer
1e2cbcaf3d Fixing duplicate error listing in builds.
Closes #1130
2016-04-27 12:33:07 +01:00
David Valdez
e567088a00 now take in account the errors in the codeception plugin (#1206) 2016-04-27 12:10:46 +01:00
Dan Cryer
ffa593f1a3 Fixing PHPCS errors 2016-04-27 12:02:13 +01:00
Dan Cryer
cc6d0f2964 Updating dependencies 2016-04-27 11:43:57 +01:00
Dan Cryer
e6b02b6019 Merge remote-tracking branch 'origin/master' into v/1.7 2016-04-27 11:32:50 +01:00
Dan Cryer
81eadcf3f5 Contributing guidelines, issue template and PR template. 2016-04-27 11:29:03 +01:00
Dan Cryer
f200bd0411 Fixing MySQL strict-mode install error.
Fixes #977
Fixes #818
2016-04-27 11:06:32 +01:00
Mike Bronner
fe9289eda5 Remove whitespace from empty lines, reformatted inline comments 2016-02-08 12:59:15 +01:00
Michael Schramm
66ffea12f0 remove whitespace 2016-02-08 12:59:15 +01:00
Michael Schramm
3c8ed45c46 implement bitbucket webhooks
this fixes issue https://github.com/Block8/PHPCI/issues/1015 and adds support for the new bitbucket webhooks
2016-02-08 12:59:15 +01:00
Dan Cryer
2ddda7711e Update README.md 2016-02-08 11:44:35 +00:00
Dan Cryer
42bea5f876 Merge pull request #1154 from staudenmeir/update-german-translation
Update German translation.
2016-01-08 11:04:54 +00:00
Dan Cryer
d3af89a0c5 Merge pull request #1157 from Lechus/patch-2
Update Polish translation
2016-01-08 11:03:35 +00:00
Leszek
9af9e0ec35 Update Polish translation
Contribution Type: improvement
Primary Area: front-end

Description of change: Updated Polish translation.
2016-01-06 22:01:38 +00:00
Jonas Staudenmeir
7f43f94cc0 Updated German translation. 2016-01-06 00:24:42 +01:00
Jonas Staudenmeir
949bfc5376 Added default value in settings language select (current language). 2016-01-05 16:21:21 +01:00
Dan Cryer
992cf767a6 Merge pull request #1128 from Block8/Deployer_branch_update
Added branch and update_only parameters
2015-11-24 11:37:16 +00:00
Stephen Ball
6eeddc9dc6 Added branch and update_only parameters
Fixes #1127
2015-11-14 23:08:22 +00:00
Dan Cryer
6eb180ed5f Merge pull request #1124 from Block8/REBELinBLUE-patch-1
Fixes a issue with a double slash in the path meaning ignore doesn't …
2015-11-12 17:47:16 +00:00
Stephen Ball
148e4414fb Fixes a issue with a double slash in the path meaning ignore doesn't work 2015-11-12 17:42:00 +00:00
Dan Cryer
b24b01ec65 Fixing tests on install command 2015-11-07 12:47:39 +00:00
Dan Cryer
04c67dc1dd Update installer to support new worker functionality. Closes #1094 2015-11-07 12:25:01 +00:00
Dan Cryer
4b8d25c0f7 Adding rebuild queue option 2015-11-03 20:42:47 +00:00
Dan Cryer
9ea6f299c1 Fix for warnings chart, courtesy of @Henk8 closes #1098 2015-11-03 11:58:22 +00:00
Dan Cryer
7b345cbc49 Making the lines of code chart hide if no data available. Closes #1103 2015-11-03 11:51:45 +00:00
Dan Cryer
c95e226496 Allow more errors. Fixes #1104 2015-11-03 11:44:08 +00:00
Dan Cryer
7bfba0ee30 Fixing error duplication bug. Closes #1112 2015-11-03 11:42:28 +00:00
Stephen Ball
551bae312f Merge pull request #1111 from SQweb-team/img_optim
ImageOptim'ed assets/
2015-10-27 10:46:13 +00:00
Pierre Lavaux
d8be38ace0 ImageOptim'ed assets/ 2015-10-27 02:17:26 +01:00
Andreus Timm
1d9c610a25 Adding slash in path
Fix / for DIRECTORY_SEPARATOR

Closed #1109
2015-10-26 16:26:47 +01:00
Dan Cryer
943c8124ac Merge pull request #1091 from andreustimm/master
Improvement with rtrim
2015-10-15 14:49:54 +01:00
Dan Cryer
f33f0a4512 Fixes 2015-10-15 14:23:55 +01:00
Dan Cryer
dd1d3b3d6a Switching charts to use Chart.js instead of Google Charts 2015-10-15 12:34:58 +01:00
Dan Cryer
66cf713ebc Merge branch 'master' of github.com:Block8/PHPCI 2015-10-15 10:41:42 +01:00
Dan Cryer
8fed994921 Fixing login pages 2015-10-15 10:41:35 +01:00
Stephen Ball
cb346f6808 Merge pull request #1093 from REBELinBLUE/missing_composer_dependencies
Added missing dependencies to require
2015-10-15 10:30:33 +01:00
Stephen Ball
78690f32c2 Merge branch 'master' into missing_composer_dependencies 2015-10-15 10:26:35 +01:00
Dan Cryer
f904cab599 Fixing PHPMD error 2015-10-15 10:26:13 +01:00
Stephen Ball
19a7ab995c Merge branch 'master' into missing_composer_dependencies 2015-10-15 10:21:00 +01:00
Dan Cryer
38b024833f Fixing PHPCI errors 2015-10-15 10:17:22 +01:00
Stephen Ball
7c03c77870 Added missing dependencies to require 2015-10-15 10:11:21 +01:00
Dan Cryer
7f823b37cf Big update: New way of storing build errors, updated UI, AdminLTE 2, fixes, etc. 2015-10-15 10:07:54 +01:00
Andreus Timm
d49cecf40a Improvement with rtrim 2015-10-14 14:24:18 -03:00
Dan Cryer
9c7a1c7907 Merge pull request #1085 from corpsee/feature-ru-update-2
Updated russian translation for Summary plugin section
2015-10-14 09:24:55 +01:00
Stephen Ball
82ffbfea5c Merge pull request #1086 from Block8/REBELinBLUE-patch-1
Adding missing closing bracket
2015-10-13 22:23:32 +01:00
Stephen Ball
0f84cc6334 Adding missing closing bracket 2015-10-13 22:02:05 +01:00
corpsee
914067c15a Updated russian translation for Summary plugin section 2015-10-13 22:33:07 +06:00
Dan Cryer
8f3cdd0fb7 Merge pull request #1083 from REBELinBLUE/build_broken_failed
Added 2 new build stages
2015-10-13 16:39:26 +01:00
Dan Cryer
9c2cdbe3d1 Merge pull request #1032 from EmmanuelVella/php-cs-fixer
Update PhpCsFixer plugin
2015-10-13 16:36:21 +01:00
Dan Cryer
a821072d27 Merge pull request #1082 from REBELinBLUE/hide_slack_attachment
Add the ability to hide the slack attachment
2015-10-13 16:36:12 +01:00
Stephen Ball
8f8009c30c Fixed parse error..... Apparently I shouldn't be allowed near a computer today 2015-10-13 16:35:27 +01:00
Dan Cryer
2f34c47656 Merge pull request #1076 from corpsee/feature-ru-update
Updated russian translation
2015-10-13 16:33:42 +01:00
Stephen Ball
3f71695013 Clean up PHPCS issue 2015-10-13 16:31:17 +01:00
corpsee
94b79860c5 Merge branch 'master' into feature-ru-update
Conflicts:
	PHPCI/Languages/lang.ru.php
2015-10-13 21:30:54 +06:00
Stephen Ball
4a5b1afb47 Clean up PHPCS issue 2015-10-13 16:28:37 +01:00
Stephen Ball
8e0122f6a0 Added a fixed and broken stage 2015-10-13 16:22:39 +01:00
Stephen Ball
2d667d477d Fix PHPCS error 2015-10-13 16:08:44 +01:00
Stephen Ball
d2ab7b300d Add the ability to hide the slack attachment 2015-10-13 16:04:16 +01:00
Emmanuel Vella
6e5ede8073 Update PhpCsFixer plugin 2015-10-13 16:41:41 +02:00
Dan Cryer
6aea2bdb88 Merge pull request #1077 from GeneaLabs/feature/fix-arrangement-of-components
Fix Arrangement of Build Result Components
2015-10-13 15:33:33 +01:00
Dan Cryer
3aeef0650d Merge pull request #1078 from GeneaLabs/feature/fix-bootstrap-datepicker-locale
Fix Datepicker Locale File Include
2015-10-13 15:31:23 +01:00
Dan Cryer
5216270416 Merge pull request #1070 from cooperaj/master
Incorrect docblock
2015-10-13 15:26:57 +01:00
Dan Cryer
ad27ca9037 Merge pull request #1025 from delormejonathan/patch-1
Fixed a css issue with Block8 logo
2015-10-13 15:14:59 +01:00
Dan Cryer
1385379f1a Merge pull request #1021 from mrobinsonuk/cleanup/spelling-corrections
Corrected a number of minor spelling errors.
2015-10-13 15:14:44 +01:00
Dan Cryer
10720656f9 Merge pull request #1019 from detain/mysql_plugin_host_support
Contribution Type: improvement / new feature
2015-10-13 15:13:55 +01:00
Dan Cryer
0fbae60f52 Merge pull request #998 from teemule11/bugfix-pdepend-1
Pdepend Module: make check for Pdepend output dir more robust.
2015-10-13 15:04:42 +01:00
Dan Cryer
3d21ad24e9 Merge pull request #993 from corpsee/feature-phpcpd-fix
Fixed GithubBuild::getDiffLineNumber method for correct phpcpd work
2015-10-13 15:04:13 +01:00
Dan Cryer
d8cbe68705 Merge pull request #992 from corpsee/feature-phpunit-fixes
Fixed TapParser parse fail
2015-10-13 15:03:09 +01:00
Dan Cryer
6591ada85b Merge pull request #972 from memnuniyetsizim/master
Fixes default value for PHP Cs Fixer ( #833 )
2015-10-13 14:45:20 +01:00
Dan Cryer
bee77a25c8 Merge pull request #942 from corpsee/feature-archived-fix
Separated strings for 'archived'/'archived_menu'
2015-10-13 14:33:07 +01:00
Dan Cryer
351b24c91d Merge pull request #939 from drydenmaker/master
adding windows batch file for console
2015-10-13 14:32:12 +01:00
Dan Cryer
c49821adf1 Merge pull request #1080 from GeneaLabs/feature/add-logo-icon-images
Add Logo Icons
2015-10-13 14:18:19 +01:00
Dan Cryer
ed35dee322 Merge branch 'master' of github.com:Block8/PHPCI 2015-10-13 10:00:17 +01:00
Dan Cryer
156c635af5 Updating deps 2015-10-13 09:59:48 +01:00
Mike Bronner
a504789bd4 Merge branch 'master' into feature/add-logo-icon-images 2015-10-12 12:26:42 -07:00
Mike Bronner
dc6e4ed6b4 Add logo icons 2015-10-12 12:23:55 -07:00
Dan Cryer
c9f6946189 Merge pull request #1079 from GeneaLabs/feature/fix-missing-sprintf-map-file
Add Missing sprintf.min.map File
2015-10-12 16:06:34 +01:00
Mike Bronner
21b59ab4d4 Add missing sprintf.min.map file 2015-10-10 10:07:31 -07:00
Mike Bronner
c474a5c73a Check if datepicker locale file exists 2015-10-10 09:54:01 -07:00
Mike Bronner
8845fe63d9 Move id attribute to outer div to hide the result component completely
This will allow the other non-hidden components to properly reflow on the page.
2015-10-10 09:22:53 -07:00
corpsee
c9541d630b Updated russian translation 2015-10-10 18:16:07 +06:00
Dan Cryer
97216b0f41 Fixing login stuff 2015-10-09 11:10:14 +01:00
Dan Cryer
725ce70ae9 Fixing ordering on projects for @REBELinBLUE 2015-10-09 10:58:18 +01:00
Dan Cryer
edcff1030d Merge pull request #1075 from Block8/dc/phpci-fixes
Fixing PHPCI Errors
2015-10-09 10:40:12 +01:00
Dan Cryer
fbc3da59dd Fix 2015-10-09 10:37:50 +01:00
Dan Cryer
f747371c6d Fixing tests #2 2015-10-09 10:29:21 +01:00
Dan Cryer
07b92fecf3 Fixing tests 2015-10-09 10:14:54 +01:00
Dan Cryer
e61ce203a3 Docblocks 2015-10-09 09:38:55 +01:00
Dan Cryer
a008358056 Cleaning up BuildWorker #1 2015-10-09 09:27:45 +01:00
Dan Cryer
68249d2f5d Cleanup of new executor changes. 2015-10-09 09:16:05 +01:00
Dan Cryer
4375c524a9 PHPMD Fix 2015-10-08 20:27:40 +01:00
Dan Cryer
af90b94a3c PHPMD Fixes 2015-10-08 20:24:32 +01:00
Dan Cryer
2f859be369 PHPCS Fix 2015-10-08 20:22:43 +01:00
Dan Cryer
370eebd227 Fixing PHPCS error in HomeController 2015-10-08 20:20:42 +01:00
Dan Cryer
ada9e33204 Restoring to login by email. 2015-10-08 20:15:20 +01:00
Dan Cryer
4e0325e887 Missing build bugfix. 2015-10-08 17:30:34 +01:00
Dan Cryer
e871df80c2 Adding the ability to run branch-specific stages 2015-10-08 16:54:01 +01:00
Dan Cryer
a7473eb250 Updating deps. 2015-10-08 16:53:39 +01:00
Dan Cryer
7ad1d317cd Updating builds to try database config first, and fall back to file config. 2015-10-08 16:52:44 +01:00
Dan Cryer
31165a6bfa General model / store cleanup 2015-10-08 16:52:19 +01:00
Dan Cryer
74d313862c Adding project groups 2015-10-08 16:33:01 +01:00
Dan Cryer
e186874eba Removing the ability to install 'plugins' via the web interface. 2015-10-08 16:29:29 +01:00
Dan Cryer
cf415a7c78 Merge branch 'master' of github.com:Block8/PHPCI 2015-10-08 09:49:20 +01:00
Dan Cryer
2380d2e472 Build worker should delete jobs in its queue that are not recognised. 2015-10-08 09:49:16 +01:00
Dan Cryer
69cd4789be Merge pull request #1073 from REBELinBLUE/add_commit_message_to_deploy_reason
Added the commit message to the BuildInterpolator
2015-10-07 15:08:52 +01:00
Stephen Ball
6f0945fb97 Added the commit message to the BuildInterpolator so it can be used as the default reason passed to deployer 2015-10-07 15:06:07 +01:00
Dan Cryer
857990b776 Update README.md 2015-10-06 19:10:11 +01:00
Dan Cryer
3f8bb1acc4 Merge pull request #1072 from REBELinBLUE/master
Update README.md
2015-10-06 19:09:00 +01:00
Stephen Ball
801a4f7309 Update README.md 2015-10-06 17:28:44 +01:00
Dan Cryer
06e3235960 Add "source" and "url" parameters to Deployer plugin. 2015-10-06 14:16:48 +01:00
Dan Cryer
de6eb62367 Deployer plugin 2015-10-06 12:00:58 +01:00
Dan Cryer
7e2fee9504 Verify that jobs actually come from PHPCI 2015-10-06 11:38:09 +01:00
Adam Cooper
668ee67617 Incorrect docblock
The "optionally" is not true, the config file is now required.
2015-10-06 10:53:23 +01:00
Dan Cryer
de1c058f83 Merge pull request #1067 from Block8/dc/workers
Adding support for a beanstalkd-based queue
2015-10-05 15:27:18 +01:00
Dan Cryer
90b4de453e Moving to new .phpci.yml filename 2015-10-05 15:25:45 +01:00
Dan Cryer
5062a58ba1 Merge pull request #1066 from REBELinBLUE/hide_yml_file
Adding support to check for .phpci.yml so the file can be 'hidden'
2015-10-05 15:23:20 +01:00
Dan Cryer
9dd6edc24a Fix 2015-10-05 15:07:25 +01:00
Dan Cryer
11445304d6 Making PHPMD happy. 2015-10-05 14:48:17 +01:00
Dan Cryer
771bee0aa3 Fixings 2015-10-05 14:41:13 +01:00
Dan Cryer
2858ce506a Merge branch 'master' into dc/workers 2015-10-05 14:17:23 +01:00
Dan Cryer
ea1157066e Fixing new PHPCS errors. 2015-10-05 14:17:13 +01:00
Dan Cryer
b2ed9f102b Docblock fixes 2015-10-05 14:11:43 +01:00
Dan Cryer
183d7d9e50 Making duplicate builds work again. 2015-10-05 14:07:48 +01:00
Dan Cryer
06204ef171 Adding support for beanstalkd-based workers. 2015-10-05 12:13:22 +01:00
Stephen Ball
647c0eb09c Update Build.php
Changed the order the files are loaded in
2015-10-05 10:17:04 +01:00
Dan Cryer
6c33417839 More composer.json cleanup 2015-10-05 09:57:50 +01:00
Dan Cryer
c0b2ed0df0 Merge branch 'master' of github.com:Block8/PHPCI 2015-10-05 09:56:42 +01:00
Dan Cryer
fa979f6a02 Fixing composer.json 2015-10-05 09:56:36 +01:00
Stephen Ball
9ee27bad37 Adding support to check for .phpci.yml so the file can be 'hidden' as with other CI systems such as Travis, StyleCI and Codeclimate 2015-10-04 17:38:03 +01:00
Dan Cryer
b0482b191d Merge pull request #872 from REBELinBLUE/additional_interpolate
Additional interpolate
2015-10-02 22:14:14 +01:00
Dan Cryer
58a97e5a97 Merge pull request #869 from REBELinBLUE/technical_debt_cleanup
Cleanup technical debt
2015-10-02 22:12:50 +01:00
Dan Cryer
b171a10759 Fixing Codeception plugin PHPCS errors. 2015-10-02 21:53:39 +01:00
Dan Cryer
aaf93ddd7d Updating PHPCI to send more detailed commit statuses, for @REBELinBLUE. 2015-10-02 21:35:53 +01:00
Dariusz Ruminski
dc6e63a7e7 PHP CS Fixer - fix name
Closed #1054
2015-10-02 13:54:25 +02:00
Mehmet Ali Ergut
f55b336900 Fixes default value for PHP Cs Fixer ( #833 ) 2015-08-24 21:24:37 +03:00
Steve Robbins
92ee90998a Fixes notice in github builds
Closed #996
2015-08-13 14:28:08 +02:00
DELORME Jonathan
8a3d7605d5 Fixed a css issue with Block8 logo
In Chrome (Android L), when you focus an input, the Block8 logo overrides the view.
See : http://s10.postimg.org/d5ffp7dwp/2015_06_30_07_57_03.png
2015-06-30 11:43:25 +02:00
Mark Robinson
d2c512d94b Corrected a number of minor spelling errors.
Originally observed that "successful" was misspelled "succesfull" on the build view page. While correcting that, a number of other spelling errors were also corrected.
2015-06-25 15:21:23 +01:00
Joe Huss
9efd8e06dc Contribution Type: improvement / new feature
Primary Area: plugins

Description of change: Implemented using the same changes mentioned here: https://www.phptesting.org/forums/topic/5-mysql-plugin-setup/
Allows you to specify a hostname for your mysql connection.  Not my original code just patching it into the current source and made a branchs/push for it.

(i.e. Created a new plugin named ToolX, used library X to implement, see link)
2015-06-25 07:48:26 -04:00
Adirelle
d698b11673 Fixes "Undefined index: login_token".
Fixes https://github.com/Block8/PHPCI/issues/994.
2015-05-30 21:16:31 +02:00
mulleto
2672908225 Make check for Pdepend output dir more robust. Before this fix, the pdepend module would check for "is_writable", which returns false even when the directory does not exist. I suggest to a) attempt to crete the dir if it does not exist, and b) make the error message more explanatory in case the dir cannot be created or is not accessible. 2015-05-29 08:26:47 +02:00
Aleix Canal
3a6008db53 Bugfix: Default codeception xml path "tests/_output/" but now 100% configurable.
Closed #991
2015-05-28 12:18:24 +02:00
tankist
9ace15a55f Fix for #988
Added class::method fallback for feature entry to prevent empty report for Codeception plugin

Closed #989
2015-05-28 12:10:14 +02:00
mulleto
3784cc8ea9 Adding an option for Plugin::Composer to pass the no_dev option. If set to true, it will execute composer with the --no-dev option, which usually suffices for testing in most projects. Default is set to false.
Closed #987
2015-05-28 12:08:30 +02:00
mulleto
d34818f029 Added missing strings in german translation
Closed #982
2015-05-28 12:05:58 +02:00
Stephen Ball
1eb5248319 Removed allowed_warnings as it is not used
Calling setOptions

Closed #975
2015-05-28 12:03:23 +02:00
Adirelle
3734d3bc9a Don't use an if and two return statements when one return is sufficient.
Don't use two if when one else does the same job.
Don't use convoluted calculations when one return statement is sufficient.
Don't call a method that doesn't exist.
Don't write a try-catch block that does nothing.
Do send exceptions when a abnormal situation occurs.

Closed #962
2015-05-28 12:00:43 +02:00
Adirelle
b7d9af1e72 Update sensiolabs/ansi-to-html to fix the log background.
Closed #961
2015-05-28 11:59:17 +02:00
corpsee
5c6dd81a9a Extended shields.io status badge support
Closed #958
2015-05-28 11:50:57 +02:00
Petr Cervenka
7ff080971e Adding Flowdock integration
Closed #954
2015-05-28 11:47:57 +02:00
Adirelle
ad29ba4cfd Track and display the build progression, for each stages and plugins.
Translations for the build summary.

Closed #944
2015-05-28 11:39:35 +02:00
Adirelle
8549ba30cf PHPUnit display: escape HTML code in test messages.
PHPUnit display: display data produced before the TapParser refactoring.

Closed #937
2015-05-28 11:14:20 +02:00
Adirelle
4edefee761 Reworked the DaemonCommand.
* Accepts options for PID and log file.
* Uses posix_kill whenever available.
* Checks that the daemon actually started or stopped.
* Try to terminate then kill the daemon.
* Uses the logger or output instead of "echo".

Added a ProcessControl interface and implementations.

Closed #908
2015-05-28 10:55:59 +02:00
Vaidas Zilionis
15b6917f68 Allow see project build status in cctray xml format
Refactoring is done for BuildStatus information.
- Fixed all phpcs, phpmd errors
- Added test for my code (hurray 100 tests already :D)

Closed #705
2015-05-28 10:50:29 +02:00
corpsee
853107027b Fixed GithubBuild::getDiffLineNumber method for correct phpcpd work 2015-05-24 14:20:51 +06:00
corpsee
3f02e63c92 Fixed TapParser::parseLine Cyclomatic Complexity 2015-05-24 12:43:43 +06:00
corpsee
c20ee0533b Fixed TapParser::parseLine Cyclomatic Complexity 2015-05-24 12:39:27 +06:00
corpsee
1c864cebed Fixed TapParser parse fail with valid data + added tests 2015-05-23 15:40:49 +06:00
Victor
0887bd4bc4 Fix for strict standards issue in RebuildCommand.php
[ErrorException]
Runtime Notice: Only variables should be passed by reference in /var/www/phpci/PHPCI/Command/RebuildCommand.php line 78
2015-05-15 16:37:45 +02:00
Adirelle
8bf1d09afd Black-on-white color theme for the build log. 2015-04-28 14:14:25 +02:00
Adirelle
5cd29ca7fc Allow to specify a mail template ('short' or 'long') in phpci.yml.
Email plugin: checks if the custom template exists before trying to use it.

Closed #933
2015-04-28 14:09:23 +02:00
Adirelle
590ab5fae5 Build::removeBuildDirectory: do not try to remove build directory of not-yet-persisted builds. 2015-04-25 21:25:50 +02:00
corpsee
21bf104587 Separated strings for 'archived'/'archived_menu' 2015-04-25 09:54:08 +06:00
alton.crossley
5133d85b99 adding windows batch file for console 2015-04-23 14:35:15 -06:00
Adam Cooper
3ca1667f12 Revert CSS file change made in error as part of the codeception update
Close #935
2015-04-23 17:01:44 +02:00
Adirelle
f46a8be648 LoggerConfig::getFor always returns the same instance of Logger for the same $name.
This avoid issues when push handlers/processors to that logger.
Use the Monolog\ErrorHandler to log errors and exceptions.
PHPCI/Logging/Handler becomes PHPCI/ErrorHandler.
And it only throws ErrorException for reported errors.
No need to initialize a second $loggerConfig in daemonise.

Close #892
2015-04-23 13:25:53 +02:00
Adirelle
5688d9c4c8 The manual build button creates build for the currently watched branch.
Close #927
2015-04-23 13:23:13 +02:00
Adam Cooper
408eb5b974 An attempt at making the codeception plugin a little more complete.
Codeception JS plugin and theme changes.
Improvements to the display. Extra total information plus some test file locations.

Close #588
2015-04-23 13:18:26 +02:00
Adirelle
209454c5f6 When starting a manual build, replace the "Manual" commit id with the HEAD hash.
Close #928
2015-04-23 13:13:29 +02:00
corpsee
634b246ed5 Fixed ru strings for create build command (https://github.com/Block8/PHPCI/pull/889)
Fixed ru strings for 'archived'

Close #932
2015-04-23 10:33:38 +02:00
corpsee
1b7f0bbb4b Improved login: now you can login using name or email
Close #873
2015-04-22 13:54:02 +02:00
Alexander Garzon
c0568d3a4b Update lang.es.php
Missing translation for "archived"

Close #931
2015-04-22 12:26:51 +02:00
Adam Henley
f7ca64bf6d SMTP Password not masked PR #921
Signed-off-by: Adam Henley <adamazing@gmail.com>

Close #923
2015-04-22 12:26:06 +02:00
rm3nchaca
81fbc6a5a0 fix file link in plugins
Running builds leave a file link with an error like "http://gitlab.example.com/root/project/blob/master/index.php#L6" but it is pointing to the actual file, not the file with a bug, example "97f0a6453d/index.php (L6)"

Close #915
2015-04-22 12:24:29 +02:00
Adirelle
290c34a27d Updated the php_codesniffer required version and added a default phpcs.xml.
Added a PHPMD configuration file.
Updated phpci.yml to use the configuration files.

Close #913
2015-04-22 12:22:28 +02:00
Adirelle
f25b1d25dc Let CommandExecutor::findBinary throw an exception when the binary is missing.
Close #910
2015-04-22 12:19:05 +02:00
Lewis Wright
7f46b650dc Added check for invalid key
Close #895
2015-04-22 12:06:26 +02:00
Jérémy DECOOL
9261f24d25 Command to create a new build
Close #889
2015-04-22 12:00:38 +02:00
Angel Koilov
77ba61c8bc add pingback for IRC plugin
Close #886
2015-04-22 11:58:43 +02:00
Adirelle
452a5ba97c Enforce "en" lang in tests.
Some tests compare the result to english strings.

Do not test UnixCommandExecutor on Windows.

PharTest: explain why PHAR writing test are skipped.

InstallCommandTest: mock checkRequirements to allow the tests to run.

Run php_parallel_lint before all other tests.

Close #846
2015-04-22 11:49:26 +02:00
Adirelle
9590336c49 Added a new configuration variable, PHPCI_BUILD_ROOT_DI.
This variable allows to change where the build happens.
It defaults to PHPCI_DIR.'PHPCI/build/'.

Moved build path calculate and build removal into the Build class.

Also remove the build directory when deleting the build.

Close #834
2015-04-22 11:44:54 +02:00
Martin Sadovy
60131ae7b6 Github: Support pull private repository from pull request
Closes #832
2015-04-22 11:41:33 +02:00
zviryatko
067a60983f Fix archive link. 2015-04-22 11:37:38 +02:00
Marco Vito Moscaritolo
3626eabc2e Merge pull request #871 from REBELinBLUE/fix_wipe_error
Preventing the wipe plugin failing due to an undefined variable
2015-04-16 18:39:30 +02:00
Marco Vito Moscaritolo
0b0589557f Merge pull request #845 from MarkMaldaba/fix_paths_with_spaces
Fixed the install script, which bails-out with an error if the PHPCI path contains spaces.
2015-04-12 21:17:12 +02:00
Adirelle
f3c1a98cf1 Detailed webhook responses.
Fixed docblocks.

Reworked WebhookController to enforce Json responses in ::handleAction.

Check the project type match the webhook.

When creating several builds, do not stop on first error.

Try to create every builds and report 'ok' if at least one succeeds.

CS fix.

Fixed Uses.

Fixed the types accepted by the git webhook.

Added some really basic test.
2015-04-09 13:09:03 +02:00
zviryatko
731fd65453 Change xmpp config and message files directory 2015-04-09 12:08:27 +02:00
Angel Koilov
524341a50b remove unnecessary code 2015-04-09 12:06:32 +02:00
Tobias van Beek
01911f11aa Add the --recursive parameter to the git clone to get the submodules 2015-04-09 12:03:08 +02:00
Adirelle
9d4116e3c9 Reworked TapParser to be compliant and more robust.
Added another test case from #571.

Updated the output of TapParser::processTestLine.

Broke TapParser::parse down in simpler methods.

TapParser: ignore leading garbage and properly complain on missing TAP log.

TapParser: detect and report duplicated TAP log.

TapParser: got rid of the "test" and "suite" values.

They are only available with PHPUnit.

TapParser: append the message from yaml diagnostic to existing message.

Reworked the dispaly of test results.

PHPUnit plugin: pretty print test data.
2015-04-09 12:00:10 +02:00
Nicolplaás
2c43cd1cac Add spanish laguage support 2015-04-09 11:57:46 +02:00
Adirelle
d804438a87 Use sensiolabs/ansi-to-html to parse the build logs.
Added an AnsiConverter helper.

Use the AnsiConverter in the email and page templates that display the build log.

Use a dedicated stylesheet for the ANSI converter.

It can be customized.
It can be inlined in the notifications.

Do not use ProphecyTestCase when not needed.
2015-04-09 11:54:57 +02:00
Marco Vito Moscaritolo
039324c056 Merge pull request #842 from corpsee/feature-ui-improvements
UI improvements
2015-03-25 12:51:10 +01:00
corpsee
3a867eb9d5 Fixed 'start' string for ru lang
Fixed 'from' and 'to' strings for ru lang
2015-03-18 13:10:21 +01:00
LAHAXE Arnaud
d8df6cab4a Fix french typo mistake 2015-03-18 12:26:27 +01:00
Adirelle
ea3b0c219a Code style fixed. 2015-03-18 12:21:06 +01:00
Adirelle
f29ff197c6 Generate an new session identifier on successful login to prevent session fixation attacks. 2015-03-18 12:21:06 +01:00
Adirelle
3467e77e74 Use a CSRF token on the login form to prevent CSRF attacks. 2015-03-18 12:21:05 +01:00
Mark Clements
1dd1af2443 Switching tabs to spaces as per style guide.
No functional changes.
2015-03-18 09:47:47 +00:00
Marco Vito Moscaritolo
9d6a65b415 Merge pull request #844 from MarkMaldaba/fix_space_after_colon
Fixed an inconsistency in the way the prompts in the install scripts were being output.
2015-03-18 09:58:21 +01:00
Stephen Ball
fdaaa1ede4 Parsing variables in the code coverage output directory for PHPUnit 2015-03-16 11:10:03 +00:00
Stephen Ball
52b2f87df2 Parsing variables in the Wipe plugin 2015-03-16 11:09:45 +00:00
Stephen Ball
9ad0e90fa1 Preventing the plugin failing due to an undefined variable 2015-03-16 11:05:33 +00:00
Stephen Ball
70f0d2516f Removed log output so that it matches the other plugins which don't pollute the build log, and to prevent issues with the log output not being escaped 2015-03-13 13:31:38 +00:00
Dan Cryer
ecc92b5f3e Fixing pull request builds. 2015-03-11 07:48:22 +00:00
Dan Cryer
5f2de9a679 Update to only build the latest commit from a Github pull request webhook. 2015-03-10 20:20:54 +00:00
vsguts
a188afb0da Fixing symlink removal.
Closes #854
2015-03-10 19:11:43 +00:00
Gustavo Novaro
33fc50a0b5 Remove blank style tag in header
Remove the tag <style> the header because it was not used

Closes #863
2015-03-10 19:04:05 +00:00
corpsee
e75ffe0b76 Fixed 'date' nl lang string 2015-03-10 15:44:33 +06:00
Igor Timoshenko
4e8dc7c87b Fixed typos in Ukrainian language 2015-03-10 10:15:50 +01:00
Adirelle
00b88630fb Display a green border in passing build notifications. 2015-03-10 09:53:36 +01:00
corpsee
942127ffe6 Added default value in profile language select (current language) 2015-03-10 09:51:22 +01:00
Leszek
a7b40ce176 archived 2015-03-10 09:29:37 +01:00
Marco Vito Moscaritolo
3c83c01520 Merge pull request #857 from thomasfrei/remove-short-array-syntax
Remove short array syntax to keep backwards compatibility with php5.3
2015-03-07 20:00:37 +01:00
Thomas Frei
542d2a3545 Remove short array syntax to keep backwards compatibility with php5.3 2015-03-06 17:44:22 +01:00
James Inman
945524c520 Merge pull request #851 from njovin/master
Fix #840: Technical Debt plugin not storing data/displaying results.
2015-03-05 08:39:17 +00:00
Nathan Jovin
610d0991a8 Fix issue #840 Technical Debt not storing data nor displaying results in table 2015-03-05 00:12:42 -08:00
Mark Clements
8a96ec8551 Fixed the install script, which bails-out with an error if the PHPCI path contains spaces.
This occurs commonly on Windows, but from my reading of the code it would also be a problem on other platforms if spaces were present (though this is less likely, due to different naming conventions).

It has been fixed by using escapeshellarg() on both of the paths used in the command.

Fixes #698, which I've just noticed has a similar solution suggested in one of the comments, but was closed without anyone actually implementing it.
2015-03-02 22:49:22 +00:00
Mark Clements
d2e6182a2f Fixed an inconsistency in the way the prompts in the install scripts were being output.
All the DB/host fields have a space after the colon, which is much better as it means the text you type is slightly separated from the prompt.  However, the admin user fields didn't include this space which was inconsistent and made the install script a little less professional.

I have therefore added the missing space for the prompts which didn't have it in the English language file, and have also updated all other language files to also use this format.  Most of them followed the same inconsistency as the English version, though some were consistent but without a space, and some which were differently inconsistent (both internally, and between languages).
2015-03-02 22:31:01 +00:00
corpsee
f97089529c Fixed 'date' it lang string 2015-03-02 08:18:40 +06:00
corpsee
000aff9121 Code style fix 2015-02-28 23:51:04 +06:00
corpsee
9f53471186 Added Date column for builds table in project page. 2015-02-28 23:38:56 +06:00
corpsee
da9be4930d Added total builds count to index and project page 2015-02-28 23:13:02 +06:00
Dan Cryer
b8983b23a3 Fixing final PHPCS error. 2015-02-26 08:48:40 +00:00
Dan Cryer
86b9c05f98 Fixing PHPCS errors. 2015-02-26 08:45:42 +00:00
Dan Cryer
ab4396e00d Hopefully fixing a bug where reporting errors back to Github causes an infinite loop. 2015-02-26 08:31:58 +00:00
Dan Cryer
bfc56a753d Updating dependencies. 2015-02-26 08:08:45 +00:00
Dan Cryer
4a84aad327 Updating basic Dockerfile. 2015-02-26 08:08:12 +00:00
Dan Cryer
8ab098821b Updating Settings Controller to use the configured config file, rather than assuming config.yml 2015-02-25 14:18:05 +00:00
Dan Cryer
e423c73c4e Fixing comparison where commit ID is Manual.
Fixes #823
2015-02-25 14:13:31 +00:00
Dan Cryer
18ff21174e Fixing dates: Stop all dates from appearing as the current date/time. Fixes #820 2015-02-25 10:33:11 +00:00
Dan Cryer
db90f2ea11 Updating the UpdateCommand to check for a config key rather than a specific file. 2015-02-25 09:36:50 +00:00
zviryatko
f4a0804100 Fix username style in user panel block. 2015-02-24 10:01:36 +02:00
Dan Cryer
fba6f2372f Updating changelog. 2015-02-23 19:45:33 +00:00
Dan Cryer
1f0cd49142 Adding changelog. 2015-02-23 19:40:31 +00:00
Alex Davyskiba
1466ad06ef Allow projects to be archived.
Closes #771
2015-02-23 19:37:11 +00:00
Dan Cryer
e98647bd97 Add support for Mercurial SSH-based clones. 2015-02-23 16:17:46 +00:00
nonlux
893deada33 Set the CommandExecutor buildPath property when a build is created.
Closes #556
2015-02-23 14:42:04 +00:00
Adirelle
4905679298 Email plugin: use "default_mailto_address" as a fallback only.
Closes #730
2015-02-23 14:16:35 +00:00
Matthew Leffler
ea3109be67 Convert tapString to UTF-8, allowing UTF8 output from PHPUnit, etc.
Closes #738
2015-02-23 14:15:11 +00:00
Daniel Seif
610a0e57ef Fixed settings handling for symlink creation in local build
Closes #766
2015-02-23 14:07:42 +00:00
Àlex Corretgé
601318b97b Fix the problem when executing Phing out of the build dir.
Closes #778
Closes #748
2015-02-23 13:53:38 +00:00
Lee Willis
6420119f1a Make SSH key generation more robust.
Do not try and predict whether we will be able to create a key. Instead
try and create one and capture failure if it happens.

Closes #803
2015-02-23 13:49:29 +00:00
corpsee
4ffeec7767 Updated lang.ru file
Closes #807
2015-02-23 13:47:16 +00:00
Leszek
1a5ecb97b1 Update lang.pl.php
Closes #810
2015-02-23 13:46:41 +00:00
Dan Cryer
dd58dd682f Adding support for commenting on Github diffs. 2015-02-23 13:40:34 +00:00
Dan Cryer
071e36a4e9 Slight cleanup to please PHPMD. 2015-02-20 14:24:37 +00:00
Dzhilkibaev Nadir
35a6d1f577 Add SVN support to PHPCI.
Closes #759
2015-02-20 14:14:36 +00:00
James Inman
9133c544d5 Adding output support to Behat plugin. 2015-02-20 13:38:42 +00:00
Dan Cryer
1f4fb58014 Update session cookie to last 12 hours. 2015-02-20 13:11:31 +00:00
Dan Cryer
aca8155a43 Merge branch 'master' of github.com:Block8/PHPCI 2015-02-20 12:46:50 +00:00
Dan Cryer
69d05d6da8 Setup plugin failure should cease execution. Fixes #797 2015-02-20 12:46:43 +00:00
James Inman
6c8df9a8ce Fix missing return statement in Technical Debt. 2015-02-19 13:37:35 +00:00
Jon Gotlin
bf6ac530a6 Create admin command cleanup 2015-02-19 07:31:00 +01:00
James Inman
5bb68507d8 Fixing Technical Debt error message. 2015-02-18 14:56:04 +00:00
James Inman
d6f72b0b7e PHPCS/PHPMD fixes for Technical Debt plugin. 2015-02-18 14:44:04 +00:00
James Inman
2a5ac8ccbc PHPCS/DocBlock fixes to Technical Debt plugin. 2015-02-18 14:15:59 +00:00
James Inman
8f5d855193 Adding Technical Debt plugin. 2015-02-18 14:07:26 +00:00
Daniel Wolkenhauer
33f2ec172d Fatal error: Call to undefined method PHPCI\Helper\Email::setIsHtml() in ../Email.php on line 107 2015-02-16 18:51:05 +01:00
Tobias van Beek
7c4a926166 Render the public key on the project edit page. CLose #722 done by @leewillis77 2015-02-16 15:00:37 +01:00
Marco Vito Moscaritolo
2255767dc1 Fixed test that use genreated mail to see information. 2015-02-16 14:55:40 +01:00
Marco Vito Moscaritolo
cbd98b6b46 Fixed typo. 2015-02-16 14:55:20 +01:00
Marco Vito Moscaritolo
885f947da8 Update loader config for test. 2015-02-16 14:55:17 +01:00
Marco Vito Moscaritolo
5b754fe07c Fixed config path for tests. 2015-02-16 14:54:41 +01:00
Marco Vito Moscaritolo
74b0513dd5 Remove unused code. 2015-02-16 14:54:41 +01:00
Marco Vito Moscaritolo
2d194de6d0 Fixed code in CC mails. 2015-02-16 14:54:41 +01:00
Marco Vito Moscaritolo
2aff5ab896 Fixed CS. 2015-02-16 14:54:41 +01:00
Marco Vito Moscaritolo
64bd64e07b Added more test case to validate subject, body, status code and mails on messages. 2015-02-16 14:54:38 +01:00
Marco Vito Moscaritolo
f16395e45b Use more consistent project name loader to compose mail. 2015-02-16 14:52:48 +01:00
Marco Vito Moscaritolo
33ca150efb Added project mocking to allow using mail template on successfull build. 2015-02-16 14:52:45 +01:00
Marco Vito Moscaritolo
501ca58729 Remove unrequire parameters on constructur and fixed error on phpdoc. 2015-02-16 14:51:18 +01:00
Marco Vito Moscaritolo
f192185e26 Refactor on mail sending to use Email helper and specific tempalte for successfull build. 2015-02-16 14:51:14 +01:00
Dan Cryer
4d142b61b6 Fixing other tests 2015-02-16 11:58:15 +00:00
Dan Cryer
0fc18503b2 Fixing install command tests. 2015-02-16 11:54:52 +00:00
Dan Cryer
691f2423f7 Fixing base store docblocks. 2015-02-16 11:20:18 +00:00
Igor Timoshenko
55b10948ef Added Codeception UI plugin 2015-02-16 11:47:07 +01:00
Dan Cryer
44c489dd22 Lots of cleanup. 2015-02-12 14:15:19 +00:00
Dan Cryer
c20ca7c8ff Lots of cleanup. 2015-02-12 14:11:58 +00:00
Dan Cryer
5ca9d4606e Fixes 2015-02-12 13:44:16 +00:00
Dan Cryer
6576974584 Cleaning up permissions on a few files. 2015-02-12 13:42:07 +00:00
Dan Cryer
12f8d376bd Adding a default robots.txt file. 2015-02-12 13:34:15 +00:00
Dan Cryer
dcbad55df8 Fixing FIXMEs 2015-02-12 13:30:32 +00:00
Dan Cryer
dea615bf26 Cleanup 2015-02-12 13:20:08 +00:00
Dan Cryer
320b0efa71 Fixing run-builds. 2015-02-12 13:17:50 +00:00
Dan Cryer
54ab93373d Fixing some bugs related to the last round of changes. 2015-02-12 13:17:42 +00:00
Dan Cryer
c986405861 Removing the now-unnecessary JSON.parse() call 2015-02-12 12:38:22 +00:00
Dan Cryer
489f71b8c2 Cleaning up unnecessary use of 'die' and 'exit' 2015-02-12 12:37:56 +00:00
Dan Cryer
36e3c622c8 Fixing missing use statements 2015-02-12 11:42:09 +00:00
Dan Cryer
e5cbccecb5 Getting rid of debug code 2015-02-12 11:41:58 +00:00
Dan Cryer
a4339fc1b6 Fixing two particularly dodgy queries in BuildStore. 2015-02-12 11:38:00 +00:00
Dan Cryer
d481140ea2 Fixing issues related to LIMIT in Base Store files. 2015-02-12 11:08:45 +00:00
Dan Cryer
9379da1393 Fixing SensioLabs Insight 'Critical' violations. 2015-02-12 10:39:41 +00:00
vigo5190
4d91bd15e7 Update Slack plugin to support the latest version of the library it relies on.
Closes #742
Closes #747
2015-02-09 12:14:30 +00:00
Lee Willis
4f73063fb7 Redesigned build summary for the dashboard to include recent builds.
Closes #783
Closes #708
2015-02-09 11:34:25 +00:00
Luca
935ffe4473 Some Italian language translation improvements.
Closes #784
2015-02-09 11:24:21 +00:00
Àlex Corretgé
f0d53638dc Allow modification of PHPCI variables without modifying versioned code.
Closes #786
Closes #781
2015-02-09 11:20:44 +00:00
Dan Cryer
4a1d62b44a Make build log and meta value columns use MEDIUMTEXT data type.
Closes #777
2015-02-09 11:18:34 +00:00
Daniel Seif
e531c80718 Reset the status of a duplicated build to avoid strict-mode errors.
When using SQL strict mode, mysql would complain that no default value of the status column is set. Setting the status to 0 before duplicating fixes this.

Closes #725
2015-02-03 12:21:04 +00:00
Adirelle
353c4cafdb Remove duplicates from the list of recipients in the email plugin.
Closes #731
2015-02-03 11:58:18 +00:00
Adirelle
0be37b9e85 Franch translation updates.
Closes #733
2015-02-03 11:56:45 +00:00
Adirelle
cd4ff6c4ea Localisation support for dates throughout the front-end using moment.js.
Closes #734
Closes #732
2015-02-03 11:53:12 +00:00
corpsee
5dced5f990 Fixing fatal error with $this->controller->layout in Application class.
Closes #736
2015-02-03 11:49:16 +00:00
corpsee
8d2c7045a4 Update the "Manual Build" string for the Russian translation.
Closes #737
2015-02-03 11:47:07 +00:00
Adam Cooper
c441e72d0a Move CSS into separate file, fix width of plugins that use tables and adds table-responsive class for smaller screens.
Closes #755
2015-02-03 11:38:16 +00:00
Sergey Linnik
96b1df55b0 Add query interpolation in PostgreSQL and SQLite plugin.
Closes #757
2015-02-03 11:33:55 +00:00
Dan Cryer
9351e889e7 Fixing failing test 2015-02-03 11:20:46 +00:00
Sam Mousa
3ade895e30 Fix parameters used to check if ssh-keygen exists to prevent an indefinite hang.
Closes #764
2015-02-03 11:15:52 +00:00
Dan Cryer
731cdcce7d Fixing missing docblock error. 2015-02-03 11:10:22 +00:00
Dan Cryer
872d37b1e6 Updating dependencies. 2015-02-03 10:54:50 +00:00
Dan Cryer
9aadcce4ca Fixing tests bootstrap 2015-02-03 10:43:44 +00:00
Sebastiaan van Stijn
9387bde61b Fix undefined variable '$config' in /Tests/bootstrap.php
When no config-file was found, no instance of Config
was created as well, resulting in:

PHP Notice:  Undefined variable: config in /Tests/bootstrap.php on line 41

This changes /Tests/bootstrap.php to always create an
instance of b8\Config(), even if no file was found.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Fixes #758
2015-02-03 10:30:51 +00:00
Alex Davyskiba
a233c4e26c Escape commit message from XSS and broken markup.
Closes #769
2015-02-03 10:27:36 +00:00
Lee Willis
25358cdfd5 Move legends to below chart to avoid word wrapping issues
Closes #773
2015-02-03 10:12:36 +00:00
Nikolas Hagelstein
3ddc723e1f German language translation improvements.
Closes #774
2015-02-03 10:11:29 +00:00
Dan Cryer
c5bdafeaa3 Fixing login is disabled checks 2015-01-22 09:17:58 +00:00
Dan Cryer
c54f22985c Merge branch 'master' of github.com:Block8/PHPCI 2015-01-13 12:38:48 +00:00
Dan Cryer
c93cdf7fe5 Fixing b8 logo and link on login screen. 2015-01-13 12:38:39 +00:00
Dan Cryer
e9e0ba8593 Merge pull request #722 from REBELinBLUE/slack-bug-fix
Fixing issue with status overwriting message
2014-12-23 15:41:45 +00:00
Stephen Ball
44f1445bbe Fixing issue with status overwriting message 2014-12-23 15:30:09 +00:00
1066 changed files with 175482 additions and 22720 deletions

41
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,41 @@
Contributions to PHPCI are very much encouraged, and we do our best to make it as welcoming and simple as possible.
### Before You Start
Before you start, please make sure that you are aware of, and agree to, the following conditions of contribution:
* By making a contribution to PHPCI, you accept that you are granting copyright ownership for that contribution to Block 8 Limited - the company responsible for PHPCI. In countries where copyright reassignment is not permitted, you grant Block 8 Limited a perpetual, non-exclusive licence to use your contribution in any way and for any purpose.
* By making a contribution to PHPCI, you accept that your code will be released under the open-source [BSD 2-Clause Licence](https://github.com/Block8/PHPCI/blob/master/LICENSE.md).
Block 8 are committed to PHPCI being a truly free and open source project, providing easy to use continuous integration and testing to as many developers as possible. We may, at our sole discretion, provide paid services based upon PHPCI - but PHPCI will always remain free (as in cost, and freedom) and open source.
### The Contribution Process
1. If you are thinking of making a large change or feature addition, [open an issue](/Block8/PHPCI/issues) titled "Intent to implement: <Your Feature>". Describe your idea in detail and discuss it with the community. It might be that someone already has a plan, could help you out, or your idea may simply not be suitable for the project at this time.
2. Fork the PHPCI project on Github
3. Add a feature or fix a bug - We recommend that you do this on a branch within your repository.
4. Create a pull request containing just the one change you want to contribute back to PHPCI. If you have more than one feature or bug fix, please create separate branches within your repository, and then submit a separate pull request for each one. Your pull request should use the template detailed below.
5. We'll then review your pull request and give any necessary feedback, this could be:
* Suggestions to improve your implementation
* Questions
* Issues/bugs related to the change
* Coding standards pointers
6. Once everyone is happy with the submission, we'll merge it back into PHPCI. Your change will then be included in the next project release.
### Not sure what to start with?
We maintain two labels within our issue tracker that may be of interest to new contributors:
* [The "Easy Fix" List](https://github.com/Block8/PHPCI/labels/flag:easy-fix)
* [The "Priority" List](https://github.com/Block8/PHPCI/labels/flag:priority)
### Coding Standards
We require that all contributions meet at least the following guidelines:
* PSR-1 & PSR-2 compliance for all code
* Doc-blocks for all classes and methods
* All files must contain the standard file-level docblock, including the copyright, license and link tags.
All pull requests will be checked against these standards. If you're modifying a file as part of your change which does not comply with the above, please make the necessary changes to bring it into compliance prior to submitting the pull request.
### Other Requirements
When you're adding new features or functionality, or you're updating existing functionality, please ensure that the relevant documentation is also either created or updated on the Wiki.

28
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,28 @@
Before submitting your issue, please make sure that you've checked all of the checkboxes below.
- [ ] You're running the [latest release](https://github.com/Block8/PHPCI/releases/latest) version of PHPCI.
- [ ] Ensure that you're running at least PHP 5.3.6, you can check this by running `php -v`
- [ ] You've run `composer install --no-dev -o` from the root of your installation.
- [ ] You have set up either the PHPCI [Worker](https://github.com/Block8/PHPCI/wiki/Run-Builds-Using-a-Worker), [Daemon](https://github.com/Block8/PHPCI/wiki/Run-Builds-Using-a-Daemon) or [Cron Job](https://github.com/Block8/PHPCI/wiki/Run-Builds-Using-Cron) to run builds.
To help us better understand your issue, please answer the following.
### Expected behaviour
*Please describe what you're expecting to see happen.*
### Actual behaviour
*Please describe what you're actually seeing happen.*
### Steps to reproduce
*If your issue requires any specific steps to reproduce, please outline them here.*
### Environment info
Operating System:
PHP Version:
MySQL Version:
### Logs or other output that would be helpful
(If logs are large, please upload as attachment).

23
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,23 @@
Contribution Type: bug fix | new plugin | new feature | refactor | cosmetic
Link to Intent to Implement:
Link to Bug:
This pull request affects the following areas:
* [ ] Front-End
* [ ] Builder
* [ ] Build Plugins
**In raising this pull request, I confirm the following (please check boxes):**
- [ ] I have read and understood the [contributing guidelines](/.github/CONTRIBUTING.md)?
- [ ] I have checked that another pull request for this purpose does not exist.
- [ ] I have considered, and confirmed that this submission will be valuable to others.
- [ ] I have created or updated the relevant documentation for this change on the PHPCI Wiki.
- [ ] Do the PHPCI tests pass?
Detailed description of change:

2
.gitignore vendored
View file

@ -15,3 +15,5 @@ PHPCI/Model/Migration.php
PHPCI/Model/Base/MigrationBase.php PHPCI/Model/Base/MigrationBase.php
PHPCI/Store/MigrationStore.php PHPCI/Store/MigrationStore.php
PHPCI/Store/Base/MigrationStoreBase.php PHPCI/Store/Base/MigrationStoreBase.php
local_vars.php
Tests/PHPCI/config.yml

View file

@ -8,17 +8,23 @@ build_settings:
- "PHPCI/Migrations" # Ignore the migrations directory, as both PHPMD and PHPCS can't cope with them. - "PHPCI/Migrations" # Ignore the migrations directory, as both PHPMD and PHPCS can't cope with them.
- "PHPCI/Model/Base" # These files are auto-generated, and sometimes hit PHPMD complexity thresholds. - "PHPCI/Model/Base" # These files are auto-generated, and sometimes hit PHPMD complexity thresholds.
- "PHPCI/Languages" # PHPCS fails on character counts for non-Latin languages - "PHPCI/Languages" # PHPCS fails on character counts for non-Latin languages
- "public/assets" # If there are any PHP files in here, we didn't write them.
setup: setup:
composer: composer:
action: "install" action: "install"
prefer_dist: false
test: test:
php_parallel_lint:
ignore:
# Only ignore vendor
- vendor/
php_mess_detector: php_mess_detector:
allowed_warnings: 0 allowed_warnings: 0
rules:
- phpmd.xml
php_code_sniffer: php_code_sniffer:
standard: "PSR2" standard: phpcs.xml
allowed_warnings: 0 allowed_warnings: 0
allowed_errors: 0 allowed_errors: 0
php_loc: php_loc:
@ -26,7 +32,7 @@ test:
php_docblock_checker: php_docblock_checker:
allowed_warnings: 0 allowed_warnings: 0
failure: broken:
email: email:
committer: true committer: true
cc: ["php-ci@googlegroups.com"] cc: ["php-ci@googlegroups.com"]

View file

@ -15,4 +15,7 @@ RUN git config --global user.email "hello@php.ci"
ADD ./ /phpci ADD ./ /phpci
RUN php -r "readfile('https://getcomposer.org/installer');" | php
RUN mv composer.phar /phpci/composer
CMD /phpci/daemonise phpci:daemonise CMD /phpci/daemonise phpci:daemonise

View file

@ -14,7 +14,6 @@ use b8\Exception\HttpException;
use b8\Http\Response; use b8\Http\Response;
use b8\Http\Response\RedirectResponse; use b8\Http\Response\RedirectResponse;
use b8\View; use b8\View;
use PHPCI\Model\Build;
/** /**
* PHPCI Front Controller * PHPCI Front Controller
@ -22,6 +21,11 @@ use PHPCI\Model\Build;
*/ */
class Application extends b8\Application class Application extends b8\Application
{ {
/**
* @var \PHPCI\Controller
*/
protected $controller;
/** /**
* Initialise PHPCI - Handles session verification, routing, etc. * Initialise PHPCI - Handles session verification, routing, etc.
*/ */
@ -47,13 +51,13 @@ class Application extends b8\Application
return false; return false;
}; };
$skipAuth = [$this, 'shouldSkipAuth']; $skipAuth = array($this, 'shouldSkipAuth');
// Handler for the route we're about to register, checks for a valid session where necessary: // Handler for the route we're about to register, checks for a valid session where necessary:
$routeHandler = function (&$route, Response &$response) use (&$request, $validateSession, $skipAuth) { $routeHandler = function (&$route, Response &$response) use (&$request, $validateSession, $skipAuth) {
$skipValidation = in_array($route['controller'], array('session', 'webhook', 'build-status')); $skipValidation = in_array($route['controller'], array('session', 'webhook', 'build-status'));
if (!$skipValidation && !$validateSession() && !$skipAuth()) { if (!$skipValidation && !$validateSession() && (!is_callable($skipAuth) || !$skipAuth())) {
if ($request->isAjax()) { if ($request->isAjax()) {
$response->setResponseCode(401); $response->setResponseCode(401);
$response->setContent(''); $response->setContent('');
@ -100,7 +104,7 @@ class Application extends b8\Application
$this->response->setContent($view->render()); $this->response->setContent($view->render());
} }
if ($this->response->hasLayout()) { if ($this->response->hasLayout() && $this->controller->layout) {
$this->setLayoutVariables($this->controller->layout); $this->setLayoutVariables($this->controller->layout);
$this->controller->layout->content = $this->response->getContent(); $this->controller->layout->content = $this->response->getContent();
@ -131,9 +135,18 @@ class Application extends b8\Application
*/ */
protected function setLayoutVariables(View &$layout) protected function setLayoutVariables(View &$layout)
{ {
/** @var \PHPCI\Store\ProjectStore $projectStore */ $groups = array();
$projectStore = b8\Store\Factory::getStore('Project'); $groupStore = b8\Store\Factory::getStore('ProjectGroup');
$layout->projects = $projectStore->getAll(); $groupList = $groupStore->getWhere(array(), 100, 0, array(), array('title' => 'ASC'));
foreach ($groupList['items'] as $group) {
$thisGroup = array('title' => $group->getTitle());
$projects = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId());
$thisGroup['projects'] = $projects['items'];
$groups[] = $thisGroup;
}
$layout->groups = $groups;
} }
/** /**

View file

@ -36,35 +36,44 @@ class BuildFactory
/** /**
* Takes a generic build and returns a type-specific build model. * Takes a generic build and returns a type-specific build model.
* @param Build $base The build from which to get a more specific build type. * @param Build $build The build from which to get a more specific build type.
* @return Build * @return Build
*/ */
public static function getBuild(Build $base) public static function getBuild(Build $build)
{ {
switch($base->getProject()->getType()) $project = $build->getProject();
{
case 'remote': if (!empty($project)) {
$type = 'RemoteGitBuild'; switch ($project->getType()) {
break; case 'remote':
case 'local': $type = 'RemoteGitBuild';
$type = 'LocalBuild'; break;
break; case 'local':
case 'github': $type = 'LocalBuild';
$type = 'GithubBuild'; break;
break; case 'github':
case 'bitbucket': $type = 'GithubBuild';
$type = 'BitbucketBuild'; break;
break; case 'bitbucket':
case 'gitlab': $type = 'BitbucketBuild';
$type = 'GitlabBuild'; break;
break; case 'gitlab':
case 'hg': $type = 'GitlabBuild';
$type = 'MercurialBuild'; break;
break; case 'hg':
$type = 'MercurialBuild';
break;
case 'svn':
$type = 'SubversionBuild';
break;
default:
return $build;
}
$class = '\\PHPCI\\Model\\Build\\' . $type;
$build = new $class($build->getDataArray());
} }
$type = '\\PHPCI\\Model\\Build\\' . $type; return $build;
return new $type($base->getDataArray());
} }
} }

View file

@ -188,6 +188,14 @@ class Builder implements LoggerAwareInterface
$this->build->sendStatusPostback(); $this->build->sendStatusPostback();
$success = true; $success = true;
$previous_build = $this->build->getProject()->getPreviousBuild($this->build->getBranch());
$previous_state = Build::STATUS_NEW;
if ($previous_build) {
$previous_state = $previous_build->getStatus();
}
try { try {
// Set up the build: // Set up the build:
$this->setupBuild(); $this->setupBuild();
@ -205,34 +213,41 @@ class Builder implements LoggerAwareInterface
$this->build->setStatus(Build::STATUS_FAILED); $this->build->setStatus(Build::STATUS_FAILED);
} }
// Complete stage plugins are always run
$this->pluginExecutor->executePlugins($this->config, 'complete');
if ($success) { if ($success) {
$this->pluginExecutor->executePlugins($this->config, 'success'); $this->pluginExecutor->executePlugins($this->config, 'success');
if ($previous_state == Build::STATUS_FAILED) {
$this->pluginExecutor->executePlugins($this->config, 'fixed');
}
$this->buildLogger->logSuccess(Lang::get('build_success')); $this->buildLogger->logSuccess(Lang::get('build_success'));
} else { } else {
$this->pluginExecutor->executePlugins($this->config, 'failure'); $this->pluginExecutor->executePlugins($this->config, 'failure');
if ($previous_state == Build::STATUS_SUCCESS || $previous_state == Build::STATUS_NEW) {
$this->pluginExecutor->executePlugins($this->config, 'broken');
}
$this->buildLogger->logFailure(Lang::get('build_failed')); $this->buildLogger->logFailure(Lang::get('build_failed'));
} }
// Clean up:
$this->buildLogger->log(Lang::get('removing_build'));
$cmd = 'rm -Rf "%s"';
if (IS_WIN) {
$cmd = 'rmdir /S /Q "%s"';
}
$this->executeCommand($cmd, $this->buildPath);
} catch (\Exception $ex) { } catch (\Exception $ex) {
$this->build->setStatus(Build::STATUS_FAILED); $this->build->setStatus(Build::STATUS_FAILED);
$this->buildLogger->logFailure(Lang::get('exception') . $ex->getMessage()); $this->buildLogger->logFailure(Lang::get('exception') . $ex->getMessage());
}finally{
// Complete stage plugins are always run
$this->pluginExecutor->executePlugins($this->config, 'complete');
} }
// Update the build in the database, ping any external services, etc. // Update the build in the database, ping any external services, etc.
$this->build->sendStatusPostback(); $this->build->sendStatusPostback();
$this->build->setFinished(new \DateTime()); $this->build->setFinished(new \DateTime());
// Clean up:
$this->buildLogger->log(Lang::get('removing_build'));
$this->build->removeBuildDirectory();
$this->store->save($this->build); $this->store->save($this->build);
} }
@ -263,12 +278,14 @@ class Builder implements LoggerAwareInterface
/** /**
* Find a binary required by a plugin. * Find a binary required by a plugin.
* @param $binary * @param string $binary
* @param bool $quiet
*
* @return null|string * @return null|string
*/ */
public function findBinary($binary) public function findBinary($binary, $quiet = false)
{ {
return $this->commandExecutor->findBinary($binary, $this->buildPath); return $this->commandExecutor->findBinary($binary, $quiet = false);
} }
/** /**
@ -287,8 +304,7 @@ class Builder implements LoggerAwareInterface
*/ */
protected function setupBuild() protected function setupBuild()
{ {
$this->buildPath = PHPCI_DIR . 'PHPCI/build/' . $this->build->getId() . '/'; $this->buildPath = $this->build->getBuildPath();
$this->build->currentBuildPath = $this->buildPath;
$this->interpolator->setupInterpolationVars( $this->interpolator->setupInterpolationVars(
$this->build, $this->build,
@ -296,6 +312,8 @@ class Builder implements LoggerAwareInterface
PHPCI_URL PHPCI_URL
); );
$this->commandExecutor->setBuildPath($this->buildPath);
// Create a working copy of the project: // Create a working copy of the project:
if (!$this->build->createWorkingCopy($this, $this->buildPath)) { if (!$this->build->createWorkingCopy($this, $this->buildPath)) {
throw new \Exception(Lang::get('could_not_create_working')); throw new \Exception(Lang::get('could_not_create_working'));

View file

@ -9,24 +9,36 @@
namespace PHPCI\Command; namespace PHPCI\Command;
use PHPCI\Helper\Lang;
use PHPCI\Service\UserService; use PHPCI\Service\UserService;
use PHPCI\Helper\Lang;
use PHPCI\Store\UserStore;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use b8\Store\Factory;
use PHPCI\Builder;
/** /**
* Create admin command - creates an admin user * Create admin command - creates an admin user
* @author Wogan May (@woganmay) * @author Wogan May (@woganmay)
* @package PHPCI * @package PHPCI
* @subpackage Console * @subpackage Console
*/ */
class CreateAdminCommand extends Command class CreateAdminCommand extends Command
{ {
/**
* @var UserStore
*/
protected $userStore;
/**
* @param UserStore $userStore
*/
public function __construct(UserStore $userStore)
{
parent::__construct();
$this->userStore = $userStore;
}
protected function configure() protected function configure()
{ {
$this $this
@ -35,92 +47,36 @@ class CreateAdminCommand extends Command
} }
/** /**
* Creates an admin user in the existing PHPCI database * Creates an admin user in the existing PHPCI database
*/ *
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$userStore = Factory::getStore('User'); $userService = new UserService($this->userStore);
$userService = new UserService($userStore);
require(PHPCI_DIR . 'bootstrap.php'); /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
$dialog = $this->getHelperSet()->get('dialog');
// Try to create a user account: // Function to validate mail address.
$adminEmail = $this->ask(Lang::get('enter_email'), true, FILTER_VALIDATE_EMAIL); $mailValidator = function ($answer) {
if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException(Lang::get('must_be_valid_email'));
}
if (empty($adminEmail)) { return $answer;
return; };
}
$adminPass = $this->ask(Lang::get('enter_pass')); $adminEmail = $dialog->askAndValidate($output, Lang::get('enter_email'), $mailValidator, false);
$adminName = $this->ask(Lang::get('enter_name')); $adminName = $dialog->ask($output, Lang::get('enter_name'));
$adminPass = $dialog->askHiddenResponse($output, Lang::get('enter_password'));
try { try {
$userService->createUser($adminName, $adminEmail, $adminPass, 1); $userService->createUser($adminName, $adminEmail, $adminPass, true);
print Lang::get('user_created') . PHP_EOL; $output->writeln(Lang::get('user_created'));
} catch (\Exception $ex) { } catch (\Exception $e) {
print Lang::get('failed_to_create') . PHP_EOL; $output->writeln(sprintf('<error>%s</error>', Lang::get('failed_to_create')));
print $ex->getMessage(); $output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
print PHP_EOL;
} }
} }
protected function ask($question, $emptyOk = false, $validationFilter = null)
{
print $question . ' ';
$rtn = '';
$stdin = fopen('php://stdin', 'r');
$rtn = fgets($stdin);
fclose($stdin);
$rtn = trim($rtn);
if (!$emptyOk && empty($rtn)) {
$rtn = $this->ask($question, $emptyOk, $validationFilter);
} elseif ($validationFilter != null && ! empty($rtn)) {
if (! $this -> controlFormat($rtn, $validationFilter, $statusMessage)) {
print $statusMessage;
$rtn = $this->ask($question, $emptyOk, $validationFilter);
}
}
return $rtn;
}
protected function controlFormat($valueToInspect, $filter, &$statusMessage)
{
$filters = !(is_array($filter))? array($filter) : $filter;
$statusMessage = '';
$status = true;
$options = array();
foreach ($filters as $filter) {
if (! is_int($filter)) {
$regexp = $filter;
$filter = FILTER_VALIDATE_REGEXP;
$options = array(
'options' => array(
'regexp' => $regexp,
)
);
}
if (! filter_var($valueToInspect, $filter, $options)) {
$status = false;
switch ($filter)
{
case FILTER_VALIDATE_URL:
$statusMessage = Lang::get('must_be_valid_url') . PHP_EOL;
break;
case FILTER_VALIDATE_EMAIL:
$statusMessage = Lang::get('must_be_valid_email') . PHP_EOL;
break;
case FILTER_VALIDATE_REGEXP:
$statusMessage = Lang::get('incorrect_format') . PHP_EOL;
break;
}
}
}
return $status;
}
} }

View file

@ -0,0 +1,85 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Command;
use PHPCI\Helper\Lang;
use PHPCI\Service\BuildService;
use PHPCI\Store\ProjectStore;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Create build command - creates a build for a project
* @author Jérémy DECOOL (@jdecool)
* @package PHPCI
* @subpackage Console
*/
class CreateBuildCommand extends Command
{
/**
* @var ProjectStore
*/
protected $projectStore;
/**
* @var BuildService
*/
protected $buildService;
/**
* @param ProjectStore $projectStore
*/
public function __construct(ProjectStore $projectStore, BuildService $buildService)
{
parent::__construct();
$this->projectStore = $projectStore;
$this->buildService = $buildService;
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('phpci:create-build')
->setDescription(Lang::get('create_build_project'))
->addArgument('projectId', InputArgument::REQUIRED, Lang::get('project_id_argument'))
->addOption('commit', null, InputOption::VALUE_OPTIONAL, Lang::get('commit_id_option'))
->addOption('branch', null, InputOption::VALUE_OPTIONAL, Lang::get('branch_name_option'));
}
/**
* {@inheritDoc}
*/
public function execute(InputInterface $input, OutputInterface $output)
{
$projectId = $input->getArgument('projectId');
$commitId = $input->getOption('commit');
$branch = $input->getOption('branch');
$project = $this->projectStore->getById($projectId);
if (empty($project)) {
throw new \InvalidArgumentException('Project does not exist: ' . $projectId);
}
try {
$this->buildService->createBuild($project, $commitId, $branch);
$output->writeln(Lang::get('build_created'));
} catch (\Exception $e) {
$output->writeln(sprintf('<error>%s</error>', Lang::get('failed')));
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
}
}
}

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* PHPCI - Continuous Integration for PHP * PHPCI - Continuous Integration for PHP
* *
@ -10,33 +11,48 @@
namespace PHPCI\Command; namespace PHPCI\Command;
use Monolog\Logger; use Monolog\Logger;
use PHPCI\ProcessControl\Factory;
use PHPCI\ProcessControl\ProcessControlInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use b8\Store\Factory;
use PHPCI\Builder;
use PHPCI\BuildFactory;
/** /**
* Daemon that loops and call the run-command. * Daemon that loops and call the run-command.
* @author Gabriel Baker <gabriel.baker@autonomicpilot.co.uk> * @author Gabriel Baker <gabriel.baker@autonomicpilot.co.uk>
* @package PHPCI * @package PHPCI
* @subpackage Console * @subpackage Console
*/ */
class DaemonCommand extends Command class DaemonCommand extends Command
{ {
/** /**
* @var \Monolog\Logger * @var Logger
*/ */
protected $logger; protected $logger;
public function __construct(Logger $logger, $name = null) /**
* @var string
*/
protected $pidFilePath;
/**
* @var string
*/
protected $logFilePath;
/**
* @var ProcessControlInterface
*/
protected $processControl;
public function __construct(Logger $logger, ProcessControlInterface $processControl = null, $name = null)
{ {
parent::__construct($name); parent::__construct($name);
$this->logger = $logger; $this->logger = $logger;
$this->processControl = $processControl ?: Factory::getInstance();
} }
protected function configure() protected function configure()
@ -45,17 +61,30 @@ class DaemonCommand extends Command
->setName('phpci:daemon') ->setName('phpci:daemon')
->setDescription('Initiates the daemon to run commands.') ->setDescription('Initiates the daemon to run commands.')
->addArgument( ->addArgument(
'state', 'state', InputArgument::REQUIRED, 'start|stop|status'
InputArgument::REQUIRED, )
'start|stop|status' ->addOption(
); 'pid-file', 'p', InputOption::VALUE_REQUIRED,
'Path of the PID file',
implode(DIRECTORY_SEPARATOR,
array(PHPCI_DIR, 'daemon', 'daemon.pid'))
)
->addOption(
'log-file', 'l', InputOption::VALUE_REQUIRED,
'Path of the log file',
implode(DIRECTORY_SEPARATOR,
array(PHPCI_DIR, 'daemon', 'daemon.log'))
);
} }
/** /**
* Loops through running. * Loops through running.
*/ */
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$this->pidFilePath = $input->getOption('pid-file');
$this->logFilePath = $input->getOption('log-file');
$state = $input->getArgument('state'); $state = $input->getArgument('state');
switch ($state) { switch ($state) {
@ -66,64 +95,108 @@ class DaemonCommand extends Command
$this->stopDaemon(); $this->stopDaemon();
break; break;
case 'status': case 'status':
$this->statusDaemon(); $this->statusDaemon($output);
break; break;
default: default:
echo "Not a valid choice, please use start stop or status"; $this->output->writeln("<error>Not a valid choice, please use start, stop or status</error>");
break; break;
} }
} }
protected function startDaemon() protected function startDaemon()
{ {
$pid = $this->getRunningPid();
if (file_exists(PHPCI_DIR.'/daemon/daemon.pid')) { if ($pid) {
echo "Already started\n"; $this->logger->notice("Daemon already started", array('pid' => $pid));
$this->logger->warning("Daemon already started");
return "alreadystarted"; return "alreadystarted";
} }
$logfile = PHPCI_DIR."/daemon/daemon.log"; $this->logger->info("Trying to start the daemon");
$cmd = "nohup %s/daemonise phpci:daemonise > %s 2>&1 &"; $cmd = "nohup %s/daemonise phpci:daemonise > %s 2>&1 &";
$command = sprintf($cmd, PHPCI_DIR, $logfile); $command = sprintf($cmd, PHPCI_DIR, $this->logFilePath);
$this->logger->info("Daemon started"); $output = $exitCode = null;
exec($command); exec($command, $output, $exitCode);
if ($exitCode !== 0) {
$this->logger->error(sprintf("daemonise exited with status %d", $exitCode));
return "notstarted";
}
for ($i = 0; !($pid = $this->getRunningPid()) && $i < 5; $i++) {
sleep(1);
}
if (!$pid) {
$this->logger->error("Could not start the daemon");
return "notstarted";
}
$this->logger->notice("Daemon started", array('pid' => $pid));
return "started";
} }
protected function stopDaemon() protected function stopDaemon()
{ {
$pid = $this->getRunningPid();
if (!file_exists(PHPCI_DIR.'/daemon/daemon.pid')) { if (!$pid) {
echo "Not started\n"; $this->logger->notice("Cannot stop the daemon as it is not started");
$this->logger->warning("Can't stop daemon as not started");
return "notstarted"; return "notstarted";
} }
$cmd = "kill $(cat %s/daemon/daemon.pid)"; $this->logger->info("Trying to terminate the daemon", array('pid' => $pid));
$command = sprintf($cmd, PHPCI_DIR); $this->processControl->kill($pid);
exec($command);
$this->logger->info("Daemon stopped");
unlink(PHPCI_DIR.'/daemon/daemon.pid');
}
protected function statusDaemon() for ($i = 0; ($pid = $this->getRunningPid()) && $i < 5; $i++) {
{ sleep(1);
if (!file_exists(PHPCI_DIR.'/daemon/daemon.pid')) {
echo "Not running\n";
return "notrunning";
} }
$pid = trim(file_get_contents(PHPCI_DIR.'/daemon/daemon.pid')); if ($pid) {
$pidcheck = sprintf("/proc/%s", $pid); $this->logger->warning("The daemon is resiting, trying to kill it", array('pid' => $pid));
if (is_dir($pidcheck)) { $this->processControl->kill($pid, true);
echo "Running\n";
for ($i = 0; ($pid = $this->getRunningPid()) && $i < 5; $i++) {
sleep(1);
}
}
if (!$pid) {
$this->logger->notice("Daemon stopped");
return "stopped";
}
$this->logger->error("Could not stop the daemon");
}
protected function statusDaemon(OutputInterface $output)
{
$pid = $this->getRunningPid();
if ($pid) {
$output->writeln(sprintf('The daemon is running, PID: %d', $pid));
return "running"; return "running";
} }
unlink(PHPCI_DIR.'/daemon/daemon.pid'); $output->writeln('The daemon is not running');
echo "Not running\n";
return "notrunning"; return "notrunning";
} }
/** Check if there is a running daemon
*
* @return int|null
*/
protected function getRunningPid()
{
if (!file_exists($this->pidFilePath)) {
return;
}
$pid = intval(trim(file_get_contents($this->pidFilePath)));
if($this->processControl->isRunning($pid, true)) {
return $pid;
}
// Not found, remove the stale PID file
unlink($this->pidFilePath);
}
} }

View file

@ -12,9 +12,7 @@ namespace PHPCI\Command;
use Monolog\Logger; use Monolog\Logger;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
/** /**
@ -76,7 +74,7 @@ class DaemoniseCommand extends Command
$this->sleep = 0; $this->sleep = 0;
$runner = new RunCommand($this->logger); $runner = new RunCommand($this->logger);
$runner->setMaxBuilds(1); $runner->setMaxBuilds(1);
$runner->setIsDaemon(true); $runner->setDaemon(true);
$emptyInput = new ArgvInput(array()); $emptyInput = new ArgvInput(array());
@ -87,7 +85,8 @@ class DaemoniseCommand extends Command
try { try {
$buildCount = $runner->run($emptyInput, $output); $buildCount = $runner->run($emptyInput, $output);
} catch (\Exception $e) { } catch (\Exception $e) {
var_dump($e); $output->writeln('<error>Exception: ' . $e->getMessage() . '</error>');
$output->writeln('<error>Line: ' . $e->getLine() . ' - File: ' . $e->getFile() . '</error>');
} }
if (0 == $buildCount && $this->sleep < 15) { if (0 == $buildCount && $this->sleep < 15) {

View file

@ -10,9 +10,7 @@
namespace PHPCI\Command; namespace PHPCI\Command;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use b8\Database; use b8\Database;
use b8\Database\CodeGenerator; use b8\Database\CodeGenerator;

View file

@ -13,16 +13,15 @@ use Exception;
use PDO; use PDO;
use b8\Config; use b8\Config;
use b8\Database;
use b8\Store\Factory; use b8\Store\Factory;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\DialogHelper;
use PHPCI\Service\UserService; use PHPCI\Service\UserService;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/** /**
* Install console command - Installs PHPCI. * Install console command - Installs PHPCI.
@ -49,6 +48,9 @@ class InstallCommand extends Command
->addOption('admin-pass', null, InputOption::VALUE_OPTIONAL, Lang::get('admin_pass')) ->addOption('admin-pass', null, InputOption::VALUE_OPTIONAL, Lang::get('admin_pass'))
->addOption('admin-mail', null, InputOption::VALUE_OPTIONAL, Lang::get('admin_email')) ->addOption('admin-mail', null, InputOption::VALUE_OPTIONAL, Lang::get('admin_email'))
->addOption('config-path', null, InputOption::VALUE_OPTIONAL, Lang::get('config_path'), $defaultPath) ->addOption('config-path', null, InputOption::VALUE_OPTIONAL, Lang::get('config_path'), $defaultPath)
->addOption('queue-disabled', null, InputOption::VALUE_NONE, 'Don\'t ask for queue details')
->addOption('queue-server', null, InputOption::VALUE_OPTIONAL, 'Beanstalkd queue server hostname')
->addOption('queue-name', null, InputOption::VALUE_OPTIONAL, 'Beanstalkd queue name')
->setDescription(Lang::get('install_phpci')); ->setDescription(Lang::get('install_phpci'));
} }
@ -59,7 +61,9 @@ class InstallCommand extends Command
{ {
$this->configFilePath = $input->getOption('config-path'); $this->configFilePath = $input->getOption('config-path');
$this->verifyNotInstalled($output); if (!$this->verifyNotInstalled($output)) {
return;
}
$output->writeln(''); $output->writeln('');
$output->writeln('<info>******************</info>'); $output->writeln('<info>******************</info>');
@ -96,7 +100,7 @@ class InstallCommand extends Command
$this->writeConfigFile($conf); $this->writeConfigFile($conf);
$this->setupDatabase($output); $this->setupDatabase($output);
$admin = $this->getAdminInforamtion($input, $output); $admin = $this->getAdminInformation($input, $output);
$this->createAdminUser($admin, $output); $this->createAdminUser($admin, $output);
} }
@ -161,7 +165,7 @@ class InstallCommand extends Command
* @param OutputInterface $output * @param OutputInterface $output
* @return array * @return array
*/ */
protected function getAdminInforamtion(InputInterface $input, OutputInterface $output) protected function getAdminInformation(InputInterface $input, OutputInterface $output)
{ {
$admin = array(); $admin = array();
@ -173,7 +177,7 @@ class InstallCommand extends Command
// Function to validate mail address. // Function to validate mail address.
$mailValidator = function ($answer) { $mailValidator = function ($answer) {
if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) {
throw new Exception(Lang::get('must_be_valid_email')); throw new \InvalidArgumentException(Lang::get('must_be_valid_email'));
} }
return $answer; return $answer;
@ -230,10 +234,45 @@ class InstallCommand extends Command
} }
$phpci['url'] = $url; $phpci['url'] = $url;
$phpci['worker'] = $this->getQueueInformation($input, $output, $dialog);
return $phpci; return $phpci;
} }
/**
* If the user wants to use a queue, get the necessary details.
* @param InputInterface $input
* @param OutputInterface $output
* @param DialogHelper $dialog
* @return array
*/
protected function getQueueInformation(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
{
if ($input->getOption('queue-disabled')) {
return null;
}
$rtn = [];
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Use beanstalkd to manage build queue? ', true);
if (!$helper->ask($input, $output, $question)) {
$output->writeln('<error>Skipping beanstalkd configuration.</error>');
return null;
}
if (!$rtn['host'] = $input->getOption('queue-server')) {
$rtn['host'] = $dialog->ask($output, 'Enter your beanstalkd hostname [localhost]: ', 'localhost');
}
if (!$rtn['queue'] = $input->getOption('queue-name')) {
$rtn['queue'] = $dialog->ask($output, 'Enter the queue (tube) name to use [phpci]: ', 'phpci');
}
return $rtn;
}
/** /**
* Load configuration for DB form CLI options or ask info to user. * Load configuration for DB form CLI options or ask info to user.
* *
@ -296,6 +335,8 @@ class InstallCommand extends Command
) )
); );
unset($pdo);
return true; return true;
} catch (Exception $ex) { } catch (Exception $ex) {
@ -322,7 +363,9 @@ class InstallCommand extends Command
{ {
$output->write(Lang::get('setting_up_db')); $output->write(Lang::get('setting_up_db'));
shell_exec(PHPCI_DIR . 'vendor/bin/phinx migrate -c "' . PHPCI_DIR . 'phinx.php"'); $phinxBinary = escapeshellarg(PHPCI_DIR . 'vendor/bin/phinx');
$phinxScript = escapeshellarg(PHPCI_DIR . 'phinx.php');
shell_exec($phinxBinary . ' migrate -c ' . $phinxScript);
$output->writeln('<info>'.Lang::get('ok').'</info>'); $output->writeln('<info>'.Lang::get('ok').'</info>');
} }
@ -346,7 +389,6 @@ class InstallCommand extends Command
} catch (\Exception $ex) { } catch (\Exception $ex) {
$output->writeln('<error>'.Lang::get('failed_to_create').'</error>'); $output->writeln('<error>'.Lang::get('failed_to_create').'</error>');
$output->writeln('<error>' . $ex->getMessage() . '</error>'); $output->writeln('<error>' . $ex->getMessage() . '</error>');
die;
} }
} }
@ -361,6 +403,7 @@ class InstallCommand extends Command
/** /**
* @param OutputInterface $output * @param OutputInterface $output
* @return bool
*/ */
protected function verifyNotInstalled(OutputInterface $output) protected function verifyNotInstalled(OutputInterface $output)
{ {
@ -370,8 +413,10 @@ class InstallCommand extends Command
if (!empty($content)) { if (!empty($content)) {
$output->writeln('<error>'.Lang::get('config_exists').'</error>'); $output->writeln('<error>'.Lang::get('config_exists').'</error>');
$output->writeln('<error>'.Lang::get('update_instead').'</error>'); $output->writeln('<error>'.Lang::get('update_instead').'</error>');
die; return false;
} }
} }
return true;
} }
} }

View file

@ -14,9 +14,7 @@ use b8\HttpClient;
use Monolog\Logger; use Monolog\Logger;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Parser;
use PHPCI\Model\Build; use PHPCI\Model\Build;
@ -60,7 +58,7 @@ class PollCommand extends Command
if (!$token) { if (!$token) {
$this->logger->error(Lang::get('no_token')); $this->logger->error(Lang::get('no_token'));
exit(); return;
} }
$buildStore = Factory::getStore('Build'); $buildStore = Factory::getStore('Build');

View file

@ -0,0 +1,93 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Command;
use b8\Store\Factory;
use Monolog\Logger;
use PHPCI\Service\BuildService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Re-runs the last run build.
* @author Dan Cryer <dan@block8.co.uk>
* @package PHPCI
* @subpackage Console
*/
class RebuildCommand extends Command
{
/**
* @var Logger
*/
protected $logger;
/**
* @var OutputInterface
*/
protected $output;
/**
* @var boolean
*/
protected $run;
/**
* @var int
*/
protected $sleep;
/**
* @param \Monolog\Logger $logger
* @param string $name
*/
public function __construct(Logger $logger, $name = null)
{
parent::__construct($name);
$this->logger = $logger;
}
protected function configure()
{
$this
->setName('phpci:rebuild')
->setDescription('Re-runs the last run build.');
}
/**
* Loops through running.
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$runner = new RunCommand($this->logger);
$runner->setMaxBuilds(1);
$runner->setDaemon(false);
/** @var \PHPCI\Store\BuildStore $store */
$store = Factory::getStore('Build');
$service = new BuildService($store);
$builds = $store->getLatestBuilds(null, 1);
$lastBuild = array_shift($builds);
$service->createDuplicateBuild($lastBuild);
$runner->run(new ArgvInput(array()), $output);
}
/**
* Called when log entries are made in Builder / the plugins.
* @see \PHPCI\Builder::log()
*/
public function logCallback($log)
{
$this->output->writeln($log);
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Command;
use b8\Config;
use b8\Store\Factory;
use Monolog\Logger;
use PHPCI\BuildFactory;
use PHPCI\Helper\Lang;
use PHPCI\Logging\OutputLogHandler;
use PHPCI\Service\BuildService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Dan Cryer <dan@block8.co.uk>
* @package PHPCI
* @subpackage Console
*/
class RebuildQueueCommand extends Command
{
/**
* @var OutputInterface
*/
protected $output;
/**
* @var Logger
*/
protected $logger;
/**
* @param \Monolog\Logger $logger
* @param string $name
*/
public function __construct(Logger $logger, $name = null)
{
parent::__construct($name);
$this->logger = $logger;
}
protected function configure()
{
$this
->setName('phpci:rebuild-queue')
->setDescription('Rebuilds the PHPCI worker queue.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->output = $output;
// For verbose mode we want to output all informational and above
// messages to the symphony output interface.
if ($input->hasOption('verbose') && $input->getOption('verbose')) {
$this->logger->pushHandler(
new OutputLogHandler($this->output, Logger::INFO)
);
}
$store = Factory::getStore('Build');
$result = $store->getByStatus(0);
$this->logger->addInfo(Lang::get('found_n_builds', count($result['items'])));
$buildService = new BuildService($store);
while (count($result['items'])) {
$build = array_shift($result['items']);
$build = BuildFactory::getBuild($build);
$this->logger->addInfo('Added build #' . $build->getId() . ' to queue.');
$buildService->addBuildToQueue($build);
}
}
}

View file

@ -15,11 +15,8 @@ use PHPCI\Helper\Lang;
use PHPCI\Logging\BuildDBLogHandler; use PHPCI\Logging\BuildDBLogHandler;
use PHPCI\Logging\LoggedBuildContextTidier; use PHPCI\Logging\LoggedBuildContextTidier;
use PHPCI\Logging\OutputLogHandler; use PHPCI\Logging\OutputLogHandler;
use Psr\Log\LoggerAwareInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use b8\Store\Factory; use b8\Store\Factory;
use PHPCI\Builder; use PHPCI\Builder;
@ -47,7 +44,7 @@ class RunCommand extends Command
/** /**
* @var int * @var int
*/ */
protected $maxBuilds = null; protected $maxBuilds = 100;
/** /**
* @var bool * @var bool
@ -143,7 +140,7 @@ class RunCommand extends Command
$this->maxBuilds = (int)$numBuilds; $this->maxBuilds = (int)$numBuilds;
} }
public function setIsDaemon($fromDaemon) public function setDaemon($fromDaemon)
{ {
$this->isFromDaemon = (bool)$fromDaemon; $this->isFromDaemon = (bool)$fromDaemon;
} }
@ -169,7 +166,7 @@ class RunCommand extends Command
$build->setStatus(Build::STATUS_FAILED); $build->setStatus(Build::STATUS_FAILED);
$build->setFinished(new \DateTime()); $build->setFinished(new \DateTime());
$store->save($build); $store->save($build);
$this->removeBuildDirectory($build); $build->removeBuildDirectory();
continue; continue;
} }
@ -178,19 +175,4 @@ class RunCommand extends Command
return $rtn; return $rtn;
} }
protected function removeBuildDirectory($build)
{
$buildPath = PHPCI_DIR . 'PHPCI/build/' . $build->getId() . '/';
if (is_dir($buildPath)) {
$cmd = 'rm -Rf "%s"';
if (IS_WIN) {
$cmd = 'rmdir /S /Q "%s"';
}
shell_exec($cmd);
}
}
} }

View file

@ -9,13 +9,11 @@
namespace PHPCI\Command; namespace PHPCI\Command;
use b8\Config;
use Monolog\Logger; use Monolog\Logger;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
/** /**
@ -49,7 +47,9 @@ class UpdateCommand extends Command
*/ */
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$this->verifyInstalled($output); if (!$this->verifyInstalled($output)) {
return;
}
$output->write(Lang::get('updating_phpci')); $output->write(Lang::get('updating_phpci'));
@ -60,17 +60,9 @@ class UpdateCommand extends Command
protected function verifyInstalled(OutputInterface $output) protected function verifyInstalled(OutputInterface $output)
{ {
if (!file_exists(PHPCI_DIR . 'PHPCI/config.yml')) { $config = Config::getInstance();
$output->writeln('<error>'.Lang::get('not_installed').'</error>'); $phpciUrl = $config->get('phpci.url');
$output->writeln('<error>'.Lang::get('install_instead').'</error>');
die;
}
$content = file_get_contents(PHPCI_DIR . 'PHPCI/config.yml'); return !empty($phpciUrl);
if (empty($content)) {
$output->writeln('<error>'.Lang::get('not_installed').'</error>');
$output->writeln('<error>'.Lang::get('install_instead').'</error>');
die;
}
} }
} }

View file

@ -0,0 +1,87 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Command;
use b8\Config;
use Monolog\Logger;
use PHPCI\Logging\OutputLogHandler;
use PHPCI\Worker\BuildWorker;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Worker Command - Starts the BuildWorker, which pulls jobs from beanstalkd
* @author Dan Cryer <dan@block8.co.uk>
* @package PHPCI
* @subpackage Console
*/
class WorkerCommand extends Command
{
/**
* @var OutputInterface
*/
protected $output;
/**
* @var Logger
*/
protected $logger;
/**
* @param \Monolog\Logger $logger
* @param string $name
*/
public function __construct(Logger $logger, $name = null)
{
parent::__construct($name);
$this->logger = $logger;
}
protected function configure()
{
$this
->setName('phpci:worker')
->setDescription('Runs the PHPCI build worker.')
->addOption('debug', null, null, 'Run PHPCI in Debug Mode');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->output = $output;
// For verbose mode we want to output all informational and above
// messages to the symphony output interface.
if ($input->hasOption('verbose') && $input->getOption('verbose')) {
$this->logger->pushHandler(
new OutputLogHandler($this->output, Logger::INFO)
);
}
// Allow PHPCI to run in "debug mode"
if ($input->hasOption('debug') && $input->getOption('debug')) {
$output->writeln('<comment>Debug mode enabled.</comment>');
define('PHPCI_DEBUG_MODE', true);
}
$config = Config::getInstance()->get('phpci.worker', []);
if (empty($config['host']) || empty($config['queue'])) {
$error = 'The worker is not configured. You must set a host and queue in your config.yml file.';
throw new \Exception($error);
}
$worker = new BuildWorker($config['host'], $config['queue']);
$worker->setLogger($this->logger);
$worker->setMaxJobs(Config::getInstance()->get('phpci.worker.max_jobs', -1));
$worker->startWorker();
}
}

View file

@ -10,6 +10,7 @@
namespace PHPCI; namespace PHPCI;
use b8\Config; use b8\Config;
use b8\Exception\HttpException\ForbiddenException;
use b8\Http\Request; use b8\Http\Request;
use b8\Http\Response; use b8\Http\Response;
use b8\View; use b8\View;
@ -30,6 +31,11 @@ class Controller extends \b8\Controller
*/ */
protected $view; protected $view;
/**
* @var \b8\View
*/
public $layout;
/** /**
* Initialise the controller. * Initialise the controller.
*/ */
@ -86,6 +92,10 @@ class Controller extends \b8\Controller
$this->setView($action); $this->setView($action);
$response = parent::handleAction($action, $actionParams); $response = parent::handleAction($action, $actionParams);
if ($response instanceof Response) {
return $response;
}
if (is_string($response)) { if (is_string($response)) {
$this->controllerView->content = $response; $this->controllerView->content = $response;
} elseif (isset($this->view)) { } elseif (isset($this->view)) {

View file

@ -11,7 +11,9 @@ namespace PHPCI\Controller;
use b8; use b8;
use b8\Exception\HttpException\NotFoundException; use b8\Exception\HttpException\NotFoundException;
use b8\Http\Response\JsonResponse;
use PHPCI\BuildFactory; use PHPCI\BuildFactory;
use PHPCI\Helper\AnsiConverter;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\Build; use PHPCI\Model\Build;
use PHPCI\Model\Project; use PHPCI\Model\Project;
@ -66,19 +68,37 @@ class BuildController extends \PHPCI\Controller
$this->layout->title = Lang::get('build_n', $buildId); $this->layout->title = Lang::get('build_n', $buildId);
$this->layout->subtitle = $build->getProjectTitle(); $this->layout->subtitle = $build->getProjectTitle();
$nav = array( switch ($build->getStatus()) {
'title' => Lang::get('build_n', $buildId), case 0:
'icon' => 'cog', $this->layout->skin = 'blue';
'links' => array( break;
'build/rebuild/' . $build->getId() => Lang::get('rebuild_now'),
),
);
if ($this->currentUserIsAdmin()) { case 1:
$nav['links']['build/delete/' . $build->getId()] = Lang::get('delete_build'); $this->layout->skin = 'yellow';
break;
case 2:
$this->layout->skin = 'green';
break;
case 3:
$this->layout->skin = 'red';
break;
} }
$this->layout->nav = $nav; $rebuild = Lang::get('rebuild_now');
$rebuildLink = PHPCI_URL . 'build/rebuild/' . $build->getId();
$delete = Lang::get('delete_build');
$deleteLink = PHPCI_URL . 'build/delete/' . $build->getId();
$actions = "<a class=\"btn btn-default\" href=\"{$rebuildLink}\">{$rebuild}</a> ";
if ($this->currentUserIsAdmin()) {
$actions .= " <a class=\"btn btn-danger\" href=\"{$deleteLink}\">{$delete}</a>";
}
$this->layout->actions = $actions;
} }
/** /**
@ -107,7 +127,17 @@ class BuildController extends \PHPCI\Controller
*/ */
public function data($buildId) public function data($buildId)
{ {
die($this->getBuildData(BuildFactory::getBuildById($buildId))); $response = new JsonResponse();
$build = BuildFactory::getBuildById($buildId);
if (!$build) {
$response->setResponseCode(404);
$response->setContent(array());
return $response;
}
$response->setContent($this->getBuildData($build));
return $response;
} }
/** /**
@ -124,13 +154,15 @@ class BuildController extends \PHPCI\Controller
$data = $this->buildStore->getMeta($key, $build->getProjectId(), $buildId, $build->getBranch(), $numBuilds); $data = $this->buildStore->getMeta($key, $build->getProjectId(), $buildId, $build->getBranch(), $numBuilds);
} }
die(json_encode($data)); $response = new JsonResponse();
$response->setContent($data);
return $response;
} }
/** /**
* Get build data from database and json encode it: * Get build data from database and json encode it:
*/ */
protected function getBuildData($build) protected function getBuildData(Build $build)
{ {
$data = array(); $data = array();
$data['status'] = (int)$build->getStatus(); $data['status'] = (int)$build->getStatus();
@ -138,8 +170,21 @@ class BuildController extends \PHPCI\Controller
$data['created'] = !is_null($build->getCreated()) ? $build->getCreated()->format('Y-m-d H:i:s') : null; $data['created'] = !is_null($build->getCreated()) ? $build->getCreated()->format('Y-m-d H:i:s') : null;
$data['started'] = !is_null($build->getStarted()) ? $build->getStarted()->format('Y-m-d H:i:s') : null; $data['started'] = !is_null($build->getStarted()) ? $build->getStarted()->format('Y-m-d H:i:s') : null;
$data['finished'] = !is_null($build->getFinished()) ? $build->getFinished()->format('Y-m-d H:i:s') : null; $data['finished'] = !is_null($build->getFinished()) ? $build->getFinished()->format('Y-m-d H:i:s') : null;
$data['duration'] = $build->getDuration();
return json_encode($data); /** @var \PHPCI\Store\BuildErrorStore $errorStore */
$errorStore = b8\Store\Factory::getStore('BuildError');
$errors = $errorStore->getErrorsForBuild($build->getId(), $this->getParam('since', null));
$errorView = new b8\View('Build/errors');
$errorView->build = $build;
$errorView->errors = $errors;
$data['errors'] = $errorStore->getErrorTotalForBuild($build->getId());
$data['error_html'] = $errorView->render();
$data['since'] = (new \DateTime())->format('Y-m-d H:i:s');
return $data;
} }
/** /**
@ -155,8 +200,13 @@ class BuildController extends \PHPCI\Controller
$build = $this->buildService->createDuplicateBuild($copy); $build = $this->buildService->createDuplicateBuild($copy);
header('Location: '.PHPCI_URL.'build/view/' . $build->getId()); if ($this->buildService->queueError) {
exit; $_SESSION['global_error'] = Lang::get('add_to_queue_failed');
}
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', PHPCI_URL.'build/view/' . $build->getId());
return $response;
} }
/** /**
@ -174,8 +224,9 @@ class BuildController extends \PHPCI\Controller
$this->buildService->deleteBuild($build); $this->buildService->deleteBuild($build);
header('Location: '.PHPCI_URL.'project/view/' . $build->getProjectId()); $response = new b8\Http\Response\RedirectResponse();
exit; $response->setHeader('Location', PHPCI_URL.'project/view/' . $build->getProjectId());
return $response;
} }
/** /**
@ -183,11 +234,7 @@ class BuildController extends \PHPCI\Controller
*/ */
protected function cleanLog($log) protected function cleanLog($log)
{ {
$log = str_replace('[0;32m', '<span style="color: green">', $log); return AnsiConverter::convert($log);
$log = str_replace('[0;31m', '<span style="color: red">', $log);
$log = str_replace('[0m', '</span>', $log);
return $log;
} }
/** /**
@ -200,9 +247,9 @@ class BuildController extends \PHPCI\Controller
'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)), 'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)),
); );
if ($this->request->isAjax()) { $response = new JsonResponse();
die(json_encode($rtn)); $response->setContent($rtn);
} return $response;
} }
/** /**

View file

@ -15,6 +15,7 @@ use b8\Store;
use PHPCI\BuildFactory; use PHPCI\BuildFactory;
use PHPCI\Model\Project; use PHPCI\Model\Project;
use PHPCI\Model\Build; use PHPCI\Model\Build;
use PHPCI\Service\BuildStatusService;
/** /**
* Build Status Controller - Allows external access to build status information / images. * Build Status Controller - Allows external access to build status information / images.
@ -24,10 +25,9 @@ use PHPCI\Model\Build;
*/ */
class BuildStatusController extends \PHPCI\Controller class BuildStatusController extends \PHPCI\Controller
{ {
/** /* @var \PHPCI\Store\ProjectStore */
* @var \PHPCI\Store\ProjectStore
*/
protected $projectStore; protected $projectStore;
/* @var \PHPCI\Store\BuildStore */
protected $buildStore; protected $buildStore;
/** /**
@ -53,7 +53,7 @@ class BuildStatusController extends \PHPCI\Controller
$status = 'passing'; $status = 'passing';
if (!$project->getAllowPublicStatus()) { if (!$project->getAllowPublicStatus()) {
die(); return null;
} }
if (isset($project) && $project instanceof Project) { if (isset($project) && $project instanceof Project) {
@ -70,16 +70,90 @@ class BuildStatusController extends \PHPCI\Controller
return $status; return $status;
} }
/**
* Displays projects information in ccmenu format
*
* @param $projectId
* @return bool
* @throws \Exception
* @throws b8\Exception\HttpException
*/
public function ccxml($projectId)
{
/* @var Project $project */
$project = $this->projectStore->getById($projectId);
$xml = new \SimpleXMLElement('<Projects/>');
if (!$project instanceof Project || !$project->getAllowPublicStatus()) {
return $this->renderXml($xml);
}
try {
$branchList = $this->buildStore->getBuildBranches($projectId);
if (!$branchList) {
$branchList = array($project->getBranch());
}
foreach ($branchList as $branch) {
$buildStatusService = new BuildStatusService($branch, $project, $project->getLatestBuild($branch));
if ($attributes = $buildStatusService->toArray()) {
$projectXml = $xml->addChild('Project');
foreach ($attributes as $attributeKey => $attributeValue) {
$projectXml->addAttribute($attributeKey, $attributeValue);
}
}
}
} catch (\Exception $e) {
$xml = new \SimpleXMLElement('<projects/>');
}
return $this->renderXml($xml);
}
/**
* @param \SimpleXMLElement $xml
* @return bool
*/
protected function renderXml(\SimpleXMLElement $xml = null)
{
$this->response->setHeader('Content-Type', 'text/xml');
$this->response->setContent($xml->asXML());
$this->response->flush();
echo $xml->asXML();
return true;
}
/** /**
* Returns the appropriate build status image in SVG format for a given project. * Returns the appropriate build status image in SVG format for a given project.
*/ */
public function image($projectId) public function image($projectId)
{ {
$status = $this->getStatus($projectId); $style = $this->getParam('style', 'plastic');
$color = ($status == 'passing') ? 'green' : 'red'; $label = $this->getParam('label', 'build');
header('Content-Type: image/svg+xml'); $status = $this->getStatus($projectId);
die(file_get_contents('http://img.shields.io/badge/build-' . $status . '-' . $color . '.svg'));
if (is_null($status)) {
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', '/');
return $response;
}
$color = ($status == 'passing') ? 'green' : 'red';
$image = file_get_contents(sprintf(
'http://img.shields.io/badge/%s-%s-%s.svg?style=%s',
$label,
$status,
$color,
$style
));
$this->response->disableLayout();
$this->response->setHeader('Content-Type', 'image/svg+xml');
$this->response->setContent($image);
return $this->response;
} }
/** /**

View file

@ -0,0 +1,120 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Controller;
use b8;
use b8\Form;
use b8\Store;
use PHPCI\Controller;
use PHPCI\Model\ProjectGroup;
/**
* Project Controller - Allows users to create, edit and view projects.
* @author Dan Cryer <dan@block8.co.uk>
* @package PHPCI
* @subpackage Web
*/
class GroupController extends Controller
{
/**
* @var \PHPCI\Store\ProjectGroupStore
*/
protected $groupStore;
/**
* Set up this controller.
*/
public function init()
{
$this->groupStore = b8\Store\Factory::getStore('ProjectGroup');
}
/**
* List project groups.
*/
public function index()
{
$this->requireAdmin();
$groups = array();
$groupList = $this->groupStore->getWhere(array(), 100, 0, array(), array('title' => 'ASC'));
foreach ($groupList['items'] as $group) {
$thisGroup = array(
'title' => $group->getTitle(),
'id' => $group->getId(),
);
$projects = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId());
$thisGroup['projects'] = $projects['items'];
$groups[] = $thisGroup;
}
$this->view->groups = $groups;
}
/**
* Add or edit a project group.
* @param null $groupId
* @return void|b8\Http\Response\RedirectResponse
*/
public function edit($groupId = null)
{
$this->requireAdmin();
if (!is_null($groupId)) {
$group = $this->groupStore->getById($groupId);
} else {
$group = new ProjectGroup();
}
if ($this->request->getMethod() == 'POST') {
$group->setTitle($this->getParam('title'));
$this->groupStore->save($group);
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', PHPCI_URL.'group');
return $response;
}
$form = new Form();
$form->setMethod('POST');
$form->setAction(PHPCI_URL . 'group/edit' . (!is_null($groupId) ? '/' . $groupId : ''));
$title = new Form\Element\Text('title');
$title->setContainerClass('form-group');
$title->setClass('form-control');
$title->setLabel('Group Title');
$title->setValue($group->getTitle());
$submit = new Form\Element\Submit();
$submit->setValue('Save Group');
$form->addField($title);
$form->addField($submit);
$this->view->form = $form;
}
/**
* Delete a project group.
* @param $groupId
* @return b8\Http\Response\RedirectResponse
*/
public function delete($groupId)
{
$this->requireAdmin();
$group = $this->groupStore->getById($groupId);
$this->groupStore->delete($group);
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', PHPCI_URL.'group');
return $response;
}
}

View file

@ -23,15 +23,20 @@ use PHPCI\Model\Build;
class HomeController extends \PHPCI\Controller class HomeController extends \PHPCI\Controller
{ {
/** /**
* @var \b8\Store\BuildStore * @var \PHPCI\Store\BuildStore
*/ */
protected $buildStore; protected $buildStore;
/** /**
* @var \b8\Store\ProjectStore * @var \PHPCI\Store\ProjectStore
*/ */
protected $projectStore; protected $projectStore;
/**
* @var \PHPCI\Store\ProjectGroupStore
*/
protected $groupStore;
/** /**
* Initialise the controller, set up stores and services. * Initialise the controller, set up stores and services.
*/ */
@ -39,6 +44,7 @@ class HomeController extends \PHPCI\Controller
{ {
$this->buildStore = b8\Store\Factory::getStore('Build'); $this->buildStore = b8\Store\Factory::getStore('Build');
$this->projectStore = b8\Store\Factory::getStore('Project'); $this->projectStore = b8\Store\Factory::getStore('Project');
$this->groupStore = b8\Store\Factory::getStore('ProjectGroup');
} }
/** /**
@ -47,9 +53,6 @@ class HomeController extends \PHPCI\Controller
public function index() public function index()
{ {
$this->layout->title = Lang::get('dashboard'); $this->layout->title = Lang::get('dashboard');
$projects = $this->projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC'));
$builds = $this->buildStore->getLatestBuilds(null, 10); $builds = $this->buildStore->getLatestBuilds(null, 10);
foreach ($builds as &$build) { foreach ($builds as &$build) {
@ -57,8 +60,7 @@ class HomeController extends \PHPCI\Controller
} }
$this->view->builds = $builds; $this->view->builds = $builds;
$this->view->projects = $projects['items']; $this->view->groups = $this->getGroupInfo();
$this->view->summary = $this->getSummaryHtml($projects);
return $this->view->render(); return $this->view->render();
} }
@ -68,7 +70,9 @@ class HomeController extends \PHPCI\Controller
*/ */
public function latest() public function latest()
{ {
die($this->getLatestBuildsHtml()); $this->response->disableLayout();
$this->response->setContent($this->getLatestBuildsHtml());
return $this->response;
} }
/** /**
@ -76,8 +80,10 @@ class HomeController extends \PHPCI\Controller
*/ */
public function summary() public function summary()
{ {
$this->response->disableLayout();
$projects = $this->projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC')); $projects = $this->projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC'));
die($this->getSummaryHtml($projects)); $this->response->setContent($this->getSummaryHtml($projects));
return $this->response;
} }
/** /**
@ -88,12 +94,22 @@ class HomeController extends \PHPCI\Controller
protected function getSummaryHtml($projects) protected function getSummaryHtml($projects)
{ {
$summaryBuilds = array(); $summaryBuilds = array();
$successes = array(); $successes = array();
$failures = array(); $failures = array();
$counts = array();
foreach ($projects['items'] as $project) { foreach ($projects as $project) {
$summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId());
$count = $this->buildStore->getWhere(
array('project_id' => $project->getId()),
1,
0,
array(),
array('id' => 'DESC')
);
$counts[$project->getId()] = $count['count'];
$success = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_SUCCESS); $success = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_SUCCESS);
$failure = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_FAILED); $failure = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_FAILED);
@ -102,10 +118,11 @@ class HomeController extends \PHPCI\Controller
} }
$summaryView = new b8\View('SummaryTable'); $summaryView = new b8\View('SummaryTable');
$summaryView->projects = $projects['items']; $summaryView->projects = $projects;
$summaryView->builds = $summaryBuilds; $summaryView->builds = $summaryBuilds;
$summaryView->successful = $successes; $summaryView->successful = $successes;
$summaryView->failed = $failures; $summaryView->failed = $failures;
$summaryView->counts = $counts;
return $summaryView->render(); return $summaryView->render();
} }
@ -126,4 +143,24 @@ class HomeController extends \PHPCI\Controller
return $view->render(); return $view->render();
} }
/**
* Get a summary of the project groups we have, and what projects they have in them.
* @return array
*/
protected function getGroupInfo()
{
$rtn = array();
$groups = $this->groupStore->getWhere(array(), 100, 0, array(), array('title' => 'ASC'));
foreach ($groups['items'] as $group) {
$thisGroup = array('title' => $group->getTitle());
$projects = $this->projectStore->getByGroupId($group->getId());
$thisGroup['projects'] = $projects['items'];
$thisGroup['summary'] = $this->getSummaryHtml($thisGroup['projects']);
$rtn[] = $thisGroup;
}
return $rtn;
}
} }

View file

@ -11,7 +11,6 @@ namespace PHPCI\Controller;
use b8; use b8;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\Build;
use PHPCI\Plugin\Util\ComposerPluginInformation; use PHPCI\Plugin\Util\ComposerPluginInformation;
use PHPCI\Plugin\Util\FilesPluginInformation; use PHPCI\Plugin\Util\FilesPluginInformation;
use PHPCI\Plugin\Util\PluginInformationCollection; use PHPCI\Plugin\Util\PluginInformationCollection;
@ -24,24 +23,6 @@ use PHPCI\Plugin\Util\PluginInformationCollection;
*/ */
class PluginController extends \PHPCI\Controller class PluginController extends \PHPCI\Controller
{ {
protected $required = array(
'php',
'ext-pdo',
'ext-pdo_mysql',
'block8/b8framework',
'ircmaxell/password-compat',
'swiftmailer/swiftmailer',
'symfony/yaml',
'symfony/console',
'psr/log',
'monolog/monolog',
'pimple/pimple',
'robmorgan/phinx',
);
protected $canInstall;
protected $composerPath;
/** /**
* List all enabled plugins, installed and recommend packages. * List all enabled plugins, installed and recommend packages.
* @return string * @return string
@ -50,12 +31,8 @@ class PluginController extends \PHPCI\Controller
{ {
$this->requireAdmin(); $this->requireAdmin();
$this->view->canWrite = is_writable(APPLICATION_PATH . 'composer.json');
$this->view->required = $this->required;
$json = $this->getComposerJson(); $json = $this->getComposerJson();
$this->view->installedPackages = $json['require']; $this->view->installedPackages = $json['require'];
$this->view->suggestedPackages = $json['suggest'];
$pluginInfo = new PluginInformationCollection(); $pluginInfo = new PluginInformationCollection();
$pluginInfo->add(FilesPluginInformation::newFromDir( $pluginInfo->add(FilesPluginInformation::newFromDir(
@ -72,46 +49,6 @@ class PluginController extends \PHPCI\Controller
return $this->view->render(); return $this->view->render();
} }
/**
* Remove a given package.
*/
public function remove()
{
$this->requireAdmin();
$package = $this->getParam('package', null);
$json = $this->getComposerJson();
if (!in_array($package, $this->required)) {
unset($json['require'][$package]);
$this->setComposerJson($json);
header('Location: ' . PHPCI_URL . 'plugin?r=' . $package);
die;
}
header('Location: ' . PHPCI_URL);
die;
}
/**
* Install a given package.
*/
public function install()
{
$this->requireAdmin();
$package = $this->getParam('package', null);
$version = $this->getParam('version', '*');
$json = $this->getComposerJson();
$json['require'][$package] = $version;
$this->setComposerJson($json);
header('Location: ' . PHPCI_URL . 'plugin?w=' . $package);
die;
}
/** /**
* Get the json-decoded contents of the composer.json file. * Get the json-decoded contents of the composer.json file.
* @return mixed * @return mixed
@ -121,79 +58,4 @@ class PluginController extends \PHPCI\Controller
$json = file_get_contents(APPLICATION_PATH . 'composer.json'); $json = file_get_contents(APPLICATION_PATH . 'composer.json');
return json_decode($json, true); return json_decode($json, true);
} }
/**
* Convert array to json and save composer.json
*
* @param $array
*/
protected function setComposerJson($array)
{
if (defined('JSON_PRETTY_PRINT')) {
$json = json_encode($array, JSON_PRETTY_PRINT);
} else {
$json = json_encode($array);
}
file_put_contents(APPLICATION_PATH . 'composer.json', $json);
}
/**
* Find a system binary.
* @param $binary
* @return null|string
*/
protected function findBinary($binary)
{
if (is_string($binary)) {
$binary = array($binary);
}
foreach ($binary as $bin) {
// Check project root directory:
if (is_file(APPLICATION_PATH . $bin)) {
return APPLICATION_PATH . $bin;
}
// Check Composer bin dir:
if (is_file(APPLICATION_PATH . 'vendor/bin/' . $bin)) {
return APPLICATION_PATH . 'vendor/bin/' . $bin;
}
// Use "which"
$which = trim(shell_exec('which ' . $bin));
if (!empty($which)) {
return $which;
}
}
return null;
}
/**
* Perform a search on packagist.org.
*/
public function packagistSearch()
{
$searchQuery = $this->getParam('q', '');
$http = new \b8\HttpClient();
$http->setHeaders(array('User-Agent: PHPCI/1.0 (+https://www.phptesting.org)'));
$res = $http->get('https://packagist.org/search.json', array('q' => $searchQuery));
die(json_encode($res['body']));
}
/**
* Look up available versions of a given package on packagist.org
*/
public function packagistVersions()
{
$name = $this->getParam('p', '');
$http = new \b8\HttpClient();
$http->setHeaders(array('User-Agent: PHPCI/1.0 (+https://www.phptesting.org)'));
$res = $http->get('https://packagist.org/packages/'.$name.'.json');
die(json_encode($res['body']));
}
} }

View file

@ -10,17 +10,14 @@
namespace PHPCI\Controller; namespace PHPCI\Controller;
use b8; use b8;
use b8\Controller;
use b8\Form; use b8\Form;
use b8\Exception\HttpException\ForbiddenException;
use b8\Exception\HttpException\NotFoundException; use b8\Exception\HttpException\NotFoundException;
use b8\Store; use b8\Store;
use PHPCI;
use PHPCI\BuildFactory; use PHPCI\BuildFactory;
use PHPCI\Helper\Github; use PHPCI\Helper\Github;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Helper\SshKey; use PHPCI\Helper\SshKey;
use PHPCI\Model\Build;
use PHPCI\Model\Project;
use PHPCI\Service\BuildService; use PHPCI\Service\BuildService;
use PHPCI\Service\ProjectService; use PHPCI\Service\ProjectService;
@ -30,7 +27,7 @@ use PHPCI\Service\ProjectService;
* @package PHPCI * @package PHPCI
* @subpackage Web * @subpackage Web
*/ */
class ProjectController extends \PHPCI\Controller class ProjectController extends PHPCI\Controller
{ {
/** /**
* @var \PHPCI\Store\ProjectStore * @var \PHPCI\Store\ProjectStore
@ -81,14 +78,15 @@ class ProjectController extends \PHPCI\Controller
$pages = $builds[1] == 0 ? 1 : ceil($builds[1] / $per_page); $pages = $builds[1] == 0 ? 1 : ceil($builds[1] / $per_page);
if ($page > $pages) { if ($page > $pages) {
header('Location: '.PHPCI_URL.'project/view/'.$projectId); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL.'project/view/'.$projectId);
return $response;
} }
$this->view->builds = $builds[0]; $this->view->builds = $builds[0];
$this->view->total = $builds[1]; $this->view->total = $builds[1];
$this->view->project = $project; $this->view->project = $project;
$this->view->branch = urldecode($branch); $this->view->branch = urldecode($branch);
$this->view->branches = $this->projectStore->getKnownBranches($projectId); $this->view->branches = $this->projectStore->getKnownBranches($projectId);
$this->view->page = $page; $this->view->page = $page;
$this->view->pages = $pages; $this->view->pages = $pages;
@ -118,8 +116,13 @@ class ProjectController extends \PHPCI\Controller
$email = $_SESSION['phpci_user']->getEmail(); $email = $_SESSION['phpci_user']->getEmail();
$build = $this->buildService->createBuild($project, null, urldecode($branch), $email); $build = $this->buildService->createBuild($project, null, urldecode($branch), $email);
header('Location: '.PHPCI_URL.'build/view/' . $build->getId()); if ($this->buildService->queueError) {
exit; $_SESSION['global_error'] = Lang::get('add_to_queue_failed');
}
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', PHPCI_URL.'build/view/' . $build->getId());
return $response;
} }
/** /**
@ -132,8 +135,9 @@ class ProjectController extends \PHPCI\Controller
$project = $this->projectStore->getById($projectId); $project = $this->projectStore->getById($projectId);
$this->projectService->deleteProject($project); $this->projectService->deleteProject($project);
header('Location: '.PHPCI_URL); $response = new b8\Http\Response\RedirectResponse();
exit; $response->setHeader('Location', PHPCI_URL);
return $response;
} }
/** /**
@ -143,7 +147,10 @@ class ProjectController extends \PHPCI\Controller
{ {
$branch = $this->getParam('branch', ''); $branch = $this->getParam('branch', '');
$builds = $this->getLatestBuildsHtml($projectId, urldecode($branch)); $builds = $this->getLatestBuildsHtml($projectId, urldecode($branch));
die($builds[0]);
$this->response->disableLayout();
$this->response->setContent($builds[0]);
return $this->response;
} }
/** /**
@ -217,11 +224,14 @@ class ProjectController extends \PHPCI\Controller
'build_config' => $this->getParam('build_config', null), 'build_config' => $this->getParam('build_config', null),
'allow_public_status' => $this->getParam('allow_public_status', 0), 'allow_public_status' => $this->getParam('allow_public_status', 0),
'branch' => $this->getParam('branch', null), 'branch' => $this->getParam('branch', null),
'group' => $this->getParam('group_id', null),
); );
$project = $this->projectService->createProject($title, $type, $reference, $options); $project = $this->projectService->createProject($title, $type, $reference, $options);
header('Location: '.PHPCI_URL.'project/view/' . $project->getId());
die; $response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', PHPCI_URL.'project/view/' . $project->getId());
return $response;
} }
} }
@ -263,7 +273,7 @@ class ProjectController extends \PHPCI\Controller
$view->type = 'edit'; $view->type = 'edit';
$view->project = $project; $view->project = $project;
$view->form = $form; $view->form = $form;
$view->key = null; $view->key = $values['pubkey'];
return $view->render(); return $view->render();
} }
@ -277,13 +287,16 @@ class ProjectController extends \PHPCI\Controller
'ssh_public_key' => $this->getParam('pubkey', null), 'ssh_public_key' => $this->getParam('pubkey', null),
'build_config' => $this->getParam('build_config', null), 'build_config' => $this->getParam('build_config', null),
'allow_public_status' => $this->getParam('allow_public_status', 0), 'allow_public_status' => $this->getParam('allow_public_status', 0),
'archived' => $this->getParam('archived', 0),
'branch' => $this->getParam('branch', null), 'branch' => $this->getParam('branch', null),
'group' => $this->getParam('group_id', null),
); );
$project = $this->projectService->updateProject($project, $title, $type, $reference, $options); $project = $this->projectService->updateProject($project, $title, $type, $reference, $options);
header('Location: '.PHPCI_URL.'project/view/' . $project->getId()); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL.'project/view/' . $project->getId());
return $response;
} }
/** /**
@ -305,10 +318,11 @@ class ProjectController extends \PHPCI\Controller
'remote' => Lang::get('remote'), 'remote' => Lang::get('remote'),
'local' => Lang::get('local'), 'local' => Lang::get('local'),
'hg' => Lang::get('hg'), 'hg' => Lang::get('hg'),
'svn' => Lang::get('svn'),
); );
$field = Form\Element\Select::create('type', Lang::get('where_hosted'), true); $field = Form\Element\Select::create('type', Lang::get('where_hosted'), true);
$field->setPattern('^(github|bitbucket|gitlab|remote|local|hg)'); $field->setPattern('^(github|bitbucket|gitlab|remote|local|hg|svn)');
$field->setOptions($options); $field->setOptions($options);
$field->setClass('form-control')->setContainerClass('form-group'); $field->setClass('form-control')->setContainerClass('form-group');
$form->addField($field); $form->addField($field);
@ -344,12 +358,32 @@ class ProjectController extends \PHPCI\Controller
$field->setClass('form-control')->setContainerClass('form-group')->setValue('master'); $field->setClass('form-control')->setContainerClass('form-group')->setValue('master');
$form->addField($field); $form->addField($field);
$field = Form\Element\Select::create('group_id', 'Project Group', true);
$field->setClass('form-control')->setContainerClass('form-group')->setValue(1);
$groups = array();
$groupStore = b8\Store\Factory::getStore('ProjectGroup');
$groupList = $groupStore->getWhere(array(), 100, 0, array(), array('title' => 'ASC'));
foreach ($groupList['items'] as $group) {
$groups[$group->getId()] = $group->getTitle();
}
$field->setOptions($groups);
$form->addField($field);
$field = Form\Element\Checkbox::create('allow_public_status', Lang::get('allow_public_status'), false); $field = Form\Element\Checkbox::create('allow_public_status', Lang::get('allow_public_status'), false);
$field->setContainerClass('form-group'); $field->setContainerClass('form-group');
$field->setCheckedValue(1); $field->setCheckedValue(1);
$field->setValue(0); $field->setValue(0);
$form->addField($field); $form->addField($field);
$field = Form\Element\Checkbox::create('archived', Lang::get('archived'), false);
$field->setContainerClass('form-group');
$field->setCheckedValue(1);
$field->setValue(0);
$form->addField($field);
$field = new Form\Element\Submit(); $field = new Form\Element\Submit();
$field->setValue(Lang::get('save_project')); $field->setValue(Lang::get('save_project'));
$field->setContainerClass('form-group'); $field->setContainerClass('form-group');
@ -366,7 +400,10 @@ class ProjectController extends \PHPCI\Controller
protected function githubRepositories() protected function githubRepositories()
{ {
$github = new Github(); $github = new Github();
die(json_encode($github->getRepositories()));
$response = new b8\Http\Response\JsonResponse();
$response->setContent($github->getRepositories());
return $response;
} }
/** /**

View file

@ -43,14 +43,23 @@ class SessionController extends \PHPCI\Controller
$isLoginFailure = false; $isLoginFailure = false;
if ($this->request->getMethod() == 'POST') { if ($this->request->getMethod() == 'POST') {
$user = $this->userStore->getByEmail($this->getParam('email')); $token = $this->getParam('token');
if (!isset($token, $_SESSION['login_token']) || $token !== $_SESSION['login_token']) {
if ($user && password_verify($this->getParam('password', ''), $user->getHash())) {
$_SESSION['phpci_user_id'] = $user->getId();
header('Location: ' . $this->getLoginRedirect());
die;
} else {
$isLoginFailure = true; $isLoginFailure = true;
} else {
unset($_SESSION['login_token']);
$user = $this->userStore->getByEmail($this->getParam('email'));
if ($user && password_verify($this->getParam('password', ''), $user->getHash())) {
session_regenerate_id(true);
$_SESSION['phpci_user_id'] = $user->getId();
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', $this->getLoginRedirect());
return $response;
} else {
$isLoginFailure = true;
}
} }
} }
@ -77,6 +86,12 @@ class SessionController extends \PHPCI\Controller
$pwd->setClass('btn-success'); $pwd->setClass('btn-success');
$form->addField($pwd); $form->addField($pwd);
$tokenValue = $this->generateToken();
$_SESSION['login_token'] = $tokenValue;
$token = new b8\Form\Element\Hidden('token');
$token->setValue($tokenValue);
$form->addField($token);
$this->view->form = $form->render(); $this->view->form = $form->render();
$this->view->failed = $isLoginFailure; $this->view->failed = $isLoginFailure;
@ -92,8 +107,10 @@ class SessionController extends \PHPCI\Controller
unset($_SESSION['phpci_user_id']); unset($_SESSION['phpci_user_id']);
session_destroy(); session_destroy();
header('Location: ' . PHPCI_URL);
die; $response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', PHPCI_URL);
return $response;
} }
/** /**
@ -151,8 +168,9 @@ class SessionController extends \PHPCI\Controller
$_SESSION['phpci_user'] = $this->userStore->save($user); $_SESSION['phpci_user'] = $this->userStore->save($user);
$_SESSION['phpci_user_id'] = $user->getId(); $_SESSION['phpci_user_id'] = $user->getId();
header('Location: ' . PHPCI_URL); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL);
return $response;
} }
$this->view->id = $userId; $this->view->id = $userId;
@ -176,4 +194,20 @@ class SessionController extends \PHPCI\Controller
return $rtn; return $rtn;
} }
/** Generate a random token.
*
* @return string
*/
protected function generateToken()
{
if (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes(16));
}
return sprintf("%04x", mt_rand(0, 0xFFFF))
. sprintf("%04x", mt_rand(0, 0xFFFF))
. sprintf("%04x", mt_rand(0, 0xFFFF))
. sprintf("%04x", mt_rand(0, 0xFFFF));
}
} }

View file

@ -14,7 +14,6 @@ use b8\Form;
use b8\HttpClient; use b8\HttpClient;
use PHPCI\Controller; use PHPCI\Controller;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\Build;
use Symfony\Component\Yaml\Dumper; use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Parser;
@ -41,7 +40,7 @@ class SettingsController extends Controller
parent::init(); parent::init();
$parser = new Parser(); $parser = new Parser();
$yaml = file_get_contents(APPLICATION_PATH . 'PHPCI/config.yml'); $yaml = file_get_contents(PHPCI_CONFIG_FILE);
$this->settings = $parser->parse($yaml); $this->settings = $parser->parse($yaml);
} }
@ -77,6 +76,7 @@ class SettingsController extends Controller
$authSettings = $this->settings['phpci']['authentication_settings']; $authSettings = $this->settings['phpci']['authentication_settings'];
} }
$this->view->configFile = PHPCI_CONFIG_FILE;
$this->view->basicSettings = $this->getBasicForm($basicSettings); $this->view->basicSettings = $this->getBasicForm($basicSettings);
$this->view->buildSettings = $this->getBuildForm($buildSettings); $this->view->buildSettings = $this->getBuildForm($buildSettings);
$this->view->github = $this->getGithubForm(); $this->view->github = $this->getGithubForm();
@ -102,13 +102,15 @@ class SettingsController extends Controller
$this->settings['phpci']['github']['secret'] = $this->getParam('githubsecret', ''); $this->settings['phpci']['github']['secret'] = $this->getParam('githubsecret', '');
$error = $this->storeSettings(); $error = $this->storeSettings();
$response = new b8\Http\Response\RedirectResponse();
if ($error) { if ($error) {
header('Location: ' . PHPCI_URL . 'settings?saved=2'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=2');
} else { } else {
header('Location: ' . PHPCI_URL . 'settings?saved=1'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=1');
} }
die; return $response;
} }
/** /**
@ -123,13 +125,15 @@ class SettingsController extends Controller
$error = $this->storeSettings(); $error = $this->storeSettings();
$response = new b8\Http\Response\RedirectResponse();
if ($error) { if ($error) {
header('Location: ' . PHPCI_URL . 'settings?saved=2'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=2');
} else { } else {
header('Location: ' . PHPCI_URL . 'settings?saved=1'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=1');
} }
die; return $response;
} }
/** /**
@ -143,13 +147,15 @@ class SettingsController extends Controller
$error = $this->storeSettings(); $error = $this->storeSettings();
$response = new b8\Http\Response\RedirectResponse();
if ($error) { if ($error) {
header('Location: ' . PHPCI_URL . 'settings?saved=2'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=2');
} else { } else {
header('Location: ' . PHPCI_URL . 'settings?saved=1'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=1');
} }
die; return $response;
} }
/** /**
@ -162,13 +168,15 @@ class SettingsController extends Controller
$this->settings['phpci']['basic'] = $this->getParams(); $this->settings['phpci']['basic'] = $this->getParams();
$error = $this->storeSettings(); $error = $this->storeSettings();
$response = new b8\Http\Response\RedirectResponse();
if ($error) { if ($error) {
header('Location: ' . PHPCI_URL . 'settings?saved=2'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=2');
} else { } else {
header('Location: ' . PHPCI_URL . 'settings?saved=1'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=1');
} }
die; return $response;
} }
/** /**
@ -183,13 +191,15 @@ class SettingsController extends Controller
$error = $this->storeSettings(); $error = $this->storeSettings();
$response = new b8\Http\Response\RedirectResponse();
if ($error) { if ($error) {
header('Location: ' . PHPCI_URL . 'settings?saved=2'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=2');
} else { } else {
header('Location: ' . PHPCI_URL . 'settings?saved=1'); $response->setHeader('Location', PHPCI_URL . 'settings?saved=1');
} }
die; return $response;
} }
/** /**
@ -212,14 +222,15 @@ class SettingsController extends Controller
$this->settings['phpci']['github']['token'] = $resp['access_token']; $this->settings['phpci']['github']['token'] = $resp['access_token'];
$this->storeSettings(); $this->storeSettings();
header('Location: ' . PHPCI_URL . 'settings?linked=1'); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL . 'settings?linked=1');
return $response;
} }
} }
$response = new b8\Http\Response\RedirectResponse();
header('Location: ' . PHPCI_URL . 'settings?linked=2'); $response->setHeader('Location', PHPCI_URL . 'settings?linked=2');
die; return $response;
} }
/** /**
@ -231,7 +242,7 @@ class SettingsController extends Controller
{ {
$dumper = new Dumper(); $dumper = new Dumper();
$yaml = $dumper->dump($this->settings, 4); $yaml = $dumper->dump($this->settings, 4);
file_put_contents(APPLICATION_PATH . 'PHPCI/config.yml', $yaml); file_put_contents(PHPCI_CONFIG_FILE, $yaml);
if (error_get_last()) { if (error_get_last()) {
$error_get_last = error_get_last(); $error_get_last = error_get_last();
@ -318,7 +329,7 @@ class SettingsController extends Controller
$field->setContainerClass('form-group'); $field->setContainerClass('form-group');
$form->addField($field); $form->addField($field);
$field = new Form\Element\Text('smtp_password'); $field = new Form\Element\Password('smtp_password');
$field->setRequired(false); $field->setRequired(false);
$field->setLabel(Lang::get('smtp_password')); $field->setLabel(Lang::get('smtp_password'));
$field->setClass('form-control'); $field->setClass('form-control');
@ -340,7 +351,7 @@ class SettingsController extends Controller
$form->addField($field); $form->addField($field);
$field = new Form\Element\Select('smtp_encryption'); $field = new Form\Element\Select('smtp_encryption');
$field->setOptions(['' => Lang::get('none'), 'tls' => Lang::get('tls'), 'ssl' => Lang::get('ssl')]); $field->setOptions(array('' => Lang::get('none'), 'tls' => Lang::get('tls'), 'ssl' => Lang::get('ssl')));
$field->setRequired(false); $field->setRequired(false);
$field->setLabel(Lang::get('use_smtp_encryption')); $field->setLabel(Lang::get('use_smtp_encryption'));
$field->setContainerClass('form-group'); $field->setContainerClass('form-group');
@ -376,7 +387,7 @@ class SettingsController extends Controller
*/ */
protected function canWriteConfig() protected function canWriteConfig()
{ {
return is_writeable(APPLICATION_PATH . 'PHPCI/config.yml'); return is_writeable(PHPCI_CONFIG_FILE);
} }
/** /**
@ -395,13 +406,13 @@ class SettingsController extends Controller
$field->setLabel(Lang::get('failed_after')); $field->setLabel(Lang::get('failed_after'));
$field->setClass('form-control'); $field->setClass('form-control');
$field->setContainerClass('form-group'); $field->setContainerClass('form-group');
$field->setOptions([ $field->setOptions(array(
300 => Lang::get('5_mins'), 300 => Lang::get('5_mins'),
900 => Lang::get('15_mins'), 900 => Lang::get('15_mins'),
1800 => Lang::get('30_mins'), 1800 => Lang::get('30_mins'),
3600 => Lang::get('1_hour'), 3600 => Lang::get('1_hour'),
10800 => Lang::get('3_hours'), 10800 => Lang::get('3_hours'),
]); ));
$field->setValue(1800); $field->setValue(1800);
$form->addField($field); $form->addField($field);
@ -433,7 +444,7 @@ class SettingsController extends Controller
$field->setClass('form-control'); $field->setClass('form-control');
$field->setContainerClass('form-group'); $field->setContainerClass('form-group');
$field->setOptions(Lang::getLanguageOptions()); $field->setOptions(Lang::getLanguageOptions());
$field->setValue('en'); $field->setValue(Lang::getLanguage());
$form->addField($field); $form->addField($field);

View file

@ -10,12 +10,10 @@
namespace PHPCI\Controller; namespace PHPCI\Controller;
use b8; use b8;
use b8\Exception\HttpException\ForbiddenException;
use b8\Exception\HttpException\NotFoundException; use b8\Exception\HttpException\NotFoundException;
use b8\Form; use b8\Form;
use PHPCI\Controller; use PHPCI\Controller;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\User;
use PHPCI\Service\UserService; use PHPCI\Service\UserService;
/** /**
@ -125,6 +123,7 @@ class UserController extends Controller
$lang->setLabel(Lang::get('language')); $lang->setLabel(Lang::get('language'));
$lang->setRequired(true); $lang->setRequired(true);
$lang->setOptions(Lang::getLanguageOptions()); $lang->setOptions(Lang::getLanguageOptions());
$lang->setValue(Lang::getLanguage());
$form->addField($lang); $form->addField($lang);
$submit = new Form\Element\Submit(); $submit = new Form\Element\Submit();
@ -175,8 +174,9 @@ class UserController extends Controller
$this->userService->createUser($name, $email, $password, $isAdmin); $this->userService->createUser($name, $email, $password, $isAdmin);
header('Location: '.PHPCI_URL.'user'); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL . 'user');
return $response;
} }
/** /**
@ -215,8 +215,9 @@ class UserController extends Controller
$this->userService->updateUser($user, $name, $email, $password, $isAdmin); $this->userService->updateUser($user, $name, $email, $password, $isAdmin);
header('Location: '.PHPCI_URL.'user'); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL . 'user');
return $response;
} }
/** /**
@ -288,7 +289,8 @@ class UserController extends Controller
$this->userService->deleteUser($user); $this->userService->deleteUser($user);
header('Location: '.PHPCI_URL.'user'); $response = new b8\Http\Response\RedirectResponse();
die; $response->setHeader('Location', PHPCI_URL . 'user');
return $response;
} }
} }

View file

@ -2,7 +2,7 @@
/** /**
* PHPCI - Continuous Integration for PHP * PHPCI - Continuous Integration for PHP
* *
* @copyright Copyright 2014, Block 8 Limited. * @copyright Copyright 2014-2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md * @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/ * @link https://www.phptesting.org/
*/ */
@ -11,32 +11,39 @@ namespace PHPCI\Controller;
use b8; use b8;
use b8\Store; use b8\Store;
use Exception;
use PHPCI\BuildFactory; use PHPCI\BuildFactory;
use PHPCI\Model\Build; use PHPCI\Model\Project;
use PHPCI\Service\BuildService; use PHPCI\Service\BuildService;
use PHPCI\Store\BuildStore;
use PHPCI\Store\ProjectStore;
/** /**
* Webhook Controller - Processes webhook pings from BitBucket, Github, Gitlab, etc. * Webhook Controller - Processes webhook pings from BitBucket, Github, Gitlab, etc.
*
* @author Dan Cryer <dan@block8.co.uk> * @author Dan Cryer <dan@block8.co.uk>
* @author Sami Tikka <stikka@iki.fi> * @author Sami Tikka <stikka@iki.fi>
* @author Alex Russell <alex@clevercherry.com> * @author Alex Russell <alex@clevercherry.com>
* @author Guillaume Perréal <adirelle@gmail.com>
* @package PHPCI * @package PHPCI
* @subpackage Web * @subpackage Web
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
*/ */
class WebhookController extends \PHPCI\Controller class WebhookController extends \b8\Controller
{ {
/** /**
* @var \PHPCI\Store\BuildStore * @var BuildStore
*/ */
protected $buildStore; protected $buildStore;
/** /**
* @var \PHPCI\Store\ProjectStore * @var ProjectStore
*/ */
protected $projectStore; protected $projectStore;
/** /**
* @var \PHPCI\Service\BuildService * @var BuildService
*/ */
protected $buildService; protected $buildService;
@ -50,86 +57,147 @@ class WebhookController extends \PHPCI\Controller
$this->buildService = new BuildService($this->buildStore); $this->buildService = new BuildService($this->buildStore);
} }
/** /** Handle the action, Ensuring to return a JsonResponse.
* Called by Bitbucket POST service. *
* @param string $action
* @param mixed $actionParams
*
* @return \b8\Http\Response
*/ */
public function bitbucket($project) public function handleAction($action, $actionParams)
{
$response = new b8\Http\Response\JsonResponse();
try {
$data = parent::handleAction($action, $actionParams);
if (isset($data['responseCode'])) {
$response->setResponseCode($data['responseCode']);
unset($data['responseCode']);
}
$response->setContent($data);
} catch (Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
}
return $response;
}
/**
* Called by Bitbucket.
*/
public function bitbucket($projectId)
{
$project = $this->fetchProject($projectId, 'bitbucket');
// Support both old services and new webhooks
if ($payload = $this->getParam('payload')) {
return $this->bitbucketService(json_decode($payload, true), $project);
}
$payload = json_decode(file_get_contents("php://input"), true);
if (empty($payload['push']['changes'])) {
// Invalid event from bitbucket
return [
'status' => 'failed',
'commits' => []
];
}
return $this->bitbucketWebhook($payload, $project);
}
/**
* Bitbucket webhooks.
*/
protected function bitbucketWebhook($payload, $project)
{
$results = array();
$status = 'failed';
foreach ($payload['push']['changes'] as $commit) {
try {
$email = $commit['new']['target']['author']['raw'];
$email = substr($email, 0, strpos($email, '>'));
$email = substr($email, strpos($email, '<') + 1);
$results[$commit['new']['target']['hash']] = $this->createBuild(
$project,
$commit['new']['target']['hash'],
$commit['new']['name'],
$email,
$commit['new']['target']['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['new']['target']['hash']] = array('status' => 'failed', 'error' => $ex->getMessage());
}
}
return array('status' => $status, 'commits' => $results);
}
/**
* Bitbucket POST service.
*/
protected function bitbucketService($payload, $project)
{ {
$payload = json_decode($this->getParam('payload'), true); $payload = json_decode($this->getParam('payload'), true);
$results = array();
$status = 'failed';
foreach ($payload['commits'] as $commit) { foreach ($payload['commits'] as $commit) {
try { try {
$email = $commit['raw_author']; $email = $commit['raw_author'];
$email = substr($email, 0, strpos($email, '>')); $email = substr($email, 0, strpos($email, '>'));
$email = substr($email, strpos($email, '<') + 1); $email = substr($email, strpos($email, '<') + 1);
$this->createBuild($project, $commit['raw_node'], $commit['branch'], $email, $commit['message']); $results[$commit['raw_node']] = $this->createBuild(
} catch (\Exception $ex) { $project,
header('HTTP/1.1 500 Internal Server Error'); $commit['raw_node'],
header('Ex: ' . $ex->getMessage()); $commit['branch'],
die('FAIL'); $email,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['raw_node']] = array('status' => 'failed', 'error' => $ex->getMessage());
} }
} }
die('OK'); return array('status' => $status, 'commits' => $results);
} }
/** /**
* Called by POSTing to /webhook/git/<project_id>?branch=<branch>&commit=<commit> * Called by POSTing to /webhook/git/<project_id>?branch=<branch>&commit=<commit>
* *
* @param string $project * @param string $projectId
*/ */
public function git($project) public function git($projectId)
{ {
$branch = $this->getParam('branch'); $project = $this->fetchProject($projectId, array('local', 'remote'));
$branch = $this->getParam('branch', $project->getBranch());
$commit = $this->getParam('commit'); $commit = $this->getParam('commit');
$commitMessage = $this->getParam('message'); $commitMessage = $this->getParam('message');
$committer = $this->getParam('committer'); $committer = $this->getParam('committer');
try { return $this->createBuild($project, $commit, $branch, $committer, $commitMessage);
if (empty($branch)) {
$branch = 'master';
}
if (empty($commit)) {
$commit = null;
}
if (empty($commitMessage)) {
$commitMessage = null;
}
if (empty($committer)) {
$committer = null;
}
$this->createBuild($project, $commit, $branch, $committer, $commitMessage);
} catch (\Exception $ex) {
header('HTTP/1.1 400 Bad Request');
header('Ex: ' . $ex->getMessage());
die('FAIL');
}
die('OK');
} }
/** /**
* Called by Github Webhooks: * Called by Github Webhooks:
*/ */
public function github($project) public function github($projectId)
{ {
$project = $this->fetchProject($projectId, 'github');
switch ($_SERVER['CONTENT_TYPE']) { switch ($_SERVER['CONTENT_TYPE']) {
case 'application/json': case 'application/json':
$payload = json_decode(file_get_contents('php://input'), true); $payload = json_decode(file_get_contents('php://input'), true);
break; break;
case 'application/x-www-form-urlencoded': case 'application/x-www-form-urlencoded':
$payload = json_decode($this->getParam('payload'), true); $payload = json_decode($this->getParam('payload'), true);
break; break;
default: default:
header('HTTP/1.1 400 Bad Request'); return array('status' => 'failed', 'error' => 'Content type not supported.', 'responseCode' => 401);
die('Request content type not supported');
} }
// Handle Pull Request web hooks: // Handle Pull Request web hooks:
@ -142,187 +210,244 @@ class WebhookController extends \PHPCI\Controller
return $this->githubCommitRequest($project, $payload); return $this->githubCommitRequest($project, $payload);
} }
header('HTTP/1.1 200 OK'); return array('status' => 'ignored', 'message' => 'Unusable payload.');
die('This request type is not supported, this is not an error.');
} }
/** /**
* Handle the payload when Github sends a commit webhook. * Handle the payload when Github sends a commit webhook.
* @param $project *
* @param Project $project
* @param array $payload * @param array $payload
* @param b8\Http\Response\JsonResponse $response
*
* @return b8\Http\Response\JsonResponse
*/ */
protected function githubCommitRequest($project, array $payload) protected function githubCommitRequest(Project $project, array $payload)
{ {
// Github sends a payload when you close a pull request with a // Github sends a payload when you close a pull request with a
// non-existant commit. We don't want this. // non-existent commit. We don't want this.
if (array_key_exists('after', $payload) && $payload['after'] === '0000000000000000000000000000000000000000') { if (array_key_exists('after', $payload) && $payload['after'] === '0000000000000000000000000000000000000000') {
die('OK'); return array('status' => 'ignored');
} }
try { if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
if (isset($payload['commits']) && is_array($payload['commits'])) { $results = array();
// If we have a list of commits, then add them all as builds to be tested: $status = 'failed';
foreach ($payload['commits'] as $commit) {
foreach ($payload['commits'] as $commit) { if (!$commit['distinct']) {
if (!$commit['distinct']) { $results[$commit['id']] = array('status' => 'ignored');
continue; continue;
} }
try {
$branch = str_replace('refs/heads/', '', $payload['ref']); $branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['committer']['email']; $committer = $commit['committer']['email'];
$this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']); $results[$commit['id']] = $this->createBuild(
$project,
$commit['id'],
$branch,
$committer,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['id']] = array('status' => 'failed', 'error' => $ex->getMessage());
} }
} elseif (substr($payload['ref'], 0, 10) == 'refs/tags/') {
// If we don't, but we're dealing with a tag, add that instead:
$branch = str_replace('refs/tags/', 'Tag: ', $payload['ref']);
$committer = $payload['pusher']['email'];
$message = $payload['head_commit']['message'];
$this->createBuild($project, $payload['after'], $branch, $committer, $message);
} }
return array('status' => $status, 'commits' => $results);
} catch (\Exception $ex) {
header('HTTP/1.1 500 Internal Server Error');
header('Ex: ' . $ex->getMessage());
die('FAIL');
} }
die('OK'); if (substr($payload['ref'], 0, 10) == 'refs/tags/') {
// If we don't, but we're dealing with a tag, add that instead:
$branch = str_replace('refs/tags/', 'Tag: ', $payload['ref']);
$committer = $payload['pusher']['email'];
$message = $payload['head_commit']['message'];
return $this->createBuild($project, $payload['after'], $branch, $committer, $message);
}
return array('status' => 'ignored', 'message' => 'Unusable payload.');
} }
/** /**
* Handle the payload when Github sends a Pull Request webhook. * Handle the payload when Github sends a Pull Request webhook.
* @param $projectId *
* @param Project $project
* @param array $payload * @param array $payload
*/ */
protected function githubPullRequest($projectId, array $payload) protected function githubPullRequest(Project $project, array $payload)
{ {
// We only want to know about open pull requests: // We only want to know about open pull requests:
if (!in_array($payload['action'], array('opened', 'synchronize', 'reopened'))) { if (!in_array($payload['action'], array('opened', 'synchronize', 'reopened'))) {
die('OK'); return array('status' => 'ok');
} }
try { $headers = array();
$headers = array(); $token = \b8\Config::getInstance()->get('phpci.github.token');
$token = \b8\Config::getInstance()->get('phpci.github.token');
if (!empty($token)) { if (!empty($token)) {
$headers[] = 'Authorization: token ' . $token; $headers[] = 'Authorization: token ' . $token;
}
$url = $payload['pull_request']['commits_url'];
$http = new \b8\HttpClient();
$http->setHeaders($headers);
$response = $http->get($url);
// Check we got a success response:
if (!$response['success']) {
throw new Exception('Could not get commits, failed API request.');
}
$results = array();
$status = 'failed';
foreach ($response['body'] as $commit) {
// Skip all but the current HEAD commit ID:
$id = $commit['sha'];
if ($id != $payload['pull_request']['head']['sha']) {
$results[$id] = array('status' => 'ignored', 'message' => 'not branch head');
continue;
} }
$url = $payload['pull_request']['commits_url']; try {
$http = new \b8\HttpClient();
$http->setHeaders($headers);
$response = $http->get($url);
// Check we got a success response:
if (!$response['success']) {
header('HTTP/1.1 500 Internal Server Error');
header('Ex: Could not get commits, failed API request.');
die('FAIL');
}
foreach ($response['body'] as $commit) {
$branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']); $branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']);
$committer = $commit['commit']['author']['email']; $committer = $commit['commit']['author']['email'];
$message = $commit['commit']['message']; $message = $commit['commit']['message'];
$remoteUrlKey = $payload['pull_request']['head']['repo']['private'] ? 'ssh_url' : 'clone_url';
$extra = array( $extra = array(
'build_type' => 'pull_request', 'build_type' => 'pull_request',
'pull_request_id' => $payload['pull_request']['id'], 'pull_request_id' => $payload['pull_request']['id'],
'pull_request_number' => $payload['number'], 'pull_request_number' => $payload['number'],
'remote_branch' => $payload['pull_request']['head']['ref'], 'remote_branch' => $payload['pull_request']['head']['ref'],
'remote_url' => $payload['pull_request']['head']['repo']['clone_url'], 'remote_url' => $payload['pull_request']['head']['repo'][$remoteUrlKey],
); );
$this->createBuild($projectId, $commit['sha'], $branch, $committer, $message, $extra); $results[$id] = $this->createBuild($project, $id, $branch, $committer, $message, $extra);
$status = 'ok';
} catch (Exception $ex) {
$results[$id] = array('status' => 'failed', 'error' => $ex->getMessage());
} }
} catch (\Exception $ex) {
header('HTTP/1.1 500 Internal Server Error');
header('Ex: ' . $ex->getMessage());
die('FAIL');
} }
die('OK'); return array('status' => $status, 'commits' => $results);
} }
/** /**
* Called by Gitlab Webhooks: * Called by Gitlab Webhooks:
*/ */
public function gitlab($project) public function gitlab($projectId)
{ {
$project = $this->fetchProject($projectId, 'gitlab');
$payloadString = file_get_contents("php://input"); $payloadString = file_get_contents("php://input");
$payload = json_decode($payloadString, true); $payload = json_decode($payloadString, true);
try { // build on merge request events
if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') {
$attributes = $payload['object_attributes'];
if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') {
$branch = $attributes['source_branch'];
$commit = $attributes['last_commit'];
$committer = $commit['author']['email'];
return $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
// build on merge request events
if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') {
$attributes = $payload['object_attributes'];
if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') {
$branch = $attributes['source_branch'];
$commit = $attributes['last_commit'];
$committer = $commit['author']['email'];
$this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
}
} }
// build on push events
if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
foreach ($payload['commits'] as $commit) {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['author']['email'];
$this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
}
}
} catch (\Exception $ex) {
header('HTTP/1.1 500 Internal Server Error');
header('Ex: ' . $ex->getMessage());
die('FAIL');
} }
die('OK'); // build on push events
if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
$results = array();
$status = 'failed';
foreach ($payload['commits'] as $commit) {
try {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['author']['email'];
$results[$commit['id']] = $this->createBuild(
$project,
$commit['id'],
$branch,
$committer,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['id']] = array('status' => 'failed', 'error' => $ex->getMessage());
}
}
return array('status' => $status, 'commits' => $results);
}
return array('status' => 'ignored', 'message' => 'Unusable payload.');
} }
/** /**
* Wrapper for creating a new build. * Wrapper for creating a new build.
* @param $projectId *
* @param $commitId * @param Project $project
* @param $branch * @param string $commitId
* @param $committer * @param string $branch
* @param $commitMessage * @param string $committer
* @param null $extra * @param string $commitMessage
* @return bool * @param array $extra
* @throws \Exception *
* @return array
*
* @throws Exception
*/ */
protected function createBuild($projectId, $commitId, $branch, $committer, $commitMessage, $extra = null) protected function createBuild(
{ Project $project,
$commitId,
$branch,
$committer,
$commitMessage,
array $extra = null
) {
// Check if a build already exists for this commit ID: // Check if a build already exists for this commit ID:
$builds = $this->buildStore->getByProjectAndCommit($projectId, $commitId); $builds = $this->buildStore->getByProjectAndCommit($project->getId(), $commitId);
if ($builds['count']) { if ($builds['count']) {
return true; return array(
} 'status' => 'ignored',
'message' => sprintf('Duplicate of build #%d', $builds['items'][0]->getId())
$project = $this->projectStore->getById($projectId); );
if (empty($project)) {
throw new \Exception('Project does not exist:' . $projectId);
} }
// If not, create a new build job for it: // If not, create a new build job for it:
$build = $this->buildService->createBuild($project, $commitId, $branch, $committer, $commitMessage, $extra); $build = $this->buildService->createBuild($project, $commitId, $branch, $committer, $commitMessage, $extra);
$build = BuildFactory::getBuild($build);
// Send a status postback if the build type provides one: return array('status' => 'ok', 'buildID' => $build->getID());
$build->sendStatusPostback(); }
return true; /**
* Fetch a project and check its type.
*
* @param int $projectId
* @param array|string $expectedType
*
* @return Project
*
* @throws Exception If the project does not exist or is not of the expected type.
*/
protected function fetchProject($projectId, $expectedType)
{
$project = $this->projectStore->getById($projectId);
if (empty($projectId)) {
throw new Exception('Project does not exist: ' . $projectId);
}
if (is_array($expectedType)
? !in_array($project->getType(), $expectedType)
: $project->getType() !== $expectedType
) {
throw new Exception('Wrong project type: ' . $project->getType());
}
return $project;
} }
} }

68
PHPCI/ErrorHandler.php Normal file
View file

@ -0,0 +1,68 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI;
/**
* Error Handler
*
* @package PHPCI\Logging
*/
class ErrorHandler
{
/**
* @var array
*/
protected $levels = array(
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
);
/**
* Registers an instance of the error handler to throw ErrorException.
*/
public static function register()
{
$handler = new static();
set_error_handler(array($handler, 'handleError'));
}
/**
* @param integer $level
* @param string $message
* @param string $file
* @param integer $line
*
* @throws \ErrorException
*
* @internal
*/
public function handleError($level, $message, $file, $line)
{
if (error_reporting() & $level === 0) {
return;
}
$exceptionLevel = isset($this->levels[$level]) ? $this->levels[$level] : $level;
throw new \ErrorException(
sprintf('%s: %s in %s line %d', $exceptionLevel, $message, $file, $line),
0,
$level,
$file,
$line
);
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Helper;
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
/**
* Converts ANSI output to HTML.
*
* @package PHPCI\Helper
*/
final class AnsiConverter
{
static private $converter = null;
/**
* Initialize the singleton.
*
* @return AnsiToHtmlConverter
*/
private static function getInstance()
{
if (self::$converter === null) {
self::$converter = new AnsiToHtmlConverter(null, false);
}
return self::$converter;
}
/**
* Convert a text containing ANSI color sequences into HTML code.
*
* @param string $text The text to convert
*
* @return string The HTML code.
*/
public static function convert($text)
{
return self::getInstance()->convert($text);
}
/**
* Do not instantiate this class.
*/
private function __construct()
{
}
}

View file

@ -9,9 +9,9 @@
namespace PHPCI\Helper; namespace PHPCI\Helper;
use \PHPCI\Logging\BuildLogger; use Exception;
use PHPCI\Logging\BuildLogger;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use PHPCI\Helper\Lang;
/** /**
* Handles running system commands with variables. * Handles running system commands with variables.
@ -20,7 +20,7 @@ use PHPCI\Helper\Lang;
abstract class BaseCommandExecutor implements CommandExecutor abstract class BaseCommandExecutor implements CommandExecutor
{ {
/** /**
* @var \PHPCI\Logging\BuildLogger * @var BuildLogger
*/ */
protected $logger; protected $logger;
@ -62,9 +62,7 @@ abstract class BaseCommandExecutor implements CommandExecutor
$this->logger = $logger; $this->logger = $logger;
$this->quiet = $quiet; $this->quiet = $quiet;
$this->verbose = $verbose; $this->verbose = $verbose;
$this->lastOutput = array(); $this->lastOutput = array();
$this->rootDir = $rootDir; $this->rootDir = $rootDir;
} }
@ -78,6 +76,7 @@ abstract class BaseCommandExecutor implements CommandExecutor
$this->lastOutput = array(); $this->lastOutput = array();
$command = call_user_func_array('sprintf', $args); $command = call_user_func_array('sprintf', $args);
$this->logger->logDebug($command);
if ($this->quiet) { if ($this->quiet) {
$this->logger->log('Executing: ' . $command); $this->logger->log('Executing: ' . $command);
@ -91,8 +90,7 @@ abstract class BaseCommandExecutor implements CommandExecutor
); );
$pipes = array(); $pipes = array();
$process = proc_open($command, $descriptorSpec, $pipes, $this->buildPath, null);
$process = proc_open($command, $descriptorSpec, $pipes, dirname($this->buildPath), null);
if (is_resource($process)) { if (is_resource($process)) {
fclose($pipes[0]); fclose($pipes[0]);
@ -146,13 +144,12 @@ abstract class BaseCommandExecutor implements CommandExecutor
/** /**
* Find a binary required by a plugin. * Find a binary required by a plugin.
* @param string $binary * @param string $binary
* @param null $buildPath * @param bool $quiet
* @return null|string * @return null|string
*/ */
public function findBinary($binary, $buildPath = null) public function findBinary($binary, $quiet = false)
{ {
$binaryPath = null; $composerBin = $this->getComposerBinDir(realpath($this->buildPath));
$composerBin = $this->getComposerBinDir(realpath($buildPath));
if (is_string($binary)) { if (is_string($binary)) {
$binary = array($binary); $binary = array($binary);
@ -162,32 +159,31 @@ abstract class BaseCommandExecutor implements CommandExecutor
$this->logger->log(Lang::get('looking_for_binary', $bin), LogLevel::DEBUG); $this->logger->log(Lang::get('looking_for_binary', $bin), LogLevel::DEBUG);
if (is_dir($composerBin) && is_file($composerBin.'/'.$bin)) { if (is_dir($composerBin) && is_file($composerBin.'/'.$bin)) {
$this->logger->log(Lang::get('found_in_path', $composerBin, $bin), LogLevel::DEBUG); $this->logger->log(Lang::get('found_in_path', $composerBin, $bin), LogLevel::DEBUG);
$binaryPath = $composerBin . '/' . $bin; return $composerBin . '/' . $bin;
break;
} }
if (is_file($this->rootDir . $bin)) { if (is_file($this->rootDir . $bin)) {
$this->logger->log(Lang::get('found_in_path', 'root', $bin), LogLevel::DEBUG); $this->logger->log(Lang::get('found_in_path', 'root', $bin), LogLevel::DEBUG);
$binaryPath = $this->rootDir . $bin; return $this->rootDir . $bin;
break;
} }
if (is_file($this->rootDir . 'vendor/bin/' . $bin)) { if (is_file($this->rootDir . 'vendor/bin/' . $bin)) {
$this->logger->log(Lang::get('found_in_path', 'vendor/bin', $bin), LogLevel::DEBUG); $this->logger->log(Lang::get('found_in_path', 'vendor/bin', $bin), LogLevel::DEBUG);
$binaryPath = $this->rootDir . 'vendor/bin/' . $bin; return $this->rootDir . 'vendor/bin/' . $bin;
break;
} }
$findCmdResult = $this->findGlobalBinary($bin); $findCmdResult = $this->findGlobalBinary($bin);
if (is_file($findCmdResult)) { if (is_file($findCmdResult)) {
$this->logger->log(Lang::get('found_in_path', '', $bin), LogLevel::DEBUG); $this->logger->log(Lang::get('found_in_path', '', $bin), LogLevel::DEBUG);
$binaryPath = $findCmdResult; return $findCmdResult;
break;
} }
} }
return $binaryPath;
if ($quiet) {
return;
}
throw new Exception(Lang::get('could_not_find', implode('/', $binary)));
} }
/** /**
@ -219,4 +215,13 @@ abstract class BaseCommandExecutor implements CommandExecutor
} }
return null; return null;
} }
/**
* Set the buildPath property.
* @param string $path
*/
public function setBuildPath($path)
{
$this->buildPath = $path;
}
} }

View file

@ -38,6 +38,7 @@ class BuildInterpolator
$this->interpolation_vars['%COMMIT%'] = $build->getCommitId(); $this->interpolation_vars['%COMMIT%'] = $build->getCommitId();
$this->interpolation_vars['%SHORT_COMMIT%'] = substr($build->getCommitId(), 0, 7); $this->interpolation_vars['%SHORT_COMMIT%'] = substr($build->getCommitId(), 0, 7);
$this->interpolation_vars['%COMMIT_EMAIL%'] = $build->getCommitterEmail(); $this->interpolation_vars['%COMMIT_EMAIL%'] = $build->getCommitterEmail();
$this->interpolation_vars['%COMMIT_MESSAGE%'] = $build->getCommitMessage();
$this->interpolation_vars['%COMMIT_URI%'] = $build->getCommitLink(); $this->interpolation_vars['%COMMIT_URI%'] = $build->getCommitLink();
$this->interpolation_vars['%BRANCH%'] = $build->getBranch(); $this->interpolation_vars['%BRANCH%'] = $build->getBranch();
$this->interpolation_vars['%BRANCH_URI%'] = $build->getBranchLink(); $this->interpolation_vars['%BRANCH_URI%'] = $build->getBranchLink();
@ -49,6 +50,7 @@ class BuildInterpolator
$this->interpolation_vars['%BUILD_URI%'] = $phpCiUrl . "build/view/" . $build->getId(); $this->interpolation_vars['%BUILD_URI%'] = $phpCiUrl . "build/view/" . $build->getId();
$this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%']; $this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%'];
$this->interpolation_vars['%PHPCI_SHORT_COMMIT%'] = $this->interpolation_vars['%SHORT_COMMIT%']; $this->interpolation_vars['%PHPCI_SHORT_COMMIT%'] = $this->interpolation_vars['%SHORT_COMMIT%'];
$this->interpolation_vars['%PHPCI_COMMIT_MESSAGE%'] = $this->interpolation_vars['%COMMIT_MESSAGE%'];
$this->interpolation_vars['%PHPCI_COMMIT_EMAIL%'] = $this->interpolation_vars['%COMMIT_EMAIL%']; $this->interpolation_vars['%PHPCI_COMMIT_EMAIL%'] = $this->interpolation_vars['%COMMIT_EMAIL%'];
$this->interpolation_vars['%PHPCI_COMMIT_URI%'] = $this->interpolation_vars['%COMMIT_URI%']; $this->interpolation_vars['%PHPCI_COMMIT_URI%'] = $this->interpolation_vars['%COMMIT_URI%'];
$this->interpolation_vars['%PHPCI_PROJECT%'] = $this->interpolation_vars['%PROJECT%']; $this->interpolation_vars['%PHPCI_PROJECT%'] = $this->interpolation_vars['%PROJECT%'];
@ -61,6 +63,7 @@ class BuildInterpolator
putenv('PHPCI=1'); putenv('PHPCI=1');
putenv('PHPCI_COMMIT=' . $this->interpolation_vars['%COMMIT%']); putenv('PHPCI_COMMIT=' . $this->interpolation_vars['%COMMIT%']);
putenv('PHPCI_SHORT_COMMIT=' . $this->interpolation_vars['%SHORT_COMMIT%']); putenv('PHPCI_SHORT_COMMIT=' . $this->interpolation_vars['%SHORT_COMMIT%']);
putenv('PHPCI_COMMIT_MESSAGE=' . $this->interpolation_vars['%COMMIT_MESSAGE%']);
putenv('PHPCI_COMMIT_EMAIL=' . $this->interpolation_vars['%COMMIT_EMAIL%']); putenv('PHPCI_COMMIT_EMAIL=' . $this->interpolation_vars['%COMMIT_EMAIL%']);
putenv('PHPCI_COMMIT_URI=' . $this->interpolation_vars['%COMMIT_URI%']); putenv('PHPCI_COMMIT_URI=' . $this->interpolation_vars['%COMMIT_URI%']);
putenv('PHPCI_PROJECT=' . $this->interpolation_vars['%PROJECT%']); putenv('PHPCI_PROJECT=' . $this->interpolation_vars['%PROJECT%']);

View file

@ -26,8 +26,17 @@ interface CommandExecutor
/** /**
* Find a binary required by a plugin. * Find a binary required by a plugin.
* @param string $binary * @param string $binary
* @param string $buildPath the current build path * @param bool $quiet Returns null instead of throwing an execption.
*
* @return null|string * @return null|string
*
* @throws \Exception when no binary has been found and $quiet is false.
*/ */
public function findBinary($binary, $buildPath = null); public function findBinary($binary, $quiet = false);
/**
* Set the buildPath property.
* @param string $path
*/
public function setBuildPath($path);
} }

62
PHPCI/Helper/Diff.php Normal file
View file

@ -0,0 +1,62 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Helper;
use b8\Cache;
use b8\Config;
use b8\HttpClient;
/**
* Provides some basic diff processing functionality.
* @package PHPCI\Helper
*/
class Diff
{
/**
* Take a diff
* @param string $diff
* @return array
*/
public function getLinePositions($diff)
{
if (empty($diff)) {
return null;
}
$rtn = array();
$diffLines = explode(PHP_EOL, $diff);
while (count($diffLines)) {
$line = array_shift($diffLines);
if (substr($line, 0, 2) == '@@') {
array_unshift($diffLines, $line);
break;
}
}
$lineNumber = 0;
$position = 0;
foreach ($diffLines as $diffLine) {
if (preg_match('/@@\s+\-[0-9]+\,[0-9]+\s+\+([0-9]+)\,([0-9]+)/', $diffLine, $matches)) {
$lineNumber = (int)$matches[1] - 1;
}
$rtn[$lineNumber] = $position;
$lineNumber++;
$position++;
}
return $rtn;
}
}

View file

@ -90,7 +90,7 @@ class Email
* @param bool $isHtml * @param bool $isHtml
* @return $this * @return $this
*/ */
public function setIsHtml($isHtml = false) public function setHtml($isHtml = false)
{ {
$this->isHtml = $isHtml; $this->isHtml = $isHtml;

View file

@ -48,24 +48,18 @@ class Github
$res = $http->get($url, $params); $res = $http->get($url, $params);
foreach ($res['body'] as $item) { foreach ($res['body'] as $item) {
$results[] = $item; $results[] = $item;
} }
foreach ($res['headers'] as $header) { foreach ($res['headers'] as $header) {
if (preg_match('/^Link: <([^>]+)>; rel="next"/', $header, $r)) { if (preg_match('/^Link: <([^>]+)>; rel="next"/', $header, $r)) {
$host = parse_url($r[1]); $host = parse_url($r[1]);
parse_str($host['query'], $params); parse_str($host['query'], $params);
$results = $this->makeRecursiveRequest($host['path'], $params, $results); $results = $this->makeRecursiveRequest($host['path'], $params, $results);
break; break;
} }
} }
return $results; return $results;
@ -79,7 +73,7 @@ class Github
$token = Config::getInstance()->get('phpci.github.token'); $token = Config::getInstance()->get('phpci.github.token');
if (!$token) { if (!$token) {
die(json_encode(null)); return null;
} }
$cache = Cache::getCache(Cache::TYPE_APC); $cache = Cache::getCache(Cache::TYPE_APC);
@ -108,4 +102,74 @@ class Github
return $rtn; return $rtn;
} }
/**
* Create a comment on a specific file (and commit) in a Github Pull Request.
* @param $repo
* @param $pullId
* @param $commitId
* @param $file
* @param $line
* @param $comment
* @return null
*/
public function createPullRequestComment($repo, $pullId, $commitId, $file, $line, $comment)
{
$token = Config::getInstance()->get('phpci.github.token');
if (!$token) {
return null;
}
$url = '/repos/' . strtolower($repo) . '/pulls/' . $pullId . '/comments';
$params = array(
'body' => $comment,
'commit_id' => $commitId,
'path' => $file,
'position' => $line,
);
$http = new HttpClient('https://api.github.com');
$http->setHeaders(array(
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Basic ' . base64_encode($token . ':x-oauth-basic'),
));
$http->post($url, json_encode($params));
}
/**
* Create a comment on a Github commit.
* @param $repo
* @param $commitId
* @param $file
* @param $line
* @param $comment
* @return null
*/
public function createCommitComment($repo, $commitId, $file, $line, $comment)
{
$token = Config::getInstance()->get('phpci.github.token');
if (!$token) {
return null;
}
$url = '/repos/' . strtolower($repo) . '/commits/' . $commitId . '/comments';
$params = array(
'body' => $comment,
'path' => $file,
'position' => $line,
);
$http = new HttpClient('https://api.github.com');
$http->setHeaders(array(
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Basic ' . base64_encode($token . ':x-oauth-basic'),
));
$http->post($url, json_encode($params));
}
} }

View file

@ -13,6 +13,7 @@ use b8\Config;
/** /**
* Languages Helper Class - Handles loading strings files and the strings within them. * Languages Helper Class - Handles loading strings files and the strings within them.
*
* @package PHPCI\Helper * @package PHPCI\Helper
*/ */
class Lang class Lang
@ -23,6 +24,7 @@ class Lang
/** /**
* Get a specific string from the language file. * Get a specific string from the language file.
*
* @param $string * @param $string
* @return mixed|string * @return mixed|string
*/ */
@ -48,6 +50,7 @@ class Lang
/** /**
* Get the currently active language. * Get the currently active language.
*
* @return string|null * @return string|null
*/ */
public static function getLanguage() public static function getLanguage()
@ -57,7 +60,9 @@ class Lang
/** /**
* Try and load a language, and if successful, set it for use throughout the system. * Try and load a language, and if successful, set it for use throughout the system.
*
* @param $language * @param $language
*
* @return bool * @return bool
*/ */
public static function setLanguage($language) public static function setLanguage($language)
@ -73,6 +78,7 @@ class Lang
/** /**
* Return a list of available languages and their names. * Return a list of available languages and their names.
*
* @return array * @return array
*/ */
public static function getLanguageOptions() public static function getLanguageOptions()
@ -90,6 +96,7 @@ class Lang
/** /**
* Get the strings for the currently active language. * Get the strings for the currently active language.
*
* @return string[] * @return string[]
*/ */
public static function getStrings() public static function getStrings()
@ -99,6 +106,7 @@ class Lang
/** /**
* Initialise the Language helper, try load the language file for the user's browser or the configured default. * Initialise the Language helper, try load the language file for the user's browser or the configured default.
*
* @param Config $config * @param Config $config
*/ */
public static function init(Config $config) public static function init(Config $config)
@ -137,6 +145,7 @@ class Lang
/** /**
* Load a specific language file. * Load a specific language file.
*
* @return string[]|null * @return string[]|null
*/ */
protected static function loadLanguage() protected static function loadLanguage()
@ -147,7 +156,7 @@ class Lang
return null; return null;
} }
require_once($langFile); require($langFile);
if (is_null($strings) || !is_array($strings) || !count($strings)) { if (is_null($strings) || !is_array($strings) || !count($strings)) {
return null; return null;
@ -168,4 +177,24 @@ class Lang
} }
} }
} }
/**
* Create a time tag for localization.
*
* See http://momentjs.com/docs/#/displaying/format/ for a list of supported formats.
*
* @param \DateTime $dateTime The dateTime to represent.
* @param string $format The moment.js format to use.
*
* @return string The formatted tag.
*/
public static function formatDateTime(\DateTime $dateTime, $format = 'lll')
{
return sprintf(
'<time datetime="%s" data-format="%s">%s</time>',
$dateTime->format(\DateTime::ISO8601),
$format,
$dateTime->format(\DateTime::RFC2822)
);
}
} }

View file

@ -9,8 +9,10 @@
namespace PHPCI\Helper; namespace PHPCI\Helper;
use b8\Config;
/** /**
* Login Is Disabled Helper - Checks if login is disalbed in the view * Login Is Disabled Helper - Checks if login is disabled in the view
* @author Stephen Ball <phpci@stephen.rebelinblue.com> * @author Stephen Ball <phpci@stephen.rebelinblue.com>
* @package PHPCI * @package PHPCI
* @subpackage Web * @subpackage Web
@ -27,7 +29,7 @@ class LoginIsDisabled
{ {
unset($method, $params); unset($method, $params);
$config = b8\Config::getInstance(); $config = Config::getInstance();
$state = (bool) $config->get('phpci.authentication_settings.state', false); $state = (bool) $config->get('phpci.authentication_settings.state', false);
return (false !== $state); return (false !== $state);

View file

@ -71,7 +71,7 @@ class MailerFactory
} else { } else {
// Check defaults // Check defaults
switch($configName) { switch ($configName) {
case 'smtp_address': case 'smtp_address':
return "localhost"; return "localhost";
case 'default_mailto_address': case 'default_mailto_address':

View file

@ -39,9 +39,9 @@ class SshKey
$return = array('private_key' => '', 'public_key' => ''); $return = array('private_key' => '', 'public_key' => '');
if ($this->canGenerateKeys()) { $output = @shell_exec('ssh-keygen -t rsa -b 2048 -f '.$keyFile.' -N "" -C "deploy@phpci"');
shell_exec('ssh-keygen -q -t rsa -b 2048 -f '.$keyFile.' -N "" -C "deploy@phpci"');
if (!empty($output)) {
$pub = file_get_contents($keyFile . '.pub'); $pub = file_get_contents($keyFile . '.pub');
$prv = file_get_contents($keyFile); $prv = file_get_contents($keyFile);
@ -56,16 +56,4 @@ class SshKey
return $return; return $return;
} }
/**
* Checks whether or not we can generate keys, by quietly test running ssh-keygen.
* @return bool
*/
public function canGenerateKeys()
{
$keygen = @shell_exec('ssh-keygen -h');
$canGenerateKeys = !empty($keygen);
return $canGenerateKeys;
}
} }

View file

@ -22,6 +22,9 @@ class WindowsCommandExecutor extends BaseCommandExecutor
*/ */
protected function findGlobalBinary($binary) protected function findGlobalBinary($binary)
{ {
return trim(shell_exec('where ' . $binary)); $command = sprintf('where %s', $binary);
$result = shell_exec($command);
return trim($result);
} }
} }

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Adgangskode-nulstilling for %s', 'reset_email_title' => 'PHPCI Adgangskode-nulstilling for %s',
'reset_invalid' => 'Ugyldig anmodning om adgangskode-nulstilling.', 'reset_invalid' => 'Ugyldig anmodning om adgangskode-nulstilling.',
'email_address' => 'Email-addresse', 'email_address' => 'Email-addresse',
'login' => 'Login / Email Address',
'password' => 'Adgangskode', 'password' => 'Adgangskode',
'log_in' => 'Log ind', 'log_in' => 'Log ind',
@ -112,6 +113,8 @@ i din foretrukne hosting-platform.',
(hvis du ikke har mulighed for at tilføje en phpci.yml fil i projektets repository)', (hvis du ikke har mulighed for at tilføje en phpci.yml fil i projektets repository)',
'default_branch' => 'Default branch navn', 'default_branch' => 'Default branch navn',
'allow_public_status' => 'Tillad offentlig status-side og -billede for dette projekt?', 'allow_public_status' => 'Tillad offentlig status-side og -billede for dette projekt?',
'archived' => 'Archived',
'archived_menu' => 'Archived',
'save_project' => 'Gem Projekt', 'save_project' => 'Gem Projekt',
'error_mercurial' => 'Mercurial repository-URL skal starte med http:// eller https://', 'error_mercurial' => 'Mercurial repository-URL skal starte med http:// eller https://',
@ -125,6 +128,7 @@ i din foretrukne hosting-platform.',
'all_branches' => 'Alle branches', 'all_branches' => 'Alle branches',
'builds' => 'Builds', 'builds' => 'Builds',
'id' => 'ID', 'id' => 'ID',
'date' => 'Date',
'project' => 'Projekt', 'project' => 'Projekt',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Branch', 'branch' => 'Branch',
@ -166,6 +170,7 @@ Services</a> sektionen under dit Bitbucket-repository.',
'lines_of_code' => 'Kode-linjer', 'lines_of_code' => 'Kode-linjer',
'build_log' => 'Build-log', 'build_log' => 'Build-log',
'quality_trend' => 'Kvalitets-trend', 'quality_trend' => 'Kvalitets-trend',
'codeception_errors' => 'Codeception-fejl',
'phpmd_warnings' => 'PHPMD-advarsler', 'phpmd_warnings' => 'PHPMD-advarsler',
'phpcs_warnings' => 'PHPCS-advarsler', 'phpcs_warnings' => 'PHPCS-advarsler',
'phpcs_errors' => 'PHPCS-fejl', 'phpcs_errors' => 'PHPCS-fejl',
@ -174,6 +179,7 @@ Services</a> sektionen under dit Bitbucket-repository.',
'phpdoccheck_warnings' => 'Manglende Docblocks', 'phpdoccheck_warnings' => 'Manglende Docblocks',
'issues' => 'Sager', 'issues' => 'Sager',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Manglende Docblocks', 'phpdoccheck' => 'Manglende Docblocks',
@ -190,14 +196,20 @@ Services</a> sektionen under dit Bitbucket-repository.',
'end' => 'Slut', 'end' => 'Slut',
'from' => 'Fra', 'from' => 'Fra',
'to' => 'Til', 'to' => 'Til',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultat', 'result' => 'Resultat',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Tog %d sekunder', 'took_n_seconds' => 'Tog %d sekunder',
'build_created' => 'Build Oprettet', 'build_created' => 'Build Oprettet',
'build_started' => 'Build Startet', 'build_started' => 'Build Startet',
'build_finished' => 'Build Afsluttet', 'build_finished' => 'Build Afsluttet',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Successful: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Navn', 'name' => 'Navn',
@ -293,15 +305,15 @@ du kører composer update.',
Kontrollér venligst nedenstående fejl før du fortsætter.', Kontrollér venligst nedenstående fejl før du fortsætter.',
'must_be_valid_email' => 'Skal være en gyldig email-adresse.', 'must_be_valid_email' => 'Skal være en gyldig email-adresse.',
'must_be_valid_url' => 'Skal være en gyldig URL.', 'must_be_valid_url' => 'Skal være en gyldig URL.',
'enter_name' => 'Administrator-navn:', 'enter_name' => 'Administrator-navn: ',
'enter_email' => 'Administrators email-adresse:', 'enter_email' => 'Administrators email-adresse: ',
'enter_password' => 'Administrator-adgangskode:', 'enter_password' => 'Administrator-adgangskode: ',
'enter_phpci_url' => 'Din PHPCI URL (eksempelvis "http://phpci.local"):', 'enter_phpci_url' => 'Din PHPCI URL (eksempelvis "http://phpci.local"): ',
'enter_db_host' => 'Indtast dit MySQL-hostnavn [localhost]:', 'enter_db_host' => 'Indtast dit MySQL-hostnavn [localhost]: ',
'enter_db_name' => 'Indtast dit MySQL database-navn [phpci]:', 'enter_db_name' => 'Indtast dit MySQL database-navn [phpci]: ',
'enter_db_user' => 'Indtast dit MySQL-brugernavn [phpci]:', 'enter_db_user' => 'Indtast dit MySQL-brugernavn [phpci]: ',
'enter_db_pass' => 'Indtast dit MySQL-password:', 'enter_db_pass' => 'Indtast dit MySQL-password: ',
'could_not_connect' => 'PHPCI kunne ikke forbinde til MySQL med de angivning oplysninger. Forsøg igen.', 'could_not_connect' => 'PHPCI kunne ikke forbinde til MySQL med de angivning oplysninger. Forsøg igen.',
'setting_up_db' => 'Indlæser database...', 'setting_up_db' => 'Indlæser database...',
'user_created' => 'Brugerkonto oprettet!', 'user_created' => 'Brugerkonto oprettet!',
@ -328,6 +340,12 @@ Kontrollér venligst nedenstående fejl før du fortsætter.',
'create_admin_user' => 'Tilføj en administrator', 'create_admin_user' => 'Tilføj en administrator',
'incorrect_format' => 'Forkert format', 'incorrect_format' => 'Forkert format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command // Run Command
'run_all_pending' => 'Kør alle PHPCI builds i køen.', 'run_all_pending' => 'Kør alle PHPCI builds i køen.',
'finding_builds' => 'Finder builds der skal køres', 'finding_builds' => 'Finder builds der skal køres',

View file

@ -13,7 +13,7 @@ $strings = array(
// Log in: // Log in:
'log_in_to_phpci' => 'In PHPCI einloggen', 'log_in_to_phpci' => 'In PHPCI einloggen',
'login_error' => 'Fehlerhafte Emailadresse oder fehlerhaftes Passowrt', 'login_error' => 'Fehlerhafte Emailadresse oder fehlerhaftes Passwort',
'forgotten_password_link' => 'Passwort vergessen?', 'forgotten_password_link' => 'Passwort vergessen?',
'reset_emailed' => 'Wir haben Ihnen einen Link geschickt, um Ihr Passwort zurückzusetzen', 'reset_emailed' => 'Wir haben Ihnen einen Link geschickt, um Ihr Passwort zurückzusetzen',
'reset_header' => '<strong>Keine Panik!</strong><br>Geben Sie einfach unten Ihre Emailadresse an 'reset_header' => '<strong>Keine Panik!</strong><br>Geben Sie einfach unten Ihre Emailadresse an
@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Passwort zurücksetzen für %s', 'reset_email_title' => 'PHPCI Passwort zurücksetzen für %s',
'reset_invalid' => 'Fehlerhafte Anfrage für das Zurücksetzen eines Passwortes', 'reset_invalid' => 'Fehlerhafte Anfrage für das Zurücksetzen eines Passwortes',
'email_address' => 'Emailadresse', 'email_address' => 'Emailadresse',
'login' => 'Login / Emailadresse',
'password' => 'Passwort', 'password' => 'Passwort',
'log_in' => 'Einloggen', 'log_in' => 'Einloggen',
@ -56,13 +57,13 @@ PHPCI',
// Sidebar // Sidebar
'hello_name' => 'Hallo, %s', 'hello_name' => 'Hallo, %s',
'dashboard' => 'Dashboard', 'dashboard' => 'Dashboard',
'admin_options' => 'Administraion', 'admin_options' => 'Administration',
'add_project' => 'Projekt hinzufügen', 'add_project' => 'Projekt hinzufügen',
'settings' => 'Einstellungen', 'settings' => 'Einstellungen',
'manage_users' => 'Benutzereinstellungen', 'manage_users' => 'Benutzereinstellungen',
'plugins' => 'Plugins', 'plugins' => 'Plugins',
'view' => 'Ansehen', 'view' => 'Ansehen',
'build_now' => 'Aktueller Build', 'build_now' => 'Jetzt bauen',
'edit_project' => 'Projekt bearbeiten', 'edit_project' => 'Projekt bearbeiten',
'delete_project' => 'Projekt löschen', 'delete_project' => 'Projekt löschen',
@ -101,6 +102,7 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'remote' => 'Externe URL', 'remote' => 'Externe URL',
'local' => 'Lokaler Pfad', 'local' => 'Lokaler Pfad',
'hg' => 'Mercurial', 'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => 'Wo wird Ihr Projekt gehostet?', 'where_hosted' => 'Wo wird Ihr Projekt gehostet?',
'choose_github' => 'Wählen Sie ein GitHub Repository:', 'choose_github' => 'Wählen Sie ein GitHub Repository:',
@ -113,6 +115,8 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
(falls Sie Ihrem Projektrepository kein phpci.yml hinzufügen können)', (falls Sie Ihrem Projektrepository kein phpci.yml hinzufügen können)',
'default_branch' => 'Name des Standardbranches', 'default_branch' => 'Name des Standardbranches',
'allow_public_status' => 'Öffentliche Statusseite und -bild für dieses Projekt einschalten?', 'allow_public_status' => 'Öffentliche Statusseite und -bild für dieses Projekt einschalten?',
'archived' => 'Archiviert',
'archived_menu' => 'Archiviert',
'save_project' => 'Projekt speichern', 'save_project' => 'Projekt speichern',
'error_mercurial' => 'Mercurial Repository-URL muss mit http://, oder https:// beginnen', 'error_mercurial' => 'Mercurial Repository-URL muss mit http://, oder https:// beginnen',
@ -126,6 +130,7 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'all_branches' => 'Alle Branches', 'all_branches' => 'Alle Branches',
'builds' => 'Builds', 'builds' => 'Builds',
'id' => 'ID', 'id' => 'ID',
'date' => 'Datum',
'project' => 'Projekt', 'project' => 'Projekt',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Branch', 'branch' => 'Branch',
@ -146,6 +151,9 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'webhooks_help_bitbucket' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed werden, fügen Sie die untenstehende URL als "POST" Service in der <a href="https://bitbucket.org/%s/admin/services">Services</a>-Sektion Ihres Bitbucket Repositories hinzu.', 'webhooks_help_bitbucket' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed werden, fügen Sie die untenstehende URL als "POST" Service in der <a href="https://bitbucket.org/%s/admin/services">Services</a>-Sektion Ihres Bitbucket Repositories hinzu.',
// View Build // View Build
'errors' => 'Fehler',
'information' => 'Information',
'build_x_not_found' => 'Build mit ID %d existiert nicht.', 'build_x_not_found' => 'Build mit ID %d existiert nicht.',
'build_n' => 'Build %d', 'build_n' => 'Build %d',
'rebuild_now' => 'Build neu starten', 'rebuild_now' => 'Build neu starten',
@ -164,6 +172,7 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'lines_of_code' => 'Anzahl Codezeilen', 'lines_of_code' => 'Anzahl Codezeilen',
'build_log' => 'Buildprotokoll', 'build_log' => 'Buildprotokoll',
'quality_trend' => 'Qualitätstrend', 'quality_trend' => 'Qualitätstrend',
'codeception_errors' => 'Codeception Errors',
'phpmd_warnings' => 'PHPMD Warnings', 'phpmd_warnings' => 'PHPMD Warnings',
'phpcs_warnings' => 'PHPCS Warnings', 'phpcs_warnings' => 'PHPCS Warnings',
'phpcs_errors' => 'PHPCS Errors', 'phpcs_errors' => 'PHPCS Errors',
@ -172,12 +181,21 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'phpdoccheck_warnings' => 'Fehlende Docblocks', 'phpdoccheck_warnings' => 'Fehlende Docblocks',
'issues' => 'Probleme', 'issues' => 'Probleme',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Fehlende Docblocks', 'phpdoccheck' => 'Fehlende Docblocks',
'phpmd' => 'PHP Mess Detector', 'phpmd' => 'PHP Mess Detector',
'phpspec' => 'PHP Spec', 'phpspec' => 'PHP Spec',
'phpunit' => 'PHP Unit', 'phpunit' => 'PHP Unit',
'technical_debt' => 'Technische Schulden',
'behat' => 'Behat',
'codeception_feature' => 'Feature',
'codeception_suite' => 'Suite',
'codeception_time' => 'Zeit',
'codeception_synopsis' => '<strong>%1$d</strong> Tests in <strong>%2$f</strong> Sekunden ausgeführt.
<strong>%3$d</strong> Fehler.',
'file' => 'Datei', 'file' => 'Datei',
'line' => 'Zeile', 'line' => 'Zeile',
@ -188,14 +206,20 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'end' => 'Ende', 'end' => 'Ende',
'from' => 'Von', 'from' => 'Von',
'to' => 'Bis', 'to' => 'Bis',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultat', 'result' => 'Resultat',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Benötigte %d Sekunden', 'took_n_seconds' => 'Benötigte %d Sekunden',
'build_created' => 'Build erstellt', 'build_created' => 'Build erstellt',
'build_started' => 'Build gestartet', 'build_started' => 'Build gestartet',
'build_finished' => 'Build abgeschlossen', 'build_finished' => 'Build abgeschlossen',
'test_message' => 'Nachricht',
'test_no_message' => 'Keine Nachricht',
'test_success' => 'Erfolgreich: %d',
'test_fail' => 'Fehlschläge: %d',
'test_skipped' => 'Übersprungen: %d',
'test_error' => 'Fehler: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d Test(s)',
// Users // Users
'name' => 'Name', 'name' => 'Name',
@ -271,6 +295,19 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'search_packagist_for_more' => 'Packagist nach mehr Packages durchsuchen', 'search_packagist_for_more' => 'Packagist nach mehr Packages durchsuchen',
'search' => 'Suchen &raquo;', 'search' => 'Suchen &raquo;',
// Summary plugin
'build-summary' => 'Zusammenfassung',
'stage' => 'Abschnitt',
'duration' => 'Dauer',
'plugin' => 'Plugin',
'stage_setup' => 'Vorbereitung',
'stage_test' => 'Test',
'stage_complete' => 'Vollständig',
'stage_success' => 'Erfolg',
'stage_failure' => 'Fehlschlag',
'stage_broken' => 'Defekt',
'stage_fixed' => 'Behoben',
// Installer // Installer
'installation_url' => 'PHPCI Installations-URL', 'installation_url' => 'PHPCI Installations-URL',
'db_host' => 'Datenbankserver', 'db_host' => 'Datenbankserver',
@ -291,9 +328,9 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
Bitte überprüfen Sie die Fehler, bevor Sie fortfahren,', Bitte überprüfen Sie die Fehler, bevor Sie fortfahren,',
'must_be_valid_email' => 'Muss eine gültige Emailadresse sein.', 'must_be_valid_email' => 'Muss eine gültige Emailadresse sein.',
'must_be_valid_url' => 'Muss eine valide URL sein.', 'must_be_valid_url' => 'Muss eine valide URL sein.',
'enter_name' => 'Name des Administrators:', 'enter_name' => 'Name des Administrators: ',
'enter_email' => 'Emailadresse des Administrators:', 'enter_email' => 'Emailadresse des Administrators: ',
'enter_password' => 'Passwort des Administrators:', 'enter_password' => 'Passwort des Administrators: ',
'enter_phpci_url' => 'Ihre PHPCI-URL (z.B. "http://phpci.local"): ', 'enter_phpci_url' => 'Ihre PHPCI-URL (z.B. "http://phpci.local"): ',
'enter_db_host' => 'Bitte geben Sie Ihren MySQL-Host ein [localhost]: ', 'enter_db_host' => 'Bitte geben Sie Ihren MySQL-Host ein [localhost]: ',
@ -326,6 +363,12 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'create_admin_user' => 'Administratorenbenutzer erstellen', 'create_admin_user' => 'Administratorenbenutzer erstellen',
'incorrect_format' => 'Falsches Format', 'incorrect_format' => 'Falsches Format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command // Run Command
'run_all_pending' => 'Führe alle ausstehenden PHPCI Builds aus.', 'run_all_pending' => 'Führe alle ausstehenden PHPCI Builds aus.',
'finding_builds' => 'Suche verarbeitbare Builds', 'finding_builds' => 'Suche verarbeitbare Builds',
@ -373,5 +416,18 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'build_file_missing' => 'Angegebene Builddatei existiert nicht.', 'build_file_missing' => 'Angegebene Builddatei existiert nicht.',
'property_file_missing' => 'Angegebene Eigenschaftsdatei existiert nicht.', 'property_file_missing' => 'Angegebene Eigenschaftsdatei existiert nicht.',
'could_not_process_report' => 'Konnte den von diesem Tool erstellten Bericht nicht verarbeiten.', 'could_not_process_report' => 'Konnte den von diesem Tool erstellten Bericht nicht verarbeiten.',
'shell_not_enabled' => 'Das Shell-Plugin ist nicht aktiviert. Bitte aktivieren Sie es via config.yml.' 'shell_not_enabled' => 'Das Shell-Plugin ist nicht aktiviert. Bitte aktivieren Sie es via config.yml.',
// Error Levels:
'critical' => 'Kritisch',
'high' => 'Hoch',
'normal' => 'Normal',
'low' => 'Niedrig',
// Plugins that generate errors:
'php_mess_detector' => 'PHP Mess Detector',
'php_code_sniffer' => 'PHP Code Sniffer',
'php_unit' => 'PHP Unit',
'php_cpd' => 'PHP Copy/Paste Detector',
'php_docblock_checker' => 'PHP Docblock Checker',
); );

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Επαναφορά Κωδικού για %s', 'reset_email_title' => 'PHPCI Επαναφορά Κωδικού για %s',
'reset_invalid' => 'Μη έγκυρο αίτημα επαναφοράς κωδικού πρόσβασης.', 'reset_invalid' => 'Μη έγκυρο αίτημα επαναφοράς κωδικού πρόσβασης.',
'email_address' => 'Διεύθυνση email', 'email_address' => 'Διεύθυνση email',
'login' => 'Login / Email Address',
'password' => 'Κωδικός πρόσβασης', 'password' => 'Κωδικός πρόσβασης',
'log_in' => 'Είσοδος', 'log_in' => 'Είσοδος',
@ -113,6 +114,8 @@ PHPCI',
(αν δεν μπορείτε να προσθέσετε ένα αρχείο phpci.yml στο αποθετήριο έργων)', (αν δεν μπορείτε να προσθέσετε ένα αρχείο phpci.yml στο αποθετήριο έργων)',
'default_branch' => 'Προκαθορισμένο όνομα διακλάδωσης', 'default_branch' => 'Προκαθορισμένο όνομα διακλάδωσης',
'allow_public_status' => 'Ενεργοποίηση της σελίδας δημόσιας κατάστασης και την εικόνα για το έργο αυτό;', 'allow_public_status' => 'Ενεργοποίηση της σελίδας δημόσιας κατάστασης και την εικόνα για το έργο αυτό;',
'archived' => 'Archived',
'archived_menu' => 'Archived',
'save_project' => 'Αποθήκευση έργου', 'save_project' => 'Αποθήκευση έργου',
'error_mercurial' => 'Ο σύνδεσμος URL του ευμετάβλητου αποθετηρίου πρέπει να ξεκινάει με http:// ή https://', 'error_mercurial' => 'Ο σύνδεσμος URL του ευμετάβλητου αποθετηρίου πρέπει να ξεκινάει με http:// ή https://',
@ -126,6 +129,7 @@ PHPCI',
'all_branches' => 'Όλες οι διακλαδώσεις', 'all_branches' => 'Όλες οι διακλαδώσεις',
'builds' => 'Κατασκευές', 'builds' => 'Κατασκευές',
'id' => 'Αριθμός αναγνώρισης', 'id' => 'Αριθμός αναγνώρισης',
'date' => 'Date',
'project' => 'Έργο', 'project' => 'Έργο',
'commit' => 'Συνεισφορά', 'commit' => 'Συνεισφορά',
'branch' => 'Διακλάδωση', 'branch' => 'Διακλάδωση',
@ -166,14 +170,17 @@ Services</a> του Bitbucket αποθετηρίου σας.',
'lines_of_code' => 'Γραμμές Κώδικα', 'lines_of_code' => 'Γραμμές Κώδικα',
'build_log' => 'Αρχείο καταγραφής κατασκευών', 'build_log' => 'Αρχείο καταγραφής κατασκευών',
'quality_trend' => 'Ποιότητα τρέντ', 'quality_trend' => 'Ποιότητα τρέντ',
'codeception_errors' => 'Λάθη Codeception',
'phpmd_warnings' => 'Προειδοποιήσεις PHPMD', 'phpmd_warnings' => 'Προειδοποιήσεις PHPMD',
'phpcs_warnings' => 'Προειδοποιήσεις PHPCS ', 'phpcs_warnings' => 'Προειδοποιήσεις PHPCS ',
'codeception_errors' => 'Λάθη Codeception',
'phpcs_errors' => 'Λάθη PHPCS', 'phpcs_errors' => 'Λάθη PHPCS',
'phplint_errors' => 'Λάθη Lint', 'phplint_errors' => 'Λάθη Lint',
'phpunit_errors' => 'Λάθη PHPUnit ', 'phpunit_errors' => 'Λάθη PHPUnit ',
'phpdoccheck_warnings' => 'Χαμένα Docblocks', 'phpdoccheck_warnings' => 'Χαμένα Docblocks',
'issues' => 'Θέματα', 'issues' => 'Θέματα',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Ανιχνευτής Αντιγραφής/Επικόλλησης', 'phpcpd' => 'PHP Ανιχνευτής Αντιγραφής/Επικόλλησης',
'phpcs' => 'Sniffer Κώδικα PHP', 'phpcs' => 'Sniffer Κώδικα PHP',
'phpdoccheck' => 'Χαμένα Docblocks', 'phpdoccheck' => 'Χαμένα Docblocks',
@ -190,14 +197,20 @@ Services</a> του Bitbucket αποθετηρίου σας.',
'end' => 'Τέλος', 'end' => 'Τέλος',
'from' => 'Από', 'from' => 'Από',
'to' => 'Προς', 'to' => 'Προς',
'suite' => 'Σουίτα',
'test' => 'Τέστ',
'result' => 'Αποτέλεσμα', 'result' => 'Αποτέλεσμα',
'ok' => 'ΟΚ', 'ok' => 'ΟΚ',
'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα', 'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα',
'build_created' => 'Η κατασκευή δημιουργήθηκε', 'build_created' => 'Η κατασκευή δημιουργήθηκε',
'build_started' => 'Η κατασκευή άρχισε', 'build_started' => 'Η κατασκευή άρχισε',
'build_finished' => 'Η κατασκευή ολοκληρώθηκε', 'build_finished' => 'Η κατασκευή ολοκληρώθηκε',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Successful: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Όνομα', 'name' => 'Όνομα',
@ -294,15 +307,15 @@ Services</a> του Bitbucket αποθετηρίου σας.',
Παρακαλούμε διαβάστε τα παραπάνω λάθη πριν συνεχίσετε.', Παρακαλούμε διαβάστε τα παραπάνω λάθη πριν συνεχίσετε.',
'must_be_valid_email' => 'Πρέπει να είναι μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.', 'must_be_valid_email' => 'Πρέπει να είναι μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.',
'must_be_valid_url' => 'Πρέπει να είναι μια έγκυρη διεύθυνση URL.', 'must_be_valid_url' => 'Πρέπει να είναι μια έγκυρη διεύθυνση URL.',
'enter_name' => 'Όνομα διαχειριστή:', 'enter_name' => 'Όνομα διαχειριστή: ',
'enter_email' => 'Ηλ. Διεύθυνση διαχειριστή:', 'enter_email' => 'Ηλ. Διεύθυνση διαχειριστή: ',
'enter_password' => 'Κωδικός πρόσβασης διαχειριστή:', 'enter_password' => 'Κωδικός πρόσβασης διαχειριστή: ',
'enter_phpci_url' => 'Ο URL σύνδεσμος σας για το PHPCI ("http://phpci.local" για παράδειγμα): ', 'enter_phpci_url' => 'Ο URL σύνδεσμος σας για το PHPCI ("http://phpci.local" για παράδειγμα): ',
'enter_db_host' => 'Παρακαλώ εισάγετε τον MySQL οικοδεσπότη σας [localhost]:', 'enter_db_host' => 'Παρακαλώ εισάγετε τον MySQL οικοδεσπότη σας [localhost]: ',
'enter_db_name' => 'Παρακαλώ εισάγετε το όνομα της MySQL βάσης δεδομένων σας [phpci]: ', 'enter_db_name' => 'Παρακαλώ εισάγετε το όνομα της MySQL βάσης δεδομένων σας [phpci]: ',
'enter_db_user' => 'Παρακαλώ εισάγετε το όνομα χρήστη της MySQL σας [phpci]:', 'enter_db_user' => 'Παρακαλώ εισάγετε το όνομα χρήστη της MySQL σας [phpci]: ',
'enter_db_pass' => 'Παρακαλώ εισάγετε τον κωδικό χρήστη της MySQL σας:', 'enter_db_pass' => 'Παρακαλώ εισάγετε τον κωδικό χρήστη της MySQL σας: ',
'could_not_connect' => 'Το PHPCI δεν μπόρεσε να συνδεθεί με την MySQL με τα στοχεία που δώσατε. Παρακαλώ δοκιμάστε ξανά.', 'could_not_connect' => 'Το PHPCI δεν μπόρεσε να συνδεθεί με την MySQL με τα στοχεία που δώσατε. Παρακαλώ δοκιμάστε ξανά.',
'setting_up_db' => 'Γίνεται ρύθμιση της βάσης δεδομένων σας ...', 'setting_up_db' => 'Γίνεται ρύθμιση της βάσης δεδομένων σας ...',
'user_created' => 'Λογαριασμός χρήστη δημιουργήθηκε!', 'user_created' => 'Λογαριασμός χρήστη δημιουργήθηκε!',
@ -329,6 +342,12 @@ Services</a> του Bitbucket αποθετηρίου σας.',
'create_admin_user' => 'Δημιουργήστε ένα χρήστη διαχειριστή', 'create_admin_user' => 'Δημιουργήστε ένα χρήστη διαχειριστή',
'incorrect_format' => 'Λανθασμένη μορφοποίηση', 'incorrect_format' => 'Λανθασμένη μορφοποίηση',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command // Run Command
'run_all_pending' => 'Εκτελέστε όλες τις εκκρεμείς PHPCI κατασκευές.', 'run_all_pending' => 'Εκτελέστε όλες τις εκκρεμείς PHPCI κατασκευές.',
'finding_builds' => 'Αναζήτηση κατασκευών για επεξεργασία', 'finding_builds' => 'Αναζήτηση κατασκευών για επεξεργασία',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Password Reset for %s', 'reset_email_title' => 'PHPCI Password Reset for %s',
'reset_invalid' => 'Invalid password reset request.', 'reset_invalid' => 'Invalid password reset request.',
'email_address' => 'Email Address', 'email_address' => 'Email Address',
'login' => 'Login / Email Address',
'password' => 'Password', 'password' => 'Password',
'log_in' => 'Log in', 'log_in' => 'Log in',
@ -101,6 +102,7 @@ PHPCI',
'remote' => 'Remote URL', 'remote' => 'Remote URL',
'local' => 'Local Path', 'local' => 'Local Path',
'hg' => 'Mercurial', 'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => 'Where is your project hosted?', 'where_hosted' => 'Where is your project hosted?',
'choose_github' => 'Choose a GitHub repository:', 'choose_github' => 'Choose a GitHub repository:',
@ -113,6 +115,8 @@ PHPCI',
(if you cannot add a phpci.yml file in the project repository)', (if you cannot add a phpci.yml file in the project repository)',
'default_branch' => 'Default branch name', 'default_branch' => 'Default branch name',
'allow_public_status' => 'Enable public status page and image for this project?', 'allow_public_status' => 'Enable public status page and image for this project?',
'archived' => 'Archived',
'archived_menu' => 'Archived',
'save_project' => 'Save Project', 'save_project' => 'Save Project',
'error_mercurial' => 'Mercurial repository URL must be start with http:// or https://', 'error_mercurial' => 'Mercurial repository URL must be start with http:// or https://',
@ -126,6 +130,7 @@ PHPCI',
'all_branches' => 'All Branches', 'all_branches' => 'All Branches',
'builds' => 'Builds', 'builds' => 'Builds',
'id' => 'ID', 'id' => 'ID',
'date' => 'Date',
'project' => 'Project', 'project' => 'Project',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Branch', 'branch' => 'Branch',
@ -149,6 +154,9 @@ PHPCI',
Services</a> section of your Bitbucket repository.', Services</a> section of your Bitbucket repository.',
// View Build // View Build
'errors' => 'Errors',
'information' => 'Information',
'build_x_not_found' => 'Build with ID %d does not exist.', 'build_x_not_found' => 'Build with ID %d does not exist.',
'build_n' => 'Build %d', 'build_n' => 'Build %d',
'rebuild_now' => 'Rebuild Now', 'rebuild_now' => 'Rebuild Now',
@ -167,6 +175,7 @@ PHPCI',
'lines_of_code' => 'Lines of Code', 'lines_of_code' => 'Lines of Code',
'build_log' => 'Build Log', 'build_log' => 'Build Log',
'quality_trend' => 'Quality Trend', 'quality_trend' => 'Quality Trend',
'codeception_errors' => 'Codeception Errors',
'phpmd_warnings' => 'PHPMD Warnings', 'phpmd_warnings' => 'PHPMD Warnings',
'phpcs_warnings' => 'PHPCS Warnings', 'phpcs_warnings' => 'PHPCS Warnings',
'phpcs_errors' => 'PHPCS Errors', 'phpcs_errors' => 'PHPCS Errors',
@ -175,12 +184,21 @@ PHPCI',
'phpdoccheck_warnings' => 'Missing Docblocks', 'phpdoccheck_warnings' => 'Missing Docblocks',
'issues' => 'Issues', 'issues' => 'Issues',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Missing Docblocks', 'phpdoccheck' => 'Missing Docblocks',
'phpmd' => 'PHP Mess Detector', 'phpmd' => 'PHP Mess Detector',
'phpspec' => 'PHP Spec', 'phpspec' => 'PHP Spec',
'phpunit' => 'PHP Unit', 'phpunit' => 'PHP Unit',
'technical_debt' => 'Technical Debt',
'behat' => 'Behat',
'codeception_feature' => 'Feature',
'codeception_suite' => 'Suite',
'codeception_time' => 'Time',
'codeception_synopsis' => '<strong>%1$d</strong> tests carried out in <strong>%2$f</strong> seconds.
<strong>%3$d</strong> failures.',
'file' => 'File', 'file' => 'File',
'line' => 'Line', 'line' => 'Line',
@ -191,14 +209,20 @@ PHPCI',
'end' => 'End', 'end' => 'End',
'from' => 'From', 'from' => 'From',
'to' => 'To', 'to' => 'To',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Result', 'result' => 'Result',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Took %d seconds', 'took_n_seconds' => 'Took %d seconds',
'build_created' => 'Build Created', 'build_created' => 'Build Created',
'build_started' => 'Build Started', 'build_started' => 'Build Started',
'build_finished' => 'Build Finished', 'build_finished' => 'Build Finished',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Successful: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Name', 'name' => 'Name',
@ -275,6 +299,19 @@ PHPCI',
'search_packagist_for_more' => 'Search Packagist for more packages', 'search_packagist_for_more' => 'Search Packagist for more packages',
'search' => 'Search &raquo;', 'search' => 'Search &raquo;',
// Summary plugin
'build-summary' => 'Summary',
'stage' => 'Stage',
'duration' => 'Duration',
'plugin' => 'Plugin',
'stage_setup' => 'Setup',
'stage_test' => 'Test',
'stage_complete' => 'Complete',
'stage_success' => 'Success',
'stage_failure' => 'Failure',
'stage_broken' => 'Broken',
'stage_fixed' => 'Fixed',
// Installer // Installer
'installation_url' => 'PHPCI Installation URL', 'installation_url' => 'PHPCI Installation URL',
'db_host' => 'Database Host', 'db_host' => 'Database Host',
@ -295,9 +332,9 @@ PHPCI',
Please review the errors above before continuing.', Please review the errors above before continuing.',
'must_be_valid_email' => 'Must be a valid email address.', 'must_be_valid_email' => 'Must be a valid email address.',
'must_be_valid_url' => 'Must be a valid URL.', 'must_be_valid_url' => 'Must be a valid URL.',
'enter_name' => 'Admin Name:', 'enter_name' => 'Admin Name: ',
'enter_email' => 'Admin Email:', 'enter_email' => 'Admin Email: ',
'enter_password' => 'Admin Password:', 'enter_password' => 'Admin Password: ',
'enter_phpci_url' => 'Your PHPCI URL ("http://phpci.local" for example): ', 'enter_phpci_url' => 'Your PHPCI URL ("http://phpci.local" for example): ',
'enter_db_host' => 'Please enter your MySQL host [localhost]: ', 'enter_db_host' => 'Please enter your MySQL host [localhost]: ',
@ -330,6 +367,15 @@ PHPCI',
'create_admin_user' => 'Create an admin user', 'create_admin_user' => 'Create an admin user',
'incorrect_format' => 'Incorrect format', 'incorrect_format' => 'Incorrect format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
'add_to_queue_failed' => 'Build created successfully, but failed to add to build queue. This usually happens
when PHPCI is set to use a beanstalkd server that does not exist,
or your beanstalkd server has stopped.',
// Run Command // Run Command
'run_all_pending' => 'Run all pending PHPCI builds.', 'run_all_pending' => 'Run all pending PHPCI builds.',
'finding_builds' => 'Finding builds to process', 'finding_builds' => 'Finding builds to process',
@ -377,5 +423,22 @@ PHPCI',
'build_file_missing' => 'Specified build file does not exist.', 'build_file_missing' => 'Specified build file does not exist.',
'property_file_missing' => 'Specified property file does not exist.', 'property_file_missing' => 'Specified property file does not exist.',
'could_not_process_report' => 'Could not process the report generated by this tool.', 'could_not_process_report' => 'Could not process the report generated by this tool.',
'shell_not_enabled' => 'The shell plugin is not enabled. Please enable it via config.yml.' 'shell_not_enabled' => 'The shell plugin is not enabled. Please enable it via config.yml.',
// Error Levels:
'critical' => 'Critical',
'high' => 'High',
'normal' => 'Normal',
'low' => 'Low',
// Plugins that generate errors:
'php_mess_detector' => 'PHP Mess Detector',
'php_code_sniffer' => 'PHP Code Sniffer',
'php_unit' => 'PHP Unit',
'php_cpd' => 'PHP Copy/Paste Detector',
'php_docblock_checker' => 'PHP Docblock Checker',
'behat' => 'Behat',
'technical_debt' => 'Technical Debt',
); );

387
PHPCI/Languages/lang.es.php Normal file
View file

@ -0,0 +1,387 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
$strings = array(
'language_name' => 'Español',
'language' => 'Lenguaje',
// Log in:
'log_in_to_phpci' => 'Ingresar a PHPCI',
'login_error' => 'Email o contraseña incorrectos',
'forgotten_password_link' => '¿Olvidaste tu contraseña?',
'reset_emailed' => 'Te hemos enviado un email para reiniciar tu contraseña.',
'reset_header' => '<strong>¡No te preocupes!</strong><br>Solo tienes que ingresar tu dirección de email
y te enviaremos por email un enlace para reiniciar tu contraseña.',
'reset_email_address' => 'Ingresa tu dirección de email:',
'reset_send_email' => 'Enviar enlace',
'reset_enter_password' => 'Ingresa una nueva contraseña',
'reset_new_password' => 'Nueva contraseña:',
'reset_change_password' => 'Cambiar contraseña',
'reset_no_user_exists' => 'No existe ningún usuario con ese email, por favor intenta nuevamente.',
'reset_email_body' => 'Hola %s,
Has recibido este correo porque , o alguien más, ha solicitado reiniciar la contraseña de PHPCI
Si fuiste , por favor haz click en el siguiente enlace para reiniciar tu contraseña: %ssession/reset-password/%d/%s
De lo contrario, por favor ignora este correo y ninguna acción será realizada.
Gracias,
PHPCI',
'reset_email_title' => 'Reiniciar contraseña de PHPCI para %s',
'reset_invalid' => 'Pedido inválido.',
'email_address' => 'Dirección de email',
'password' => 'Contraseña',
'log_in' => 'Ingresar',
// Top Nav
'toggle_navigation' => 'Activar navegación',
'n_builds_pending' => '%d builds pendientes',
'n_builds_running' => '%d builds ejecutándose',
'edit_profile' => 'Editar Perfil',
'sign_out' => 'Cerrar Sesión',
'branch_x' => 'Rama: %s',
'created_x' => 'Creada el: %s',
'started_x' => 'Comenzó: %s',
// Sidebar
'hello_name' => 'Hola, %s',
'dashboard' => 'Escritorio',
'admin_options' => 'Opciones de Admin.',
'add_project' => 'Agregar Proyecto',
'settings' => 'Configuración',
'manage_users' => 'Administrar Usuarios',
'plugins' => 'Plugins',
'view' => 'Vista',
'build_now' => 'Ejecutar Build',
'edit_project' => 'Editar Proyecto',
'delete_project' => 'Eliminar Proyecto',
// Project Summary:
'no_builds_yet' => '¡No existen builds aún!',
'x_of_x_failed' => '%d de los últimos %d builds fallaron.',
'x_of_x_failed_short' => '%d / %d fallaron.',
'last_successful_build' => ' El último build exitoso fue %s.',
'never_built_successfully' => ' Este proyecto nunca tuvo un build exitoso.',
'all_builds_passed' => 'Todos los últimos %d builds pasaron.',
'all_builds_passed_short' => '%d / %d pasaron.',
'last_failed_build' => ' El último build en fallar fue %s.',
'never_failed_build' => ' Este proyecto no tiene ningún build fallido.',
'view_project' => 'Ver Proyecto',
// Timeline:
'latest_builds' => 'Últimos builds',
'pending' => 'Pediente',
'running' => 'Ejecutando',
'success' => 'Éxito',
'successful' => 'Exitoso',
'failed' => 'Falló',
'manual_build' => 'Build Manual',
// Add/Edit Project:
'new_project' => 'Nuevo Proyecto',
'project_x_not_found' => 'El Proyecto con ID %d no existe.',
'project_details' => 'Detalles del Proyecto',
'public_key_help' => 'Para facilitarte, hemos generado un par de llaves SSH para que uses en este proyecto.
Para usarlo, sólo agrega la siguiente llave pública a la sección de "deploy keys"
de tu plataforma de hosting de versionado de código.',
'select_repository_type' => 'Selecciona tipo de repositorio...',
'github' => 'GitHub',
'bitbucket' => 'Bitbucket',
'gitlab' => 'GitLab',
'remote' => 'URL Remota',
'local' => 'Path local',
'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => '¿Dónde está alojado tu proyecto?',
'choose_github' => 'Selecciona un repositorio de GitHub:',
'repo_name' => 'Nombre del repositorio / URL (Remoto) o Ruta (Local)',
'project_title' => 'Titulo del proyecto',
'project_private_key' => 'Clave privada a usar para acceder al repositorio
(dejar en blanco para remotos locales o anónimos)',
'build_config' => 'Configuración PHPCI para builds del proyecto
(en caso que no puedas agregar el archivo phpci.yml al repositorio)',
'default_branch' => 'Nombre de la rama por defecto',
'allow_public_status' => '¿Activar página pública con el estado del proyecto?',
'archived' => 'Archivado',
'archived_menu' => 'Archivado',
'save_project' => 'Guardar Proyecto',
'error_mercurial' => 'La URL del repositorio de Mercurial debe comenzar con http:// or https://',
'error_remote' => 'La URL del repositorio debe comenzar con git://, http:// or https://',
'error_gitlab' => 'El nombre del repositorio de GitLab debe tener el formato "user@domain.tld:owner/repo.git"',
'error_github' => 'El nombre del repositorio debe tener el formato "owner/repo"',
'error_bitbucket' => 'El nombre del repo debe tener el formato "owner/repo"',
'error_path' => 'La ruta especificada no existe.',
// View Project:
'all_branches' => 'Todas las ramas',
'builds' => 'Builds',
'id' => 'ID',
'project' => 'Proyecto',
'commit' => 'Commit',
'branch' => 'Rama',
'status' => 'Estado',
'prev_link' => '&laquo; Anterior',
'next_link' => 'Siguiente &raquo;',
'public_key' => 'Llave pública',
'delete_build' => 'Eliminar Build',
'webhooks' => 'Webhooks',
'webhooks_help_github' => 'Para compilar automáticamente este proyecto cada vez que se realiza un commit, agreagar la siguiente URL
como un nuevo "webhook" en la sección <a href="https://github.com/%s/settings/hooks">Webhooks
and Services</a> de tu repositorio en GitHub.',
'webhooks_help_gitlab' => 'Para compilar automáticamente este proyecto, cada vez que se realiza un commit, agreagar la siguiente URL
como una "WebHook URL" en la sección "web hooks" de tu repositorio en GitLab.',
'webhooks_help_bitbucket' => 'Para compilar automáticamente este proyecto, cada vez que se realiza un commit, agreagar la siguiente URL
como un servicio "POST" en la sección
<a href="https://bitbucket.org/%s/admin/services">
Services</a> de tu repositorio en Bitbucket.',
// View Build
'build_x_not_found' => 'El build con ID %d no existe.',
'build_n' => 'Build %d',
'rebuild_now' => 'Rebuild Ahora',
'committed_by_x' => 'Commit hecho por %s',
'commit_id_x' => 'Commit: %s',
'chart_display' => 'Este gráfico será mostrado una vez que el build se haya completado.',
'build' => 'Build',
'lines' => 'Líneas',
'comment_lines' => 'Líneas de comentario',
'noncomment_lines' => 'Líneas no comentario',
'logical_lines' => 'Líneas lógicas',
'lines_of_code' => 'Líneas de código',
'build_log' => 'Log',
'quality_trend' => 'Tendencia de calidad',
'codeception_errors' => 'Errores de Codeception',
'phpmd_warnings' => 'PHPMD Warnings',
'phpcs_warnings' => 'PHPCS Warnings',
'phpcs_errors' => 'PHPCS Errors',
'phplint_errors' => 'Lint Errors',
'phpunit_errors' => 'PHPUnit Errors',
'phpdoccheck_warnings' => 'Docblocks faltantes',
'issues' => 'Incidencias',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Missing Docblocks',
'phpmd' => 'PHP Mess Detector',
'phpspec' => 'PHP Spec',
'phpunit' => 'PHP Unit',
'technical_debt' => 'Deuda Técnica',
'behat' => 'Behat',
'file' => 'Archivo',
'line' => 'Línea',
'class' => 'Clase',
'method' => 'Método',
'message' => 'Mensaje',
'start' => 'Inicio',
'end' => 'Fin',
'from' => 'De',
'to' => 'Para',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultado',
'ok' => 'OK',
'took_n_seconds' => 'Tomó %d segundos',
'build_created' => 'Build Creado',
'build_started' => 'Build Comenzado',
'build_finished' => 'Build Terminado',
// Users
'name' => 'Nombre',
'password_change' => 'Contraseña (dejar en blanco si no quiere cambiarla)',
'save' => 'Guardar &raquo;',
'update_your_details' => 'Actualizar los detalles',
'your_details_updated' => 'Tus detalles han sido actualizados.',
'add_user' => 'Agregar Usuario',
'is_admin' => '¿Es Admin?',
'yes' => 'Si',
'no' => 'No',
'edit' => 'Editar',
'edit_user' => 'Editar Usuario',
'delete_user' => 'Delete Usuario',
'user_n_not_found' => 'Usuario con ID %d no existe.',
'is_user_admin' => '¿Es un usuario administrador?',
'save_user' => 'Guardar Usuario',
// Settings:
'settings_saved' => 'Tu configuración ha sido guardada.',
'settings_check_perms' => 'Tu configuración no fue guardada, verificar los permisos del archivo config.yml.',
'settings_cannot_write' => 'PHPCI no puede escribir en el archivo config.yml file, la configuración no será guardada correctamente
hasta no corregir esto.',
'settings_github_linked' => 'Tu cuenta GitHub ha sido conectada.',
'settings_github_not_linked' => 'No se pudo conectar a tu cuenta GitHub.',
'build_settings' => 'Configuración del Build ',
'github_application' => 'Aplicación GitHub',
'github_sign_in' => 'Antes de comenzar a utilizar GitHub, tienes que <a href="%s">ingresar</a> y permitir
el acceso a tu cuenta a PHPCI.',
'github_phpci_linked' => 'PHPCI ha sido conectado a tu cuenta GitHub.',
'github_where_to_find' => 'Donde encontrar estos...',
'github_where_help' => 'Si eres priopietario de la aplicaión que quieres usar, puedes encontrar esta información en
el área de configuración de <a href="https://github.com/settings/applications">aplicaciones</a>.',
'email_settings' => 'Configuraciones de Email',
'email_settings_help' => 'Para que PHPCI pueda enviar email con el status de los builds,
debes configurar las siguientes propiedades SMTP.',
'application_id' => 'ID de aplicación',
'application_secret' => 'Application Secret',
'smtp_server' => 'Servidor SMTP',
'smtp_port' => 'Puerto SMTP',
'smtp_username' => 'Usuario SMTP',
'smtp_password' => 'Contraseña SMTP',
'from_email_address' => 'Dirección de email DE',
'default_notification_address' => 'Dirección de correo de notificación por defecto',
'use_smtp_encryption' => 'Usar encriptación SMTP?',
'none' => 'None',
'ssl' => 'SSL',
'tls' => 'TLS',
'failed_after' => 'Considerar el build como fallido luego de ',
'5_mins' => '5 Minutos',
'15_mins' => '15 Minutos',
'30_mins' => '30 Minutos',
'1_hour' => '1 Hora',
'3_hours' => '3 Horas',
// Plugins
'cannot_update_composer' => 'PHPCI no puede actualizar composer.json porque no tiene permisos de escritura.',
'x_has_been_removed' => '%s ha sido elimiando.',
'x_has_been_added' => '%s ha sido agregado a composer.json y será instalado la próxima vez que ejecutes composer update.',
'enabled_plugins' => 'Activar Plugins',
'provided_by_package' => 'Provisto por Paquete',
'installed_packages' => 'Paquetes Instalados',
'suggested_packages' => 'Paquetes Sugeridos',
'title' => 'Título',
'description' => 'Descripción',
'version' => 'Versión',
'install' => 'Instalar &raquo;',
'remove' => 'Eliminar &raquo;',
'search_packagist_for_more' => 'Buscar más paquetes en Packagist',
'search' => 'Buscar &raquo;',
// Installer
'installation_url' => 'URL de la instalación PHPCI',
'db_host' => 'Host',
'db_name' => 'Nombre de la base de datos',
'db_user' => 'Usuario de la base de datos',
'db_pass' => 'Clave de la base de datos',
'admin_name' => 'Nombre del Admin',
'admin_pass' => 'Clave del Admin',
'admin_email' => 'Email de Admin',
'config_path' => 'Ruta al archivo config',
'install_phpci' => 'Instalar PHPCI',
'welcome_to_phpci' => 'Bienvenido a PHPCI',
'please_answer' => 'Por favor, responde las siguientes preguntas:',
'phpci_php_req' => 'PHPCI requiere al menos PHP 5.3.8 para funcionar.',
'extension_required' => 'Extensión requerida: %s',
'function_required' => 'PHPCI debe poder invocar la función %s(). Está deshabilitada en php.ini?',
'requirements_not_met' => 'PHPCI no pudo ser instalado, ya que no se cumplen todos los requerimientos.
Por favor, corrige los errores antes de continuar.',
'must_be_valid_email' => 'Debe ser una dirección de correos válida.',
'must_be_valid_url' => 'Debe ser una URL válida.',
'enter_name' => 'Nombre del Admin:',
'enter_email' => 'Email del Admin:',
'enter_password' => 'Contraseña de Admin:',
'enter_phpci_url' => 'La URL de PHPCI ("Por ejemplo: http://phpci.local"): ',
'enter_db_host' => 'Por favor, ingresa el servidor MySQL [localhost]: ',
'enter_db_name' => 'Por favor, ingresa el nombre de la base de datos MySQL [phpci]: ',
'enter_db_user' => 'Por favor, ingresa el usuario MySQL [phpci]: ',
'enter_db_pass' => 'Por favor, ingresa la contraseña MySQL: ',
'could_not_connect' => 'PHPCI no pudo conectarse a MySQL con los datos dados. Por favor, intenta nuevamente.',
'setting_up_db' => 'Configurando base de datos... ',
'user_created' => '¡Cuenta de usuario creada!',
'failed_to_create' => 'PHPCI no pudo crear la cuenta de admin.',
'config_exists' => 'El archivo config de PHPCI ya existe y no es vacío.',
'update_instead' => 'Si está intentando actualizar PHPCI, por favor, utiliza phpci:update.',
// Update
'update_phpci' => 'Actuliza la base de datos para reflejar los modelos actualizados.',
'updating_phpci' => 'Actualizando base de datos PHPCI: ',
'not_installed' => 'PHPCI no está instalado.',
'install_instead' => 'Por favor, instala PHPCI via phpci:install.',
// Poll Command
'poll_github' => 'Chequear en GitHub si se necesita comenzar un Build.',
'no_token' => 'No se encontró ningún token GitHub',
'finding_projects' => 'Buscando proyectos para chequear',
'found_n_projects' => 'Se encontraron %d proyectos',
'last_commit_is' => 'El último commit en GitHub para %s es %s',
'adding_new_build' => 'Último commit es diferente a la base de datos, agregando nuevo build.',
'finished_processing_builds' => 'Fin de procesamiento de builds.',
// Create Admin
'create_admin_user' => 'Crear un usuario Admin',
'incorrect_format' => 'Formato incorrecto',
// Run Command
'run_all_pending' => 'Ejecutar todos los builds PHPCI pendientes.',
'finding_builds' => 'Buscando builds a procesar',
'found_n_builds' => 'Se encontraron %d builds',
'skipping_build' => 'Saltando Build %d - Build del proyecto ya en ejecución.',
'marked_as_failed' => 'Build %d falló debido a timeout.',
// Builder
'missing_phpci_yml' => 'Este proyecto no contiene el archivo phpci.yml o está vacío.',
'build_success' => 'BUILD EXITOSO',
'build_failed' => 'BUILD FALLIDO',
'removing_build' => 'Eliminando Build.',
'exception' => 'Excepción: ',
'could_not_create_working' => 'Imposible crear copia de trabajo.',
'working_copy_created' => 'Copia de trabajo creada: %s',
'looking_for_binary' => 'Buscando binario: %s',
'found_in_path' => 'Encontrado en %s: %s',
'running_plugin' => 'EJECUTANDO PLUGIN: %s',
'plugin_success' => 'PLUGIN: EXITO',
'plugin_failed' => 'PLUGIN: FALLÓ',
'plugin_missing' => 'No existe el plugin: %s',
'tap_version' => 'TapParser únicamente soporta la verisón 13 de TAP',
'tap_error' => 'Cadena de caracteres TAP inválida, el número de tests no coincide con la cuenta de tests declarada.',
// Build Plugins:
'no_tests_performed' => 'No se ejecutaron tests.',
'could_not_find' => 'No se encontró %s',
'no_campfire_settings' => 'No se especificaron parámetros de conexión para el plugin Campfire',
'failed_to_wipe' => 'Imposible eliminar directorio existente %s antes de copiarlo',
'passing_build' => 'Build Exitoso',
'failing_build' => 'Build Fallido',
'log_output' => 'Log de Salida: ',
'n_emails_sent' => '%d emails enviados.',
'n_emails_failed' => '%d emails no pudieron ser enviados.',
'unable_to_set_env' => 'Imposible setear variable de entorno',
'tag_created' => 'Tag creado por PHPCI: %s',
'x_built_at_x' => 'Build de %PROJECT_TITLE% en %BUILD_URI%',
'hipchat_settings' => 'Por favor, definir room y authToken para el plugin hipchat_notify',
'irc_settings' => 'Debes configurar un servidor, room y apodo.',
'invalid_command' => 'Comando inválido',
'import_file_key' => 'Sentencia de importación debe contener una llave \'file\'',
'cannot_open_import' => 'Imposible abrir archivo de importación SQL: %s',
'unable_to_execute' => 'Imposible ejecutar archivo SQL',
'phar_internal_error' => 'Error interno en plugin Phar',
'build_file_missing' => 'El archivo de build especificado no existe.',
'property_file_missing' => 'El archivo de propiedades especificado no existe.',
'could_not_process_report' => 'Imposible procesar el reporte generado por la herramienta.',
'shell_not_enabled' => 'El plugin shell no está habilitado. Por favor, habilitalo desde config.yml.'
);

View file

@ -14,21 +14,21 @@ $strings = array(
// Log in: // Log in:
'log_in_to_phpci' => 'Connectez-vous à PHPCI', 'log_in_to_phpci' => 'Connectez-vous à PHPCI',
'login_error' => 'Adresse email ou mot de passe invalide', 'login_error' => 'Adresse email ou mot de passe invalide',
'forgotten_password_link' => 'Mot de passe oublié ?', 'forgotten_password_link' => 'Mot de passe oublié&nbsp;?',
'reset_emailed' => 'Nous vous avons envoyé un email avec un lien pour réinitialiser votre mot de passe.', 'reset_emailed' => 'Nous vous avons envoyé un email avec un lien pour réinitialiser votre mot de passe.',
'reset_header' => '<strong>Pas d\'inquiétude</strong><br>Entrez simplement votre adresse email ci-dessous 'reset_header' => '<strong>Pas d\'inquiétude</strong><br>Entrez simplement votre adresse email ci-dessous
et nous vous enverrons un message avec un lien pour réinitialiser votre mot de passe.', et nous vous enverrons un message avec un lien pour réinitialiser votre mot de passe.',
'reset_email_address' => 'Entrez votre adresse email:', 'reset_email_address' => 'Entrez votre adresse email:',
'reset_send_email' => 'Envoyer le mail', 'reset_send_email' => 'Envoyer le mail',
'reset_enter_password' => 'Veuillez entrer un nouveau mot de passe', 'reset_enter_password' => 'Veuillez entrer un nouveau mot de passe',
'reset_new_password' => 'Nouveau mot de passe :', 'reset_new_password' => 'Nouveau mot de passe&nbsp;:',
'reset_change_password' => 'Modifier le mot de passe', 'reset_change_password' => 'Modifier le mot de passe',
'reset_no_user_exists' => 'Il n\'existe aucun utilisateur avec cette adresse email, merci de réessayer.', 'reset_no_user_exists' => 'Il n\'existe aucun utilisateur avec cette adresse email, merci de réessayer.',
'reset_email_body' => 'Bonjour %s, 'reset_email_body' => 'Bonjour %s,
Vous avez reçu cet email parce qu\'une demande de réinitialisation de mot de passe a été faite pour votre compte PHPCI. Vous avez reçu cet email parce qu\'une demande de réinitialisation de mot de passe a été faite pour votre compte PHPCI.
Si c\'est bien vous, merci de cliquer sur le lien suivant pour réinitialiser votre mot de passe : %ssession/reset-password/%d/%s Si c\'est bien vous, merci de cliquer sur le lien suivant pour réinitialiser votre mot de passe&nbsp;: %ssession/reset-password/%d/%s
Sinon, merci d\'ignorer ce message. Sinon, merci d\'ignorer ce message.
@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Réinitialisation du mot de passe PHPCI pour %s', 'reset_email_title' => 'Réinitialisation du mot de passe PHPCI pour %s',
'reset_invalid' => 'Requête de réinitialisation de mot de passe invalide.', 'reset_invalid' => 'Requête de réinitialisation de mot de passe invalide.',
'email_address' => 'Adresse email', 'email_address' => 'Adresse email',
'login' => 'Login / Email Address',
'password' => 'Mot de passe', 'password' => 'Mot de passe',
'log_in' => 'Connexion', 'log_in' => 'Connexion',
@ -49,9 +50,9 @@ PHPCI',
'n_builds_running' => '%d builds en cours d\'exécution', 'n_builds_running' => '%d builds en cours d\'exécution',
'edit_profile' => 'Éditer le profil', 'edit_profile' => 'Éditer le profil',
'sign_out' => 'Déconnexion', 'sign_out' => 'Déconnexion',
'branch_x' => 'Branche : %s', 'branch_x' => 'Branche&nbsp;: %s',
'created_x' => 'Créé à : %s', 'created_x' => 'Créé à&nbsp;: %s',
'started_x' => 'Démarré à : %s', 'started_x' => 'Démarré à&nbsp;: %s',
// Sidebar // Sidebar
'hello_name' => 'Salut %s', 'hello_name' => 'Salut %s',
@ -67,19 +68,19 @@ PHPCI',
'delete_project' => 'Supprimer le projet', 'delete_project' => 'Supprimer le projet',
// Project Summary: // Project Summary:
'no_builds_yet' => 'Aucun build pour le moment!', 'no_builds_yet' => 'Aucun build pour le moment&nbsp;!',
'x_of_x_failed' => '%d parmis les derniers %d builds ont échoué.', 'x_of_x_failed' => '%d des %d derniers builds ont échoué.',
'x_of_x_failed_short' => '%d / %d ont échoué.', 'x_of_x_failed_short' => '%d échecs / %d.',
'last_successful_build' => ' Le dernier build qui a réussi est %s.', 'last_successful_build' => ' Le dernier build réussi date du %s.',
'never_built_successfully' => ' Aucun build n\'a été exécuté avec succès sur ce projet.', 'never_built_successfully' => ' Aucun build de ce projet n\'a réussi.',
'all_builds_passed' => 'Les derniers %d builds ont réussis.', 'all_builds_passed' => 'Les %d derniers builds ont réussi.',
'all_builds_passed_short' => '%d / %d ont réussis.', 'all_builds_passed_short' => '%d réussites / %d.',
'last_failed_build' => ' Le dernier build en échec est %s.', 'last_failed_build' => ' Le dernier build en échec date du %s.',
'never_failed_build' => ' Ce projet n\'a jamais eu un build en échec.', 'never_failed_build' => ' Aucun build de ce projet n\'a échoué.',
'view_project' => 'Voir le projet', 'view_project' => 'Voir le projet',
// Timeline: // Timeline:
'latest_builds' => 'Les derniers Builds', 'latest_builds' => 'Derniers builds',
'pending' => 'En attente', 'pending' => 'En attente',
'running' => 'En cours', 'running' => 'En cours',
'success' => 'Terminé', 'success' => 'Terminé',
@ -102,8 +103,8 @@ PHPCI',
'local' => 'Chemin local', 'local' => 'Chemin local',
'hg' => 'Mercurial', 'hg' => 'Mercurial',
'where_hosted' => 'Où est hébergé votre projet ?', 'where_hosted' => 'Où est hébergé votre projet&nbsp;?',
'choose_github' => 'Choisissez un dépôt GitHub :', 'choose_github' => 'Choisissez un dépôt GitHub&nbsp;:',
'repo_name' => 'Nom du dépôt / URL (distance) ou chemin (local)', 'repo_name' => 'Nom du dépôt / URL (distance) ou chemin (local)',
'project_title' => 'Titre du projet', 'project_title' => 'Titre du projet',
@ -112,7 +113,9 @@ PHPCI',
'build_config' => 'Configuration PHPCI spécifique pour ce projet 'build_config' => 'Configuration PHPCI spécifique pour ce projet
(si vous ne pouvez pas ajouter de fichier phpci.yml à la racine du dépôt)', (si vous ne pouvez pas ajouter de fichier phpci.yml à la racine du dépôt)',
'default_branch' => 'Nom de la branche par défaut', 'default_branch' => 'Nom de la branche par défaut',
'allow_public_status' => 'Activer la page de statut publique et l\'image pour ce projet ?', 'allow_public_status' => 'Activer la page de statut publique et l\'image pour ce projet&nbsp;?',
'archived' => 'Archived',
'archived_menu' => 'Archived',
'save_project' => 'Enregistrer le projet', 'save_project' => 'Enregistrer le projet',
'error_mercurial' => 'Les URLs de dépôt Mercurial doivent commencer par http:// ou https://', 'error_mercurial' => 'Les URLs de dépôt Mercurial doivent commencer par http:// ou https://',
@ -126,6 +129,7 @@ PHPCI',
'all_branches' => 'Toutes les branches', 'all_branches' => 'Toutes les branches',
'builds' => 'Builds', 'builds' => 'Builds',
'id' => 'ID', 'id' => 'ID',
'date' => 'Date',
'project' => 'Projet', 'project' => 'Projet',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Branche', 'branch' => 'Branche',
@ -155,7 +159,7 @@ PHPCI',
'committed_by_x' => 'Committé par %s', 'committed_by_x' => 'Committé par %s',
'commit_id_x' => 'Commit: %s', 'commit_id_x' => 'Commit&nbsp;: %s',
'chart_display' => 'Ce graphique s\'affichera une fois que le build sera terminé.', 'chart_display' => 'Ce graphique s\'affichera une fois que le build sera terminé.',
@ -167,6 +171,7 @@ PHPCI',
'lines_of_code' => 'Lignes de code', 'lines_of_code' => 'Lignes de code',
'build_log' => 'Log du build', 'build_log' => 'Log du build',
'quality_trend' => 'Tendance de la qualité', 'quality_trend' => 'Tendance de la qualité',
'codeception_errors' => 'Erreurs Codeception',
'phpmd_warnings' => 'Alertes PHPMD', 'phpmd_warnings' => 'Alertes PHPMD',
'phpcs_warnings' => 'Alertes PHPCS', 'phpcs_warnings' => 'Alertes PHPCS',
'phpcs_errors' => 'Erreurs PHPCS', 'phpcs_errors' => 'Erreurs PHPCS',
@ -175,6 +180,7 @@ PHPCI',
'phpdoccheck_warnings' => 'Blocs de documentation manquants', 'phpdoccheck_warnings' => 'Blocs de documentation manquants',
'issues' => 'Tickets', 'issues' => 'Tickets',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Missing Docblocks', 'phpdoccheck' => 'Missing Docblocks',
@ -191,14 +197,20 @@ PHPCI',
'end' => 'Fin', 'end' => 'Fin',
'from' => 'À partir de', 'from' => 'À partir de',
'to' => 'jusque', 'to' => 'jusque',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultat', 'result' => 'Resultat',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Exécuté en %d secondes', 'took_n_seconds' => 'Exécuté en %d secondes',
'build_created' => 'Build créé', 'build_created' => 'Build créé',
'build_started' => 'Build démarré', 'build_started' => 'Build démarré',
'build_finished' => 'Build terminé', 'build_finished' => 'Build terminé',
'test_message' => 'Message',
'test_no_message' => 'Pas de message',
'test_success' => 'Réussi(s): %d',
'test_fail' => 'Echec(s): %d',
'test_skipped' => 'Passé(s): %d',
'test_error' => 'Erreurs: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Nom', 'name' => 'Nom',
@ -207,7 +219,7 @@ PHPCI',
'update_your_details' => 'Mettre à jour vos préférences', 'update_your_details' => 'Mettre à jour vos préférences',
'your_details_updated' => 'Vos préférences ont été bien mises à jour.', 'your_details_updated' => 'Vos préférences ont été bien mises à jour.',
'add_user' => 'Ajouter un utilisateur', 'add_user' => 'Ajouter un utilisateur',
'is_admin' => 'Est-il administrateur ?', 'is_admin' => 'Est-il administrateur&nbsp;?',
'yes' => 'Oui', 'yes' => 'Oui',
'no' => 'Non', 'no' => 'Non',
'edit' => 'Éditer', 'edit' => 'Éditer',
@ -261,7 +273,7 @@ PHPCI',
// Plugins // Plugins
'cannot_update_composer' => 'PHPCI ne peut pas mettre à jour le fichier composer.json pour vous, il n\'est pas modifiable.', 'cannot_update_composer' => 'PHPCI ne peut pas mettre à jour le fichier composer.json pour vous, il n\'est pas modifiable.',
'x_has_been_removed' => '%s a été supprimé.', 'x_has_been_removed' => '%s a été supprimé.',
'x_has_been_added' => '%s a été ajouté au fichier composer.json poru vous et il sera installé la prochaine fois 'x_has_been_added' => '%s a été ajouté au fichier composer.json pour vous et il sera installé la prochaine fois
que vous lancerez "composer update".', que vous lancerez "composer update".',
'enabled_plugins' => 'Plugins activés', 'enabled_plugins' => 'Plugins activés',
'provided_by_package' => 'Fournis par le paquet', 'provided_by_package' => 'Fournis par le paquet',
@ -275,6 +287,17 @@ PHPCI',
'search_packagist_for_more' => 'Rechercher sur Packagist pour trouver plus de paquets', 'search_packagist_for_more' => 'Rechercher sur Packagist pour trouver plus de paquets',
'search' => 'Rechercher &raquo;', 'search' => 'Rechercher &raquo;',
// Summary plugin
'build-summary' => 'Résumé',
'stage' => 'Étape',
'duration' => 'Durée',
'plugin' => 'Plugin',
'stage_setup' => 'Préparation',
'stage_test' => 'Test',
'stage_complete' => 'Terminé',
'stage_success' => 'Succes',
'stage_failure' => 'Échec',
// Installer // Installer
'installation_url' => 'URL d\'installation de PHPCI', 'installation_url' => 'URL d\'installation de PHPCI',
'db_host' => 'Hôte de la BDD', 'db_host' => 'Hôte de la BDD',
@ -287,17 +310,17 @@ PHPCI',
'config_path' => 'Chemin vers le fichier de configuration', 'config_path' => 'Chemin vers le fichier de configuration',
'install_phpci' => 'Installer PHPCI', 'install_phpci' => 'Installer PHPCI',
'welcome_to_phpci' => 'Bienvenue sur PHPCI', 'welcome_to_phpci' => 'Bienvenue sur PHPCI',
'please_answer' => 'Merci de répondre aux questions suivantes:', 'please_answer' => 'Merci de répondre aux questions suivantes :',
'phpci_php_req' => 'PHPCI requiert au moins PHP 5.3.8 pour fonctionner.', 'phpci_php_req' => 'PHPCI requiert au moins PHP 5.3.8 pour fonctionner.',
'extension_required' => 'Extensions requises: %s', 'extension_required' => 'Extensions requises : %s',
'function_required' => 'PHPCI doit être capable d\'appeler la fonction %s(). Est-ce qu\'elle est désactivée dans votre php.ini?', 'function_required' => 'PHPCI doit être capable d\'appeler la fonction %s(). Est-ce qu\'elle est désactivée dans votre php.ini?',
'requirements_not_met' => 'PHPCI ne peut pas être installé parce que toutes les conditions requises ne sont pas respectées. 'requirements_not_met' => 'PHPCI ne peut pas être installé parce que toutes les conditions requises ne sont pas respectées.
Merci de corriger les erreurs ci-dessus avant de continuer.', Merci de corriger les erreurs ci-dessus avant de continuer.',
'must_be_valid_email' => 'Doit être une adresse email valide.', 'must_be_valid_email' => 'Doit être une adresse email valide.',
'must_be_valid_url' => 'Doit être une URL valide.', 'must_be_valid_url' => 'Doit être une URL valide.',
'enter_name' => 'Nom de l\'admin:', 'enter_name' => 'Nom de l\'admin: ',
'enter_email' => 'Email de l\'admin:', 'enter_email' => 'Email de l\'admin: ',
'enter_password' => 'Mot de passe de l\'admin:', 'enter_password' => 'Mot de passe de l\'admin: ',
'enter_phpci_url' => 'Votre URL vers PHPCI (par exemple "http://phpci.local"): ', 'enter_phpci_url' => 'Votre URL vers PHPCI (par exemple "http://phpci.local"): ',
'enter_db_host' => 'Merci d\'entrer le nom d\'hôte MySQL [localhost]: ', 'enter_db_host' => 'Merci d\'entrer le nom d\'hôte MySQL [localhost]: ',
@ -306,7 +329,7 @@ PHPCI',
'enter_db_pass' => 'Merci d\'entrer le mot de passe MySQL: ', 'enter_db_pass' => 'Merci d\'entrer le mot de passe MySQL: ',
'could_not_connect' => 'PHPCI ne peut pas se connecter à MySQL à partir des informations fournies. Veuillez réessayer..', 'could_not_connect' => 'PHPCI ne peut pas se connecter à MySQL à partir des informations fournies. Veuillez réessayer..',
'setting_up_db' => 'Paramétrage de la base de données... ', 'setting_up_db' => 'Paramétrage de la base de données... ',
'user_created' => 'Le compte utilisateur a été créé!', 'user_created' => 'Le compte utilisateur a été créé !',
'failed_to_create' => 'PHPCI n\'a pas réussi à créer votre compte admin.', 'failed_to_create' => 'PHPCI n\'a pas réussi à créer votre compte admin.',
'config_exists' => 'Le fichier de configuration PHPCI existe et n\'est pas vide.', 'config_exists' => 'Le fichier de configuration PHPCI existe et n\'est pas vide.',
'update_instead' => 'Si vous essayez de mettre à jour PHPCI, merci d\'utiliser la commande phpci:update.', 'update_instead' => 'Si vous essayez de mettre à jour PHPCI, merci d\'utiliser la commande phpci:update.',
@ -330,6 +353,12 @@ PHPCI',
'create_admin_user' => 'Créer un utilisateur admin', 'create_admin_user' => 'Créer un utilisateur admin',
'incorrect_format' => 'Format incorrect', 'incorrect_format' => 'Format incorrect',
// Create Build Command
'create_build_project' => 'Créer un build projet',
'project_id_argument' => 'ID du projet',
'commit_id_option' => 'ID du commit',
'branch_name_option' => 'Branche',
// Run Command // Run Command
'run_all_pending' => 'Démarrage de tout les builds PHPCI en attente.', 'run_all_pending' => 'Démarrage de tout les builds PHPCI en attente.',
'finding_builds' => 'Découverte des builds à traiter', 'finding_builds' => 'Découverte des builds à traiter',
@ -365,7 +394,7 @@ PHPCI',
'n_emails_sent' => '%d emails envoyés.', 'n_emails_sent' => '%d emails envoyés.',
'n_emails_failed' => '%d emails dont l\'envoi a échoué.', 'n_emails_failed' => '%d emails dont l\'envoi a échoué.',
'unable_to_set_env' => 'Impossible d\'initialiser la variable d\'environnement', 'unable_to_set_env' => 'Impossible d\'initialiser la variable d\'environnement',
'tag_created' => 'Tag créé par PHPCI: %s', 'tag_created' => 'Tag créé par PHPCI : %s',
'x_built_at_x' => '%PROJECT_TITLE% construit à %BUILD_URI%', 'x_built_at_x' => '%PROJECT_TITLE% construit à %BUILD_URI%',
'hipchat_settings' => 'Merci de définir une "room" et un "authToken" pour le plugin hipchat_notify', 'hipchat_settings' => 'Merci de définir une "room" et un "authToken" pour le plugin hipchat_notify',
'irc_settings' => 'Vous devez configurer un serveur, une "room" et un "nick".', 'irc_settings' => 'Vous devez configurer un serveur, une "room" et un "nick".',

View file

@ -16,20 +16,19 @@ $strings = array(
'log_in_to_phpci' => 'Accedi a PHPCI', 'log_in_to_phpci' => 'Accedi a PHPCI',
'login_error' => 'Indirizzo email o password errati', 'login_error' => 'Indirizzo email o password errati',
'forgotten_password_link' => 'Hai dimenticato la tua password?', 'forgotten_password_link' => 'Hai dimenticato la tua password?',
'reset_emailed' => 'Ti abbiamo inviato un link per ripristinare la tua password via email.', 'reset_emailed' => 'Ti abbiamo inviato un link via email per ripristinare la tua password.',
'reset_header' => '<strong>Non preoccuparti!</strong><br>E\' sufficiente inserire il tuo indirizzo email di seguito e ti invieremo una email con il link per il ripristino della tua password.', 'reset_header' => '<strong>Non preoccuparti!</strong><br>E\' sufficiente inserire il tuo indirizzo email di seguito e ti invieremo una email con il link per il ripristino della tua password.',
'reset_email_address' => 'Inserisci il tuo indirizzo email:', 'reset_email_address' => 'Inserisci il tuo indirizzo email:',
'reset_send_email' => 'Invia il link di reset della password', 'reset_send_email' => 'Invia il link di reset della password',
'reset_enter_password' => 'Per favore inserisci la nuova password', 'reset_enter_password' => 'Per favore inserisci la nuova password',
'reset_new_password' => 'Nuova password:', 'reset_new_password' => 'Nuova password:',
'reset_change_password' => 'Cambia password', 'reset_change_password' => 'Cambia password',
'reset_no_user_exists' => 'No user exists with that email address, please try again.',
'reset_no_user_exists' => 'Non esiste nessun utente con questo indirizzo email, per favore prova ancora.', 'reset_no_user_exists' => 'Non esiste nessun utente con questo indirizzo email, per favore prova ancora.',
'reset_email_body' => 'Ciao %s, 'reset_email_body' => 'Ciao %s,
hai ricevuto questa email perché tu, o qualcun\'altro, ha richiesto un reset della password per PHPCI. hai ricevuto questa email perché tu, o qualcun\'altro, ha richiesto un reset della password per PHPCI.
Se questa mail è tua, per favore apri il seguente link per resettare la tua password: %ssession/reset-password/%d/%s Se questa mail è tua, per favore apri il seguente link per ripristinare la tua password: %ssession/reset-password/%d/%s
altrimenti, per favore, ignora questa email e nessuna azione verrà intrapresa. altrimenti, per favore, ignora questa email e nessuna azione verrà intrapresa.
@ -40,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Ripristino della password di PHPCI per %s', 'reset_email_title' => 'Ripristino della password di PHPCI per %s',
'reset_invalid' => 'Richeista di ripristino password non valida.', 'reset_invalid' => 'Richeista di ripristino password non valida.',
'email_address' => 'Indirizzo Email', 'email_address' => 'Indirizzo Email',
'login' => 'Login / Email Address',
'password' => 'Password', 'password' => 'Password',
'log_in' => 'Accedi', 'log_in' => 'Accedi',
@ -113,7 +113,9 @@ PHPCI',
(se non puoi aggiungere il file phpci.yml nel repository di questo progetto)', (se non puoi aggiungere il file phpci.yml nel repository di questo progetto)',
'default_branch' => 'Nome del branch di default', 'default_branch' => 'Nome del branch di default',
'allow_public_status' => 'Vuoi rendere pubblica la pagina dello stato e l\'immagine per questo progetto?', 'allow_public_status' => 'Vuoi rendere pubblica la pagina dello stato e l\'immagine per questo progetto?',
'save_project' => 'Salca il Progetto', 'archived' => 'Archived',
'archived_menu' => 'Archived',
'save_project' => 'Salva il Progetto',
'error_mercurial' => 'L\'URL del repository Mercurial URL deve iniziare con http:// o https://', 'error_mercurial' => 'L\'URL del repository Mercurial URL deve iniziare con http:// o https://',
'error_remote' => 'L\'URL del repository deve iniziare con git://, http:// o https://', 'error_remote' => 'L\'URL del repository deve iniziare con git://, http:// o https://',
@ -127,6 +129,7 @@ PHPCI',
'all_branches' => 'Tutti i Branche', 'all_branches' => 'Tutti i Branche',
'builds' => 'Builds', 'builds' => 'Builds',
'id' => 'ID', 'id' => 'ID',
'date' => 'Data',
'project' => 'Progetto', 'project' => 'Progetto',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Branch', 'branch' => 'Branch',
@ -137,16 +140,16 @@ PHPCI',
'delete_build' => 'Rimuovi build', 'delete_build' => 'Rimuovi build',
'webhooks' => 'Webhooks', 'webhooks' => 'Webhooks',
'webhooks_help_github' => 'Per efettuare la build automatica di questo progetto quando vengono pushati nuovi commit, 'webhooks_help_github' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi commit,
aggiungi l\'URL seguente come "Webhook" nella sezione aggiungi l\'URL seguente come "Webhook" nella sezione
<a href="https://github.com/%s/settings/hooks">Webhooks and Services</a> del tuo <a href="https://github.com/%s/settings/hooks">Webhooks and Services</a> del tuo
repository su GitHub.', repository su GitHub.',
'webhooks_help_gitlab' => 'Per efettuare la build automatica di questo progetto quando vengono pushati nuovi commit, 'webhooks_help_gitlab' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi commit,
aggiungi l\'URL seguente come "Webhook URL" nella sezione "WebHook URL" del tuo aggiungi l\'URL seguente come "Webhook URL" nella sezione "WebHook URL" del tuo
repository GitLab.', repository GitLab.',
'webhooks_help_bitbucket' => 'Per efettuare la build automatica di questo progetto quando vengono pushati nuovi 'webhooks_help_bitbucket' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi
commit, aggiungi l\'URL seguente come serizio "POST" nella sezione commit, aggiungi l\'URL seguente come serizio "POST" nella sezione
<a href="https://bitbucket.org/%s/admin/services">Services</a> del tuo repository su <a href="https://bitbucket.org/%s/admin/services">Services</a> del tuo repository su
BITBUCKET.', BITBUCKET.',
@ -157,7 +160,7 @@ PHPCI',
'rebuild_now' => 'Esegui nuovamente la build ora', 'rebuild_now' => 'Esegui nuovamente la build ora',
'committed_by_x' => 'Committato da %s', 'committed_by_x' => 'Inviato da %s',
'commit_id_x' => 'Commit: %s', 'commit_id_x' => 'Commit: %s',
'chart_display' => 'Questo grafico verrà mostrato una volta terminata la build.', 'chart_display' => 'Questo grafico verrà mostrato una volta terminata la build.',
@ -169,15 +172,17 @@ PHPCI',
'logical_lines' => 'Linee di logica', 'logical_lines' => 'Linee di logica',
'lines_of_code' => 'Linee di codice', 'lines_of_code' => 'Linee di codice',
'build_log' => 'Log della build', 'build_log' => 'Log della build',
'quality_trend' => 'Trand della qualità', 'quality_trend' => 'Trend della qualità',
'phpmd_warnings' => 'Warning di PHPMD', 'codeception_errors' => 'Errori di Codeception',
'phpcs_warnings' => 'Warning di PHPCS', 'phpmd_warnings' => 'Avvisi di PHPMD',
'phpcs_warnings' => 'Avvisi di PHPCS',
'phpcs_errors' => 'Errori di PHPCS', 'phpcs_errors' => 'Errori di PHPCS',
'phplint_errors' => 'Errori di Lint', 'phplint_errors' => 'Errori di Lint',
'phpunit_errors' => 'Errori di PHPUnit', 'phpunit_errors' => 'Errori di PHPUnit',
'phpdoccheck_warnings' => 'Docblocks mancanti', 'phpdoccheck_warnings' => 'Docblocks mancanti',
'issues' => 'Segnalazioni', 'issues' => 'Segnalazioni',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Docblocks mancanti', 'phpdoccheck' => 'Docblocks mancanti',
@ -194,14 +199,20 @@ PHPCI',
'end' => 'Finisci', 'end' => 'Finisci',
'from' => 'Da', 'from' => 'Da',
'to' => 'A', 'to' => 'A',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Risultati', 'result' => 'Risultati',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Sono stati impiegati %d seconds', 'took_n_seconds' => 'Sono stati impiegati %d seconds',
'build_created' => 'Build Creata', 'build_created' => 'Build Creata',
'build_started' => 'Build Avviata', 'build_started' => 'Build Avviata',
'build_finished' => 'Build Terminata', 'build_finished' => 'Build Terminata',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Successful: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Nome', 'name' => 'Nome',
@ -228,16 +239,16 @@ PHPCI',
'settings_github_linked' => 'Il tuo account GitHub è stato collegato.', 'settings_github_linked' => 'Il tuo account GitHub è stato collegato.',
'settings_github_not_linked' => 'Il tuo account GitHub non può essere collegato.', 'settings_github_not_linked' => 'Il tuo account GitHub non può essere collegato.',
'build_settings' => 'Configurazioni della build', 'build_settings' => 'Configurazioni della build',
'github_application' => 'Applicatzione GitHub', 'github_application' => 'Applicazione GitHub',
'github_sign_in' => 'Prima di poter iniziare ad usare GitHub, è necessario <a href="%s">collegarsi</a> e garantire 'github_sign_in' => 'Prima di poter iniziare ad usare GitHub, è necessario <a href="%s">collegarsi</a> e garantire
a PHPCI l\'accesso al tuo account.', a PHPCI l\'accesso al tuo account.',
'github_phpci_linked' => 'PHPCI è stato collegato correttamente al tuo account GitHub.', 'github_phpci_linked' => 'PHPCI è stato collegato correttamente al tuo account GitHub.',
'github_where_to_find' => 'Dove trovare queste...', 'github_where_to_find' => 'Dove trovare queste...',
'github_where_help' => 'Se sei il proprietario dell\'applicazione, puoi trovare queste informazioni nell\'are delle 'github_where_help' => 'Se sei il proprietario dell\'applicazione, puoi trovare queste informazioni nell\'area delle
configurazioni dell\'<a href="https://github.com/settings/applications">applicazione</a>.', configurazioni dell\'<a href="https://github.com/settings/applications">applicazione</a>.',
'email_settings' => 'Impostazioni Email', 'email_settings' => 'Impostazioni Email',
'email_settings_help' => 'Prima che possa inviare le email con lo status PHPCI, devi configurare l\'SMTP qio sotto.', 'email_settings_help' => 'Prima che possa inviare le email con lo status PHPCI, devi configurare l\'SMTP qui sotto.',
'application_id' => 'ID dell\'Applicazione', 'application_id' => 'ID dell\'Applicazione',
'application_secret' => 'Secret dell\'Applicazione', 'application_secret' => 'Secret dell\'Applicazione',
@ -263,7 +274,7 @@ PHPCI',
// Plugins // Plugins
'cannot_update_composer' => 'PHPCI non può aggiornare composer.json per te non essendo scrivibile.', 'cannot_update_composer' => 'PHPCI non può aggiornare composer.json per te non essendo scrivibile.',
'x_has_been_removed' => '%s è stato rimosso.', 'x_has_been_removed' => '%s è stato rimosso.',
'x_has_been_added' => '%s è stato aggiunto al composer.json per te verrà installato la prossima volta che eseguirai 'x_has_been_added' => '%s è stato aggiunto al file composer.json per te, verrà installato la prossima volta che eseguirai
composer update.', composer update.',
'enabled_plugins' => 'Plugins attivati', 'enabled_plugins' => 'Plugins attivati',
'provided_by_package' => 'Fornito dal pacchetto', 'provided_by_package' => 'Fornito dal pacchetto',
@ -298,9 +309,9 @@ PHPCI',
Per favore controlla gli errori riportati prima di proseguire.', Per favore controlla gli errori riportati prima di proseguire.',
'must_be_valid_email' => 'Deve essere un indirizzo email valido.', 'must_be_valid_email' => 'Deve essere un indirizzo email valido.',
'must_be_valid_url' => 'Deve essere un URL valido.', 'must_be_valid_url' => 'Deve essere un URL valido.',
'enter_name' => 'Nome dell\'amministratore:', 'enter_name' => 'Nome dell\'amministratore: ',
'enter_email' => 'Email dell\'amministratore:', 'enter_email' => 'Email dell\'amministratore: ',
'enter_password' => 'Password dell\'amministratore:', 'enter_password' => 'Password dell\'amministratore: ',
'enter_phpci_url' => 'L\'URL di PHPCI ("http://phpci.locale" ad esempio): ', 'enter_phpci_url' => 'L\'URL di PHPCI ("http://phpci.locale" ad esempio): ',
'enter_db_host' => 'Per favore inserisci l\'host MySQL [localhost]: ', 'enter_db_host' => 'Per favore inserisci l\'host MySQL [localhost]: ',
@ -333,12 +344,18 @@ PHPCI',
'create_admin_user' => 'Crea un nuovo utente amministrarore', 'create_admin_user' => 'Crea un nuovo utente amministrarore',
'incorrect_format' => 'Formato errato', 'incorrect_format' => 'Formato errato',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command // Run Command
'run_all_pending' => 'Esegui tutte le build in attesa su PHPCI.', 'run_all_pending' => 'Esegui tutte le build in attesa su PHPCI.',
'finding_builds' => 'Ricerca delel build da processare', 'finding_builds' => 'Ricerca delel build da processare',
'found_n_builds' => 'Trovate %d build', 'found_n_builds' => 'Trovate %d build',
'skipping_build' => 'Saltata la build %d - La build del progetto è già in corso.', 'skipping_build' => 'Saltata la build %d - La build del progetto è già in corso.',
'marked_as_failed' => 'Build %d è satata contrassegnata come come fallita per un timeout.', 'marked_as_failed' => 'Build %d è stata contrassegnata come fallita per un timeout.',
// Builder // Builder
'missing_phpci_yml' => 'Questo progetto non contiene il file phpci.yml, o il file è vuoto.', 'missing_phpci_yml' => 'Questo progetto non contiene il file phpci.yml, o il file è vuoto.',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI wachtwoord reset voor %s', 'reset_email_title' => 'PHPCI wachtwoord reset voor %s',
'reset_invalid' => 'Ongeldig wachtwoord reset verzoek', 'reset_invalid' => 'Ongeldig wachtwoord reset verzoek',
'email_address' => 'E-mailadres', 'email_address' => 'E-mailadres',
'login' => 'Login / Email Address',
'password' => 'Wachtwoord', 'password' => 'Wachtwoord',
'log_in' => 'Log in', 'log_in' => 'Log in',
@ -113,6 +114,8 @@ van je gekozen source code hosting platform',
(indien je geen phpci.yml bestand aan de project repository kan toevoegen)', (indien je geen phpci.yml bestand aan de project repository kan toevoegen)',
'default_branch' => 'Standaard branch naam', 'default_branch' => 'Standaard branch naam',
'allow_public_status' => 'Publieke statuspagina en afbeelding beschikbaar maken voor dit project?', 'allow_public_status' => 'Publieke statuspagina en afbeelding beschikbaar maken voor dit project?',
'archived' => 'Archived',
'archived_menu' => 'Archived',
'save_project' => 'Project opslaan', 'save_project' => 'Project opslaan',
'error_mercurial' => 'Mercurial repository URL dient te starten met http:// of https://', 'error_mercurial' => 'Mercurial repository URL dient te starten met http:// of https://',
@ -126,6 +129,7 @@ van je gekozen source code hosting platform',
'all_branches' => 'Alle brances', 'all_branches' => 'Alle brances',
'builds' => 'Builds', 'builds' => 'Builds',
'id' => 'ID', 'id' => 'ID',
'date' => 'Datum',
'project' => 'Project', 'project' => 'Project',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Branch', 'branch' => 'Branch',
@ -167,6 +171,7 @@ Services</a> sectie van je Bitbucket repository toegevoegd worden.',
'lines_of_code' => 'Lijnen code', 'lines_of_code' => 'Lijnen code',
'build_log' => 'Build Log', 'build_log' => 'Build Log',
'quality_trend' => 'Kwaliteitstrend', 'quality_trend' => 'Kwaliteitstrend',
'codeception_errors' => 'Codeception Fouten',
'phpmd_warnings' => 'PHPMD Waarschuwingen', 'phpmd_warnings' => 'PHPMD Waarschuwingen',
'phpcs_warnings' => 'PHPCS Waarschuwingen', 'phpcs_warnings' => 'PHPCS Waarschuwingen',
'phpcs_errors' => 'PHPCS Fouten', 'phpcs_errors' => 'PHPCS Fouten',
@ -175,6 +180,7 @@ Services</a> sectie van je Bitbucket repository toegevoegd worden.',
'phpdoccheck_warnings' => 'Ontbrekende Docblocks', 'phpdoccheck_warnings' => 'Ontbrekende Docblocks',
'issues' => 'Problemen', 'issues' => 'Problemen',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Ontbrekende Docblocks', 'phpdoccheck' => 'Ontbrekende Docblocks',
@ -191,14 +197,20 @@ Services</a> sectie van je Bitbucket repository toegevoegd worden.',
'end' => 'Einde', 'end' => 'Einde',
'from' => 'Van', 'from' => 'Van',
'to' => 'Tot', 'to' => 'Tot',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultaat', 'result' => 'Resultaat',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Duurde %d seconden', 'took_n_seconds' => 'Duurde %d seconden',
'build_created' => 'Build aangemaakt', 'build_created' => 'Build aangemaakt',
'build_started' => 'Build gestart', 'build_started' => 'Build gestart',
'build_finished' => 'Build beëindigd', 'build_finished' => 'Build beëindigd',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Successful: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Naam', 'name' => 'Naam',
@ -295,15 +307,15 @@ keer je composer update uitvoert.',
Gelieve de fouten na te kijken vooraleer verder te gaan.', Gelieve de fouten na te kijken vooraleer verder te gaan.',
'must_be_valid_email' => 'Moet een geldig e-mailadres zijn.', 'must_be_valid_email' => 'Moet een geldig e-mailadres zijn.',
'must_be_valid_url' => 'Moet een geldige URL zijn.', 'must_be_valid_url' => 'Moet een geldige URL zijn.',
'enter_name' => 'Administrator naam:', 'enter_name' => 'Administrator naam: ',
'enter_email' => 'Administrator e-mailadres:', 'enter_email' => 'Administrator e-mailadres: ',
'enter_password' => 'Administrator wachtwoord:', 'enter_password' => 'Administrator wachtwoord: ',
'enter_phpci_url' => 'Je PHPCI URL (bijvoorbeeld "http://phpci.local")', 'enter_phpci_url' => 'Je PHPCI URL (bijvoorbeeld "http://phpci.local"): ',
'enter_db_host' => 'Vul je MySQL host in [localhost]:', 'enter_db_host' => 'Vul je MySQL host in [localhost]: ',
'enter_db_name' => 'Vul je MySQL databasenaam in [phpci]:', 'enter_db_name' => 'Vul je MySQL databasenaam in [phpci]: ',
'enter_db_user' => 'Vul je MySQL gebruikersnaam in [phpci]:', 'enter_db_user' => 'Vul je MySQL gebruikersnaam in [phpci]: ',
'enter_db_pass' => 'Vul je MySQL watchtwoord in:', 'enter_db_pass' => 'Vul je MySQL watchtwoord in: ',
'could_not_connect' => 'PHPCI kon met deze gegevens geen verbinding maken met MySQL. Gelieve opnieuw te proberen.', 'could_not_connect' => 'PHPCI kon met deze gegevens geen verbinding maken met MySQL. Gelieve opnieuw te proberen.',
'setting_up_db' => 'Database wordt aangemaakt...', 'setting_up_db' => 'Database wordt aangemaakt...',
'user_created' => 'Gebruikersprofiel aangemaakt!', 'user_created' => 'Gebruikersprofiel aangemaakt!',
@ -330,6 +342,12 @@ Gelieve de fouten na te kijken vooraleer verder te gaan.',
'create_admin_user' => 'Administrator-gebruiker aanmaken', 'create_admin_user' => 'Administrator-gebruiker aanmaken',
'incorrect_format' => 'Incorrect formaat', 'incorrect_format' => 'Incorrect formaat',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command // Run Command
'run_all_pending' => 'Voer alle wachtende PHPCI builds uit.', 'run_all_pending' => 'Voer alle wachtende PHPCI builds uit.',
'finding_builds' => 'Zoekt builds om te verwerken', 'finding_builds' => 'Zoekt builds om te verwerken',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Reset Hasła PHPCI dla %s', 'reset_email_title' => 'Reset Hasła PHPCI dla %s',
'reset_invalid' => 'Prośba o zmianę hasła jest nieważna.', 'reset_invalid' => 'Prośba o zmianę hasła jest nieważna.',
'email_address' => 'Adres email', 'email_address' => 'Adres email',
'login' => 'Login / Email Address',
'password' => 'Hasło', 'password' => 'Hasło',
'log_in' => 'Zaloguj się', 'log_in' => 'Zaloguj się',
@ -101,6 +102,7 @@ od wybranego kodu źródłowego platformy hostingowej.',
'remote' => 'Zdalny URL ', 'remote' => 'Zdalny URL ',
'local' => 'Lokalna Ścieżka ', 'local' => 'Lokalna Ścieżka ',
'hg' => 'Mercurial', 'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => 'Gdzie hostowany jest Twój projekt?', 'where_hosted' => 'Gdzie hostowany jest Twój projekt?',
'choose_github' => 'Wybierz repozytorium GitHub:', 'choose_github' => 'Wybierz repozytorium GitHub:',
@ -113,6 +115,8 @@ od wybranego kodu źródłowego platformy hostingowej.',
(jeśli nie możesz dodać pliku phpci.yml do repozytorium projektu)', (jeśli nie możesz dodać pliku phpci.yml do repozytorium projektu)',
'default_branch' => 'Domyślna nazwa gałęzi', 'default_branch' => 'Domyślna nazwa gałęzi',
'allow_public_status' => 'Włączyć status publiczny dla tego projektu?', 'allow_public_status' => 'Włączyć status publiczny dla tego projektu?',
'archived' => 'W archiwum',
'archived_menu' => 'W archiwum',
'save_project' => 'Zachowaj Projekt', 'save_project' => 'Zachowaj Projekt',
'error_mercurial' => 'URL repozytorium Mercurialnego powinno zaczynać się od http:// and https://', 'error_mercurial' => 'URL repozytorium Mercurialnego powinno zaczynać się od http:// and https://',
@ -126,6 +130,7 @@ od wybranego kodu źródłowego platformy hostingowej.',
'all_branches' => 'Wszystkie Gałęzie', 'all_branches' => 'Wszystkie Gałęzie',
'builds' => 'Budowania', 'builds' => 'Budowania',
'id' => 'ID', 'id' => 'ID',
'date' => 'Data',
'project' => 'Projekt', 'project' => 'Projekt',
'commit' => 'Commit', 'commit' => 'Commit',
'branch' => 'Gałąź', 'branch' => 'Gałąź',
@ -167,6 +172,7 @@ Services</a> repozytoria Bitbucket.',
'lines_of_code' => 'Linie Kodu', 'lines_of_code' => 'Linie Kodu',
'build_log' => 'Log Budowania', 'build_log' => 'Log Budowania',
'quality_trend' => 'Trend Jakości', 'quality_trend' => 'Trend Jakości',
'codeception_errors' => 'Błędy Codeception',
'phpmd_warnings' => 'Alerty PHPMD', 'phpmd_warnings' => 'Alerty PHPMD',
'phpcs_warnings' => 'Alerty PHPCS', 'phpcs_warnings' => 'Alerty PHPCS',
'phpcs_errors' => 'Błędy PHPCS', 'phpcs_errors' => 'Błędy PHPCS',
@ -175,12 +181,15 @@ Services</a> repozytoria Bitbucket.',
'phpdoccheck_warnings' => 'Brakuje sekcji DocBlock', 'phpdoccheck_warnings' => 'Brakuje sekcji DocBlock',
'issues' => 'Problemy', 'issues' => 'Problemy',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Brakuje sekcji DocBlock', 'phpdoccheck' => 'Brakuje sekcji DocBlock',
'phpmd' => 'PHP Mess Detector', 'phpmd' => 'PHP Mess Detector',
'phpspec' => 'Speck PHP', 'phpspec' => 'PHPSpec',
'phpunit' => 'Jednostka PHP', 'phpunit' => 'PHPUnit',
'technical_debt' => 'Dług technologiczny',
'behat' => 'Behat',
'file' => 'Plik', 'file' => 'Plik',
'line' => 'Linia', 'line' => 'Linia',
@ -191,14 +200,20 @@ Services</a> repozytoria Bitbucket.',
'end' => 'Koniec', 'end' => 'Koniec',
'from' => 'Od', 'from' => 'Od',
'to' => 'Do', 'to' => 'Do',
'suite' => 'Zestaw ',
'test' => 'Test',
'result' => 'Wynik', 'result' => 'Wynik',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Zajęło %d sekund', 'took_n_seconds' => 'Zajęło %d sekund',
'build_created' => 'Budowanie Stworzone', 'build_created' => 'Budowanie Stworzone',
'build_started' => 'Budowanie Rozpoczęte', 'build_started' => 'Budowanie Rozpoczęte',
'build_finished' => 'Budowanie Zakończone', 'build_finished' => 'Budowanie Zakończone',
'test_message' => 'Wiadomość',
'test_no_message' => 'Brak wiadomości',
'test_success' => 'Powodzenie: %d',
'test_fail' => 'Niepowodzenia: %d',
'test_skipped' => 'Pominęte: %d',
'test_error' => 'Błędy: %d',
'test_todo' => 'Do zrobienia: %d',
'test_total' => '%d test(ów)',
// Users // Users
'name' => 'Nazwa', 'name' => 'Nazwa',
@ -293,15 +308,15 @@ wywołaniu polecenia composer update.',
Przejrzyj powyższą listę błędów przed kontynuowaniem.', Przejrzyj powyższą listę błędów przed kontynuowaniem.',
'must_be_valid_email' => 'Poprawny adres email jest wymagany.', 'must_be_valid_email' => 'Poprawny adres email jest wymagany.',
'must_be_valid_url' => 'Poprawny URL jest wymagany.', 'must_be_valid_url' => 'Poprawny URL jest wymagany.',
'enter_name' => 'Imię Admina:', 'enter_name' => 'Imię Admina: ',
'enter_email' => 'Email Admina:', 'enter_email' => 'Email Admina: ',
'enter_password' => 'Hasło Admina:', 'enter_password' => 'Hasło Admina: ',
'enter_phpci_url' => 'URL PHPCI (na przykład "http://phpci.local"):', 'enter_phpci_url' => 'URL PHPCI (na przykład "http://phpci.local"): ',
'enter_db_host' => 'Wpisz hosta MySQL [host lokalny]:', 'enter_db_host' => 'Wpisz hosta MySQL [host lokalny]: ',
'enter_db_name' => 'Wpisz nazwę bazy danych MySQL [phpci]:', 'enter_db_name' => 'Wpisz nazwę bazy danych MySQL [phpci]: ',
'enter_db_user' => 'Wpisz nazwę użytkownika MySQL [phpci]:', 'enter_db_user' => 'Wpisz nazwę użytkownika MySQL [phpci]: ',
'enter_db_pass' => 'Wpisz hasło MySQL:', 'enter_db_pass' => 'Wpisz hasło MySQL: ',
'could_not_connect' => 'Z podanymi ustawieniami PHPCI nie udało się połączyć z MySQL. Spróbuj ponownie.', 'could_not_connect' => 'Z podanymi ustawieniami PHPCI nie udało się połączyć z MySQL. Spróbuj ponownie.',
'setting_up_db' => 'Ustawianie Twojej bazy danych...', 'setting_up_db' => 'Ustawianie Twojej bazy danych...',
'user_created' => 'Utworzono konto użytkownika!', 'user_created' => 'Utworzono konto użytkownika!',
@ -328,6 +343,12 @@ Przejrzyj powyższą listę błędów przed kontynuowaniem.',
'create_admin_user' => 'Utwórz admina', 'create_admin_user' => 'Utwórz admina',
'incorrect_format' => 'Niepoprawny format', 'incorrect_format' => 'Niepoprawny format',
// Create Build Command
'create_build_project' => 'Utwórz budowanie dla projektu',
'project_id_argument' => 'ID projektu',
'commit_id_option' => 'ID Commita do budowania',
'branch_name_option' => 'Gałąź do budowania',
// Run Command // Run Command
'run_all_pending' => 'Uruchom wszystkie oczekujące budowy w PHPCI', 'run_all_pending' => 'Uruchom wszystkie oczekujące budowy w PHPCI',
'finding_builds' => 'Szukam budów do przetwarzania.', 'finding_builds' => 'Szukam budów do przetwarzania.',

View file

@ -38,10 +38,10 @@ PHPCI',
'reset_email_title' => 'Сброс пароля PHPCI для %s', 'reset_email_title' => 'Сброс пароля PHPCI для %s',
'reset_invalid' => 'Некорректный запрос на сброс пароля.', 'reset_invalid' => 'Некорректный запрос на сброс пароля.',
'email_address' => 'Email', 'email_address' => 'Email',
'login' => 'Логин / Email',
'password' => 'Пароль', 'password' => 'Пароль',
'log_in' => 'Войти', 'log_in' => 'Войти',
// Top Nav // Top Nav
'toggle_navigation' => 'Скрыть/показать панель навигации', 'toggle_navigation' => 'Скрыть/показать панель навигации',
'n_builds_pending' => '%d сборок ожидает', 'n_builds_pending' => '%d сборок ожидает',
@ -84,7 +84,7 @@ PHPCI',
'success' => 'Успешно', 'success' => 'Успешно',
'successful' => 'Успешна', 'successful' => 'Успешна',
'failed' => 'Провалена', 'failed' => 'Провалена',
'manual_build' => 'Ручной сборки', 'manual_build' => 'Запущена вручную',
// Add/Edit Project: // Add/Edit Project:
'new_project' => 'Новый проект', 'new_project' => 'Новый проект',
@ -99,6 +99,7 @@ PHPCI',
'remote' => 'Внешний URL', 'remote' => 'Внешний URL',
'local' => 'Локальный путь', 'local' => 'Локальный путь',
'hg' => 'Mercurial', 'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => 'Расположение проекта', 'where_hosted' => 'Расположение проекта',
'choose_github' => 'Выберите GitHub репозиторий:', 'choose_github' => 'Выберите GitHub репозиторий:',
@ -106,11 +107,13 @@ PHPCI',
'repo_name' => 'Репозиторий / Внешний URL / Локальный путь', 'repo_name' => 'Репозиторий / Внешний URL / Локальный путь',
'project_title' => 'Название проекта', 'project_title' => 'Название проекта',
'project_private_key' => 'Приватный ключ для доступа к репозиторию 'project_private_key' => 'Приватный ключ для доступа к репозиторию
(оставьте поле пустым для локального использования и/или анонимного доступа)', (оставьте поле пустым для локального использования и/или анонимного доступа)',
'build_config' => 'Конфигурация сборки проекта для PHPCI 'build_config' => 'Конфигурация сборки проекта для PHPCI
(если вы не добавили файл phpci.yml в репозиторий вашего проекта)', (если вы не добавили файл phpci.yml в репозиторий вашего проекта)',
'default_branch' => 'Ветка по умолчанию', 'default_branch' => 'Ветка по умолчанию',
'allow_public_status' => 'Разрешить публичный статус и изображение (статуса) для проекта', 'allow_public_status' => 'Разрешить публичный статус и изображение (статуса) для проекта',
'archived' => 'Архивный',
'archived_menu' => 'Архив',
'save_project' => 'Сохранить проект', 'save_project' => 'Сохранить проект',
'error_mercurial' => 'URL репозитория Mercurial должен начинаться с http:// или https://', 'error_mercurial' => 'URL репозитория Mercurial должен начинаться с http:// или https://',
@ -124,6 +127,7 @@ PHPCI',
'all_branches' => 'Все ветки', 'all_branches' => 'Все ветки',
'builds' => 'Сборки', 'builds' => 'Сборки',
'id' => 'ID', 'id' => 'ID',
'date' => 'Дата',
'project' => 'Проект', 'project' => 'Проект',
'commit' => 'Коммит', 'commit' => 'Коммит',
'branch' => 'Ветка', 'branch' => 'Ветка',
@ -135,13 +139,13 @@ PHPCI',
'webhooks' => 'Webhooks', 'webhooks' => 'Webhooks',
'webhooks_help_github' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве нового хука в разделе настроек <a href="https://github.com/%s/settings/hooks">Webhooks 'webhooks_help_github' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве нового хука в разделе настроек <a href="https://github.com/%s/settings/hooks">Webhooks
and Services</a> вашего GitHub репозитория.', and Services</a> вашего GitHub репозитория.',
'webhooks_help_gitlab' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве "WebHook URL" 'webhooks_help_gitlab' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве "WebHook URL"
в разделе "Web Hooks" вашего GitLab репозитория.', в разделе "Web Hooks" вашего GitLab репозитория.',
'webhooks_help_bitbucket' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже как "POST" сервис в разделе <a href="https://bitbucket.org/%s/admin/services"> 'webhooks_help_bitbucket' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже как "POST" сервис в разделе <a href="https://bitbucket.org/%s/admin/services">
Services</a> вашего Bitbucket репозитория.', Services</a> вашего Bitbucket репозитория.',
// View Build // View Build
'build_x_not_found' => 'Сборки с ID %d не существует.', 'build_x_not_found' => 'Сборки с ID %d не существует.',
@ -162,6 +166,7 @@ PHPCI',
'lines_of_code' => 'Строк кода', 'lines_of_code' => 'Строк кода',
'build_log' => 'Лог сборки', 'build_log' => 'Лог сборки',
'quality_trend' => 'Тенденция качества', 'quality_trend' => 'Тенденция качества',
'codeception_errors' => 'Ошибки Codeception',
'phpmd_warnings' => 'Предупреждения PHPMD', 'phpmd_warnings' => 'Предупреждения PHPMD',
'phpcs_warnings' => 'Предупреждения PHPCS', 'phpcs_warnings' => 'Предупреждения PHPCS',
'phpcs_errors' => 'Ошибки PHPCS', 'phpcs_errors' => 'Ошибки PHPCS',
@ -170,30 +175,44 @@ PHPCI',
'phpdoccheck_warnings' => 'Пропущенные Docblocks', 'phpdoccheck_warnings' => 'Пропущенные Docblocks',
'issues' => 'Проблемы', 'issues' => 'Проблемы',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Missing Docblocks', 'phpdoccheck' => 'Missing Docblocks',
'phpmd' => 'PHP Mess Detector', 'phpmd' => 'PHP Mess Detector',
'phpspec' => 'PHP Spec', 'phpspec' => 'PHP Spec',
'phpunit' => 'PHP Unit', 'phpunit' => 'PHP Unit',
'technical_debt' => 'Технические долги',
'behat' => 'Behat',
'codeception_feature' => 'Свойство',
'codeception_suite' => 'Набор',
'codeception_time' => 'Время',
'codeception_synopsis' => 'Тестов выполнено: <strong>%1$d</strong> (за <strong>%2$f</strong> сек.). Провалов: <strong>%3$d</strong>.',
'file' => 'Файл', 'file' => 'Файл',
'line' => 'Строка', 'line' => 'Строка',
'class' => 'Класс', 'class' => 'Класс',
'method' => 'Метод', 'method' => 'Метод',
'message' => 'Сообщение', 'message' => 'Сообщение',
'start' => 'Запуск', 'start' => 'Начало',
'end' => 'Конец', 'end' => 'Конец',
'from' => 'От', 'from' => 'Из',
'to' => 'До', 'to' => 'В',
'suite' => 'Комплект',
'test' => 'Тест',
'result' => 'Результат', 'result' => 'Результат',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Заняло секунд: %d', 'took_n_seconds' => 'Заняло секунд: %d',
'build_created' => 'Сборка создана', 'build_created' => 'Сборка создана',
'build_started' => 'Сборка запущена', 'build_started' => 'Сборка запущена',
'build_finished' => 'Сборка окончена', 'build_finished' => 'Сборка окончена',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Успешно: %d',
'test_fail' => 'Провалено: %d',
'test_skipped' => 'Пропущено: %d',
'test_error' => 'Ошибок: %d',
'test_todo' => 'Todo: %d',
'test_total' => 'Тестов: %d',
// Users // Users
'name' => 'Имя', 'name' => 'Имя',
@ -268,6 +287,19 @@ PHPCI',
'search_packagist_for_more' => 'Искать на Packagist', 'search_packagist_for_more' => 'Искать на Packagist',
'search' => 'Искать &raquo;', 'search' => 'Искать &raquo;',
// Summary plugin
'build-summary' => 'Сводка',
'stage' => 'Этап',
'duration' => 'Продолжительность',
'plugin' => 'Плагин',
'stage_setup' => 'Установка',
'stage_test' => 'тестирование',
'stage_complete' => 'Завершение',
'stage_success' => 'Успешное завершение',
'stage_failure' => 'Провал',
'stage_broken' => 'Поломка',
'stage_fixed' => 'Исправление',
// Installer // Installer
'installation_url' => 'URL-адрес PHPCI для установки', 'installation_url' => 'URL-адрес PHPCI для установки',
'db_host' => 'Хост базы данных', 'db_host' => 'Хост базы данных',
@ -288,9 +320,9 @@ PHPCI',
Пожалуйста, просмотрите возникшие ошибки перед тем, как продолжить.', Пожалуйста, просмотрите возникшие ошибки перед тем, как продолжить.',
'must_be_valid_email' => 'Должен быть корректным email-адресом.', 'must_be_valid_email' => 'Должен быть корректным email-адресом.',
'must_be_valid_url' => 'Должен быть корректным URL-адресом.', 'must_be_valid_url' => 'Должен быть корректным URL-адресом.',
'enter_name' => 'Имя администратора:', 'enter_name' => 'Имя администратора: ',
'enter_email' => 'Email администратора:', 'enter_email' => 'Email администратора: ',
'enter_password' => 'Пароль администратора:', 'enter_password' => 'Пароль администратора: ',
'enter_phpci_url' => 'URL-адрес вашего PHPCI (например: "http://phpci.local"): ', 'enter_phpci_url' => 'URL-адрес вашего PHPCI (например: "http://phpci.local"): ',
'enter_db_host' => 'Пожалуйста, введите хост MySQL [localhost]: ', 'enter_db_host' => 'Пожалуйста, введите хост MySQL [localhost]: ',
@ -323,6 +355,12 @@ PHPCI',
'create_admin_user' => 'Добавить аккаунт администратора', 'create_admin_user' => 'Добавить аккаунт администратора',
'incorrect_format' => 'Неверный формат', 'incorrect_format' => 'Неверный формат',
// Create Build Command
'create_build_project' => 'Создать сборку проекта',
'project_id_argument' => 'ID проекта',
'commit_id_option' => 'ID коммита для сборки',
'branch_name_option' => 'Ветка для сборки',
// Run Command // Run Command
'run_all_pending' => 'Запустить все ожидающие PHPCI сборки.', 'run_all_pending' => 'Запустить все ожидающие PHPCI сборки.',
'finding_builds' => 'Поиск сборок для запуска', 'finding_builds' => 'Поиск сборок для запуска',

View file

@ -8,7 +8,7 @@
*/ */
$strings = array( $strings = array(
'language_name' => 'Український', 'language_name' => 'Українська',
'language' => 'Мова', 'language' => 'Мова',
// Log in: // Log in:
@ -20,10 +20,10 @@ $strings = array(
і вам буде надіслано листа із посиланням на скидання паролю.', і вам буде надіслано листа із посиланням на скидання паролю.',
'reset_email_address' => 'Введіть свою email адресу:', 'reset_email_address' => 'Введіть свою email адресу:',
'reset_send_email' => 'Скидання пароля', 'reset_send_email' => 'Скидання пароля',
'reset_enter_password' => 'Введіть будь-ласка новий пароль', 'reset_enter_password' => 'Введіть, будь ласка, новий пароль',
'reset_new_password' => 'Новий пароль:', 'reset_new_password' => 'Новий пароль:',
'reset_change_password' => 'Змінити пароль', 'reset_change_password' => 'Змінити пароль',
'reset_no_user_exists' => 'Не існує користувача з такою email адресою, будь-ласка повторіть знову.', 'reset_no_user_exists' => 'Не існує користувача з такою email адресою, будь ласка, повторіть знову.',
'reset_email_body' => 'Привіт, %s, 'reset_email_body' => 'Привіт, %s,
Ви отримали цей лист, тому що ви або хтось інший запросили скидання пароля в PHPCI. Ви отримали цей лист, тому що ви або хтось інший запросили скидання пароля в PHPCI.
@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Скидання пароль PHPCI для %s', 'reset_email_title' => 'Скидання пароль PHPCI для %s',
'reset_invalid' => 'Невірний запит скидання паролю.', 'reset_invalid' => 'Невірний запит скидання паролю.',
'email_address' => 'Email адреса', 'email_address' => 'Email адреса',
'login' => 'Логин / Email адреса',
'password' => 'Пароль', 'password' => 'Пароль',
'log_in' => 'Увійти', 'log_in' => 'Увійти',
@ -62,7 +63,7 @@ PHPCI',
'manage_users' => 'Управління користувачами', 'manage_users' => 'Управління користувачами',
'plugins' => 'Плагіни', 'plugins' => 'Плагіни',
'view' => 'Переглянути', 'view' => 'Переглянути',
'build_now' => 'Збірати', 'build_now' => 'Зібрати',
'edit_project' => 'Редагувати проект', 'edit_project' => 'Редагувати проект',
'delete_project' => 'Видалити проект', 'delete_project' => 'Видалити проект',
@ -112,6 +113,8 @@ PHPCI',
(якщо ви не додали файл phpci.yml до репозиторію вашого проекту)', (якщо ви не додали файл phpci.yml до репозиторію вашого проекту)',
'default_branch' => 'Назва гілки за замовчуванням', 'default_branch' => 'Назва гілки за замовчуванням',
'allow_public_status' => 'Увімкнути публічну сторінку статусу та зображення для цього проекта?', 'allow_public_status' => 'Увімкнути публічну сторінку статусу та зображення для цього проекта?',
'archived' => 'Архівний',
'archived_menu' => 'Архів',
'save_project' => 'Зберегти проект', 'save_project' => 'Зберегти проект',
'error_mercurial' => 'URL репозиторію Mercurial повинен починатись із http:// або https://', 'error_mercurial' => 'URL репозиторію Mercurial повинен починатись із http:// або https://',
@ -125,6 +128,7 @@ PHPCI',
'all_branches' => 'Усі гілки', 'all_branches' => 'Усі гілки',
'builds' => 'Збірки', 'builds' => 'Збірки',
'id' => 'ID', 'id' => 'ID',
'date' => 'Дата',
'project' => 'Проект', 'project' => 'Проект',
'commit' => 'Комміт', 'commit' => 'Комміт',
'branch' => 'Гілка', 'branch' => 'Гілка',
@ -167,14 +171,16 @@ PHPCI',
'lines_of_code' => 'Рядки коду', 'lines_of_code' => 'Рядки коду',
'build_log' => 'Лог збірки', 'build_log' => 'Лог збірки',
'quality_trend' => 'Тенденція якості', 'quality_trend' => 'Тенденція якості',
'codeception_errors' => 'Помилки Codeception',
'phpmd_warnings' => 'Попередження PHPMD', 'phpmd_warnings' => 'Попередження PHPMD',
'phpcs_warnings' => 'Попередження PHPCS', 'phpcs_warnings' => 'Попередження PHPCS',
'phpcs_errors' => 'Помилки PHPCS', 'phpcs_errors' => 'Помилки PHPCS',
'phplint_errors' => 'Помилки Lint', 'phplint_errors' => 'Помилки Lint',
'phpunit_errors' => 'Помилки PHPCS', 'phpunit_errors' => 'Помилки PHPUnit',
'phpdoccheck_warnings' => 'Відсутні Docblocks', 'phpdoccheck_warnings' => 'Відсутні Docblocks',
'issues' => 'Проблеми', 'issues' => 'Проблеми',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector', 'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer', 'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Відсутні Docblocks', 'phpdoccheck' => 'Відсутні Docblocks',
@ -183,7 +189,7 @@ PHPCI',
'phpunit' => 'PHP Unit', 'phpunit' => 'PHP Unit',
'file' => 'Файл', 'file' => 'Файл',
'line' => 'Строка', 'line' => 'Рядок',
'class' => 'Клас', 'class' => 'Клас',
'method' => 'Метод', 'method' => 'Метод',
'message' => 'Повідомлення', 'message' => 'Повідомлення',
@ -191,14 +197,20 @@ PHPCI',
'end' => 'Кінець', 'end' => 'Кінець',
'from' => 'Від', 'from' => 'Від',
'to' => 'До', 'to' => 'До',
'suite' => 'Комплект',
'test' => 'Тест',
'result' => 'Результат', 'result' => 'Результат',
'ok' => 'OK', 'ok' => 'OK',
'took_n_seconds' => 'Зайняло %d секунд', 'took_n_seconds' => 'Зайняло %d секунд',
'build_created' => 'Збірка створена', 'build_created' => 'Збірка створена',
'build_started' => 'Збірка розпочата', 'build_started' => 'Збірка розпочата',
'build_finished' => 'Збірка завершена', 'build_finished' => 'Збірка завершена',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Successful: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users // Users
'name' => 'Ім’я', 'name' => 'Ім’я',
@ -295,15 +307,15 @@ PHPCI',
Будь ласка, продивіться наявні помилки перед тим, як продовжити.', Будь ласка, продивіться наявні помилки перед тим, як продовжити.',
'must_be_valid_email' => 'Повинно бути коректною email адресою.', 'must_be_valid_email' => 'Повинно бути коректною email адресою.',
'must_be_valid_url' => 'Повинно бути коректним URL.', 'must_be_valid_url' => 'Повинно бути коректним URL.',
'enter_name' => 'Ім’я адміністратора:', 'enter_name' => 'Ім’я адміністратора: ',
'enter_email' => 'Email адміністратора:', 'enter_email' => 'Email адміністратора: ',
'enter_password' => 'Пароль адміністратора:', 'enter_password' => 'Пароль адміністратора: ',
'enter_phpci_url' => 'URL адреса вашого PHPCI (наприклад, "http://phpci.local"):', 'enter_phpci_url' => 'URL адреса вашого PHPCI (наприклад, "http://phpci.local"): ',
'enter_db_host' => 'Будь ласка, введіть хост MySQL [localhost]:', 'enter_db_host' => 'Будь ласка, введіть хост MySQL [localhost]: ',
'enter_db_name' => 'Будь ласка, введить ім’я бази даних MySQL [phpci]:', 'enter_db_name' => 'Будь ласка, введить ім’я бази даних MySQL [phpci]: ',
'enter_db_user' => 'Будь ласка, введить ім’я користувача MySQL [phpci]:', 'enter_db_user' => 'Будь ласка, введить ім’я користувача MySQL [phpci]: ',
'enter_db_pass' => 'Будь ласка, введить ваш пароль MySQL:', 'enter_db_pass' => 'Будь ласка, введить ваш пароль MySQL: ',
'could_not_connect' => 'PHPCI не може підключитися до MySQL із наданими параметрами. Будь ласка, спробуйте ще раз.', 'could_not_connect' => 'PHPCI не може підключитися до MySQL із наданими параметрами. Будь ласка, спробуйте ще раз.',
'setting_up_db' => 'Налаштування вашої бази даних...', 'setting_up_db' => 'Налаштування вашої бази даних...',
'user_created' => 'Аккаунт користувача створено!', 'user_created' => 'Аккаунт користувача створено!',
@ -330,6 +342,12 @@ PHPCI',
'create_admin_user' => 'Створити аккаунт адміністратора', 'create_admin_user' => 'Створити аккаунт адміністратора',
'incorrect_format' => 'Невірний формат', 'incorrect_format' => 'Невірний формат',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command // Run Command
'run_all_pending' => 'Запустити всі PHPCI збірки, які очікують.', 'run_all_pending' => 'Запустити всі PHPCI збірки, які очікують.',
'finding_builds' => 'Пошук збірок для обробки', 'finding_builds' => 'Пошук збірок для обробки',

View file

@ -12,6 +12,7 @@ namespace PHPCI\Logging;
use b8\Store\Factory; use b8\Store\Factory;
use Monolog\Handler\AbstractProcessingHandler; use Monolog\Handler\AbstractProcessingHandler;
use PHPCI\Model\Build; use PHPCI\Model\Build;
use Psr\Log\LogLevel;
/** /**
* Class BuildDBLogHandler writes the build log to the database. * Class BuildDBLogHandler writes the build log to the database.

View file

@ -67,7 +67,7 @@ class BuildLogger implements LoggerAwareInterface
} }
} }
/** /**
* Add a success-coloured message to the log. * Add a success-coloured message to the log.
* @param string * @param string
*/ */
@ -98,6 +98,17 @@ class BuildLogger implements LoggerAwareInterface
); );
} }
/**
* Add a debug message to the log.
* @param string
*/
public function logDebug($message)
{
if (defined('PHPCI_DEBUG_MODE') && PHPCI_DEBUG_MODE) {
$this->log("\033[0;33m" . $message . "\033[0m");
}
}
/** /**
* Sets a logger instance on the object * Sets a logger instance on the object
* *

View file

@ -70,7 +70,6 @@ class Handler
public function handleError($level, $message, $file, $line) public function handleError($level, $message, $file, $line)
{ {
if (error_reporting() & $level) { if (error_reporting() & $level) {
$exception_level = isset($this->levels[$level]) ? $this->levels[$level] : $level; $exception_level = isset($this->levels[$level]) ? $this->levels[$level] : $level;
throw new \ErrorException( throw new \ErrorException(
@ -140,7 +139,6 @@ class Handler
protected function log(\Exception $exception) protected function log(\Exception $exception)
{ {
if (null !== $this->logger) { if (null !== $this->logger) {
$message = sprintf( $message = sprintf(
'%s: %s (uncaught exception) at %s line %s', '%s: %s (uncaught exception) at %s line %s',
get_class($exception), get_class($exception),

View file

@ -9,6 +9,7 @@
namespace PHPCI\Logging; namespace PHPCI\Logging;
use Monolog\ErrorHandler;
use Monolog\Logger; use Monolog\Logger;
/** /**
@ -19,6 +20,7 @@ class LoggerConfig
{ {
const KEY_ALWAYS_LOADED = "_"; const KEY_ALWAYS_LOADED = "_";
private $config; private $config;
private $cache = array();
/** /**
* The filepath is expected to return an array which will be * The filepath is expected to return an array which will be
@ -56,9 +58,20 @@ class LoggerConfig
*/ */
public function getFor($name) public function getFor($name)
{ {
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
$handlers = $this->getHandlers(self::KEY_ALWAYS_LOADED); $handlers = $this->getHandlers(self::KEY_ALWAYS_LOADED);
$handlers = array_merge($handlers, $this->getHandlers($name)); if ($name !== self::KEY_ALWAYS_LOADED) {
return new Logger($name, $handlers); $handlers = array_merge($handlers, $this->getHandlers($name));
}
$logger = new Logger($name, $handlers);
ErrorHandler::register($logger);
$this->cache[$name] = $logger;
return $logger;
} }
/** /**

View file

@ -20,7 +20,7 @@ class FixDatabaseColumns extends AbstractMigration
$build->changeColumn('project_id', 'integer', array('null' => false)); $build->changeColumn('project_id', 'integer', array('null' => false));
$build->changeColumn('commit_id', 'string', array('limit' => 50, 'null' => false)); $build->changeColumn('commit_id', 'string', array('limit' => 50, 'null' => false));
$build->changeColumn('status', 'integer', array('null' => false)); $build->changeColumn('status', 'integer', array('null' => false));
$build->changeColumn('log', 'text', array('null' => true, 'default' => '')); $build->changeColumn('log', 'text', array('null' => true));
$build->changeColumn('branch', 'string', array('limit' => 50, 'null' => false, 'default' => 'master')); $build->changeColumn('branch', 'string', array('limit' => 50, 'null' => false, 'default' => 'master'));
$build->changeColumn('created', 'datetime', array('null' => true)); $build->changeColumn('created', 'datetime', array('null' => true));
$build->changeColumn('started', 'datetime', array('null' => true)); $build->changeColumn('started', 'datetime', array('null' => true));

View file

@ -0,0 +1,26 @@
<?php
use Phinx\Migration\AbstractMigration;
class ArchiveProject extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$project = $this->table('project');
$project->addColumn('archived', 'boolean');
$project->save();
}
/**
* Migrate Down.
*/
public function down()
{
$project = $this->table('project');
$project->removeColumn('archived');
$project->save();
}
}

View file

@ -0,0 +1,27 @@
<?php
use Phinx\Migration\AbstractMigration;
use Phinx\Db\Adapter\MysqlAdapter;
class FixColumnTypes extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// Update the build log column to MEDIUMTEXT:
$build = $this->table('build');
$build->changeColumn('log', 'text', array(
'null' => true,
'limit' => MysqlAdapter::TEXT_MEDIUM,
));
// Update the build meta value column to MEDIUMTEXT:
$buildMeta = $this->table('build_meta');
$buildMeta->changeColumn('meta_value', 'text', array(
'null' => false,
'limit' => MysqlAdapter::TEXT_MEDIUM,
));
}
}

View file

@ -0,0 +1,30 @@
<?php
use Phinx\Migration\AbstractMigration;
class UniqueEmailAndNameUserFields extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$user_table = $this->table('user');
$user_table
->addIndex('email', array('unique' => true))
->addIndex('name', array('unique' => true))
->save();
}
/**
* Migrate Down.
*/
public function down()
{
$user_table = $this->table('user');
$user_table
->removeIndex('email', array('unique' => true))
->removeIndex('name', array('unique' => true))
->save();
}
}

View file

@ -0,0 +1,29 @@
<?php
use Phinx\Migration\AbstractMigration;
class AddProjectGroups extends AbstractMigration
{
public function change()
{
$table = $this->table('project_group');
$table->addColumn('title', 'string', array('limit' => 100, 'null' => false));
$table->save();
$group = new \PHPCI\Model\ProjectGroup();
$group->setTitle('Projects');
/** @var \PHPCI\Model\ProjectGroup $group */
$group = \b8\Store\Factory::getStore('ProjectGroup')->save($group);
$table = $this->table('project');
$table->addColumn('group_id', 'integer', array(
'signed' => true,
'null' => false,
'default' => $group->getId(),
));
$table->addForeignKey('group_id', 'project_group', 'id', array('delete'=> 'RESTRICT', 'update' => 'CASCADE'));
$table->save();
}
}

View file

@ -0,0 +1,40 @@
<?php
use Phinx\Migration\AbstractMigration;
class RemoveUniqueNameIndex extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$user = $this->table('user');
if ($user->hasIndex('name', array('unique' => true))) {
$user->removeIndex('name', array('unique' => true));
$user->save();
}
$user->addIndex('name', array('unique' => false));
$user->save();
}
}

View file

@ -0,0 +1,24 @@
<?php
use Phinx\Migration\AbstractMigration;
use Phinx\Db\Adapter\MysqlAdapter;
class ErrorsTable extends AbstractMigration
{
public function change()
{
$table = $this->table('build_error');
$table->addColumn('build_id', 'integer', array('signed' => true));
$table->addColumn('plugin', 'string', array('limit' => 100));
$table->addColumn('file', 'string', array('limit' => 250, 'null' => true));
$table->addColumn('line_start', 'integer', array('signed' => false, 'null' => true));
$table->addColumn('line_end', 'integer', array('signed' => false, 'null' => true));
$table->addColumn('severity', 'integer', array('signed' => false, 'limit' => MysqlAdapter::INT_TINY));
$table->addColumn('message', 'string', array('limit' => 250));
$table->addColumn('created_date', 'datetime');
$table->addIndex(array('build_id', 'created_date'), array('unique' => false));
$table->addForeignKey('build_id', 'build', 'id', array('delete'=> 'CASCADE', 'update' => 'CASCADE'));
$table->save();
}
}

View file

@ -0,0 +1,183 @@
<?php
use Phinx\Migration\AbstractMigration;
use PHPCI\Model\BuildMeta;
use PHPCI\Model\BuildError;
class ConvertErrors extends AbstractMigration
{
/**
* @var \PHPCI\Store\BuildMetaStore
*/
protected $metaStore;
/**
* @var \PHPCI\Store\BuildErrorStore
*/
protected $errorStore;
public function change()
{
$count = 100;
$this->metaStore = \b8\Store\Factory::getStore('BuildMeta');
$this->errorStore = \b8\Store\Factory::getStore('BuildError');
while ($count == 100) {
$data = $this->metaStore->getErrorsForUpgrade(100);
$count = count($data);
/** @var \PHPCI\Model\BuildMeta $meta */
foreach ($data as $meta) {
try {
switch ($meta->getMetaKey()) {
case 'phpmd-data':
$this->processPhpMdMeta($meta);
break;
case 'phpcs-data':
$this->processPhpCsMeta($meta);
break;
case 'phpdoccheck-data':
$this->processPhpDocCheckMeta($meta);
break;
case 'phpcpd-data':
$this->processPhpCpdMeta($meta);
break;
case 'technicaldebt-data':
$this->processTechnicalDebtMeta($meta);
break;
}
} catch (\Exception $ex) {}
$this->metaStore->delete($meta);
}
}
}
protected function processPhpMdMeta(BuildMeta $meta)
{
$data = json_decode($meta->getMetaValue(), true);
if (is_array($data) && count($data)) {
foreach ($data as $error) {
$buildError = new BuildError();
$buildError->setBuildId($meta->getBuildId());
$buildError->setPlugin('php_mess_detector');
$buildError->setCreatedDate(new \DateTime());
$buildError->setFile($error['file']);
$buildError->setLineStart($error['line_start']);
$buildError->setLineEnd($error['line_end']);
$buildError->setSeverity(BuildError::SEVERITY_HIGH);
$buildError->setMessage($error['message']);
$this->errorStore->save($buildError);
}
}
}
protected function processPhpCsMeta(BuildMeta $meta)
{
$data = json_decode($meta->getMetaValue(), true);
if (is_array($data) && count($data)) {
foreach ($data as $error) {
$buildError = new BuildError();
$buildError->setBuildId($meta->getBuildId());
$buildError->setPlugin('php_code_sniffer');
$buildError->setCreatedDate(new \DateTime());
$buildError->setFile($error['file']);
$buildError->setLineStart($error['line']);
$buildError->setLineEnd($error['line']);
$buildError->setMessage($error['message']);
switch ($error['type']) {
case 'ERROR':
$buildError->setSeverity(BuildError::SEVERITY_HIGH);
break;
case 'WARNING':
$buildError->setSeverity(BuildError::SEVERITY_LOW);
break;
}
$this->errorStore->save($buildError);
}
}
}
protected function processPhpDocCheckMeta(BuildMeta $meta)
{
$data = json_decode($meta->getMetaValue(), true);
if (is_array($data) && count($data)) {
foreach ($data as $error) {
$buildError = new BuildError();
$buildError->setBuildId($meta->getBuildId());
$buildError->setPlugin('php_docblock_checker');
$buildError->setCreatedDate(new \DateTime());
$buildError->setFile($error['file']);
$buildError->setLineStart($error['line']);
$buildError->setLineEnd($error['line']);
switch ($error['type']) {
case 'method':
$buildError->setMessage($error['class'] . '::' . $error['method'] . ' is missing a docblock.');
$buildError->setSeverity(BuildError::SEVERITY_NORMAL);
break;
case 'class':
$buildError->setMessage('Class ' . $error['class'] . ' is missing a docblock.');
$buildError->setSeverity(BuildError::SEVERITY_LOW);
break;
}
$this->errorStore->save($buildError);
}
}
}
protected function processPhpCpdMeta(BuildMeta $meta)
{
$data = json_decode($meta->getMetaValue(), true);
if (is_array($data) && count($data)) {
foreach ($data as $error) {
$buildError = new BuildError();
$buildError->setBuildId($meta->getBuildId());
$buildError->setPlugin('php_cpd');
$buildError->setCreatedDate(new \DateTime());
$buildError->setFile($error['file']);
$buildError->setLineStart($error['line_start']);
$buildError->setLineEnd($error['line_end']);
$buildError->setSeverity(BuildError::SEVERITY_NORMAL);
$buildError->setMessage('Copy and paste detected.');
$this->errorStore->save($buildError);
}
}
}
protected function processTechnicalDebtMeta(BuildMeta $meta)
{
$data = json_decode($meta->getMetaValue(), true);
if (is_array($data) && count($data)) {
foreach ($data as $error) {
$buildError = new BuildError();
$buildError->setBuildId($meta->getBuildId());
$buildError->setPlugin('technical_debt');
$buildError->setCreatedDate(new \DateTime());
$buildError->setFile($error['file']);
$buildError->setLineStart($error['line']);
$buildError->setSeverity(BuildError::SEVERITY_NORMAL);
$buildError->setMessage($error['message']);
$this->errorStore->save($buildError);
}
}
}
}

View file

@ -0,0 +1,18 @@
<?php
use Phinx\Migration\AbstractMigration;
use Phinx\Db\Adapter\MysqlAdapter;
class ProjectTableDefaults extends AbstractMigration
{
public function change()
{
$this->table('project')
->changeColumn('build_config', MysqlAdapter::PHINX_TYPE_TEXT, array('null' => true))
->changeColumn('archived', MysqlAdapter::PHINX_TYPE_INTEGER, array(
'length' => MysqlAdapter::INT_TINY,
'default' => 0,
))
->save();
}
}

View file

@ -118,7 +118,7 @@ class BuildBase extends Model
'default' => null, 'default' => null,
), ),
'log' => array( 'log' => array(
'type' => 'text', 'type' => 'mediumtext',
'nullable' => true, 'nullable' => true,
'default' => null, 'default' => null,
), ),
@ -621,6 +621,18 @@ class BuildBase extends Model
return $this->setProjectId($value->getId()); return $this->setProjectId($value->getId());
} }
/**
* Get BuildError models by BuildId for this Build.
*
* @uses \PHPCI\Store\BuildErrorStore::getByBuildId()
* @uses \PHPCI\Model\BuildError
* @return \PHPCI\Model\BuildError[]
*/
public function getBuildBuildErrors()
{
return Factory::getStore('BuildError', 'PHPCI')->getByBuildId($this->getId());
}
/** /**
* Get BuildMeta models by BuildId for this Build. * Get BuildMeta models by BuildId for this Build.
* *

View file

@ -0,0 +1,503 @@
<?php
/**
* BuildError base model for table: build_error
*/
namespace PHPCI\Model\Base;
use PHPCI\Model;
use b8\Store\Factory;
/**
* BuildError Base Model
*/
class BuildErrorBase extends Model
{
/**
* @var array
*/
public static $sleepable = array();
/**
* @var string
*/
protected $tableName = 'build_error';
/**
* @var string
*/
protected $modelName = 'BuildError';
/**
* @var array
*/
protected $data = array(
'id' => null,
'build_id' => null,
'plugin' => null,
'file' => null,
'line_start' => null,
'line_end' => null,
'severity' => null,
'message' => null,
'created_date' => null,
);
/**
* @var array
*/
protected $getters = array(
// Direct property getters:
'id' => 'getId',
'build_id' => 'getBuildId',
'plugin' => 'getPlugin',
'file' => 'getFile',
'line_start' => 'getLineStart',
'line_end' => 'getLineEnd',
'severity' => 'getSeverity',
'message' => 'getMessage',
'created_date' => 'getCreatedDate',
// Foreign key getters:
'Build' => 'getBuild',
);
/**
* @var array
*/
protected $setters = array(
// Direct property setters:
'id' => 'setId',
'build_id' => 'setBuildId',
'plugin' => 'setPlugin',
'file' => 'setFile',
'line_start' => 'setLineStart',
'line_end' => 'setLineEnd',
'severity' => 'setSeverity',
'message' => 'setMessage',
'created_date' => 'setCreatedDate',
// Foreign key setters:
'Build' => 'setBuild',
);
/**
* @var array
*/
public $columns = array(
'id' => array(
'type' => 'int',
'length' => 11,
'primary_key' => true,
'auto_increment' => true,
'default' => null,
),
'build_id' => array(
'type' => 'int',
'length' => 11,
'default' => null,
),
'plugin' => array(
'type' => 'varchar',
'length' => 100,
'default' => null,
),
'file' => array(
'type' => 'varchar',
'length' => 250,
'nullable' => true,
'default' => null,
),
'line_start' => array(
'type' => 'int',
'length' => 11,
'nullable' => true,
'default' => null,
),
'line_end' => array(
'type' => 'int',
'length' => 11,
'nullable' => true,
'default' => null,
),
'severity' => array(
'type' => 'tinyint',
'length' => 3,
'default' => null,
),
'message' => array(
'type' => 'varchar',
'length' => 250,
'default' => null,
),
'created_date' => array(
'type' => 'datetime',
'default' => null,
),
);
/**
* @var array
*/
public $indexes = array(
'PRIMARY' => array('unique' => true, 'columns' => 'id'),
'build_id' => array('columns' => 'build_id, created_date'),
);
/**
* @var array
*/
public $foreignKeys = array(
'build_error_ibfk_1' => array(
'local_col' => 'build_id',
'update' => 'CASCADE',
'delete' => 'CASCADE',
'table' => 'build',
'col' => 'id'
),
);
/**
* Get the value of Id / id.
*
* @return int
*/
public function getId()
{
$rtn = $this->data['id'];
return $rtn;
}
/**
* Get the value of BuildId / build_id.
*
* @return int
*/
public function getBuildId()
{
$rtn = $this->data['build_id'];
return $rtn;
}
/**
* Get the value of Plugin / plugin.
*
* @return string
*/
public function getPlugin()
{
$rtn = $this->data['plugin'];
return $rtn;
}
/**
* Get the value of File / file.
*
* @return string
*/
public function getFile()
{
$rtn = $this->data['file'];
return $rtn;
}
/**
* Get the value of LineStart / line_start.
*
* @return int
*/
public function getLineStart()
{
$rtn = $this->data['line_start'];
return $rtn;
}
/**
* Get the value of LineEnd / line_end.
*
* @return int
*/
public function getLineEnd()
{
$rtn = $this->data['line_end'];
return $rtn;
}
/**
* Get the value of Severity / severity.
*
* @return int
*/
public function getSeverity()
{
$rtn = $this->data['severity'];
return $rtn;
}
/**
* Get the value of Message / message.
*
* @return string
*/
public function getMessage()
{
$rtn = $this->data['message'];
return $rtn;
}
/**
* Get the value of CreatedDate / created_date.
*
* @return \DateTime
*/
public function getCreatedDate()
{
$rtn = $this->data['created_date'];
if (!empty($rtn)) {
$rtn = new \DateTime($rtn);
}
return $rtn;
}
/**
* Set the value of Id / id.
*
* Must not be null.
* @param $value int
*/
public function setId($value)
{
$this->_validateNotNull('Id', $value);
$this->_validateInt('Id', $value);
if ($this->data['id'] === $value) {
return;
}
$this->data['id'] = $value;
$this->_setModified('id');
}
/**
* Set the value of BuildId / build_id.
*
* Must not be null.
* @param $value int
*/
public function setBuildId($value)
{
$this->_validateNotNull('BuildId', $value);
$this->_validateInt('BuildId', $value);
if ($this->data['build_id'] === $value) {
return;
}
$this->data['build_id'] = $value;
$this->_setModified('build_id');
}
/**
* Set the value of Plugin / plugin.
*
* Must not be null.
* @param $value string
*/
public function setPlugin($value)
{
$this->_validateNotNull('Plugin', $value);
$this->_validateString('Plugin', $value);
if ($this->data['plugin'] === $value) {
return;
}
$this->data['plugin'] = $value;
$this->_setModified('plugin');
}
/**
* Set the value of File / file.
*
* @param $value string
*/
public function setFile($value)
{
$this->_validateString('File', $value);
if ($this->data['file'] === $value) {
return;
}
$this->data['file'] = $value;
$this->_setModified('file');
}
/**
* Set the value of LineStart / line_start.
*
* @param $value int
*/
public function setLineStart($value)
{
$this->_validateInt('LineStart', $value);
if ($this->data['line_start'] === $value) {
return;
}
$this->data['line_start'] = $value;
$this->_setModified('line_start');
}
/**
* Set the value of LineEnd / line_end.
*
* @param $value int
*/
public function setLineEnd($value)
{
$this->_validateInt('LineEnd', $value);
if ($this->data['line_end'] === $value) {
return;
}
$this->data['line_end'] = $value;
$this->_setModified('line_end');
}
/**
* Set the value of Severity / severity.
*
* Must not be null.
* @param $value int
*/
public function setSeverity($value)
{
$this->_validateNotNull('Severity', $value);
$this->_validateInt('Severity', $value);
if ($this->data['severity'] === $value) {
return;
}
$this->data['severity'] = $value;
$this->_setModified('severity');
}
/**
* Set the value of Message / message.
*
* Must not be null.
* @param $value string
*/
public function setMessage($value)
{
$this->_validateNotNull('Message', $value);
$this->_validateString('Message', $value);
if ($this->data['message'] === $value) {
return;
}
$this->data['message'] = $value;
$this->_setModified('message');
}
/**
* Set the value of CreatedDate / created_date.
*
* Must not be null.
* @param $value \DateTime
*/
public function setCreatedDate($value)
{
$this->_validateNotNull('CreatedDate', $value);
$this->_validateDate('CreatedDate', $value);
if ($this->data['created_date'] === $value) {
return;
}
$this->data['created_date'] = $value;
$this->_setModified('created_date');
}
/**
* Get the Build model for this BuildError by Id.
*
* @uses \PHPCI\Store\BuildStore::getById()
* @uses \PHPCI\Model\Build
* @return \PHPCI\Model\Build
*/
public function getBuild()
{
$key = $this->getBuildId();
if (empty($key)) {
return null;
}
$cacheKey = 'Cache.Build.' . $key;
$rtn = $this->cache->get($cacheKey, null);
if (empty($rtn)) {
$rtn = Factory::getStore('Build', 'PHPCI')->getById($key);
$this->cache->set($cacheKey, $rtn);
}
return $rtn;
}
/**
* Set Build - Accepts an ID, an array representing a Build or a Build model.
*
* @param $value mixed
*/
public function setBuild($value)
{
// Is this an instance of Build?
if ($value instanceof \PHPCI\Model\Build) {
return $this->setBuildObject($value);
}
// Is this an array representing a Build item?
if (is_array($value) && !empty($value['id'])) {
return $this->setBuildId($value['id']);
}
// Is this a scalar value representing the ID of this foreign key?
return $this->setBuildId($value);
}
/**
* Set Build - Accepts a Build model.
*
* @param $value \PHPCI\Model\Build
*/
public function setBuildObject(\PHPCI\Model\Build $value)
{
return $this->setBuildId($value->getId());
}
}

View file

@ -99,7 +99,7 @@ class BuildMetaBase extends Model
'default' => null, 'default' => null,
), ),
'meta_value' => array( 'meta_value' => array(
'type' => 'text', 'type' => 'mediumtext',
'default' => null, 'default' => null,
), ),
); );

View file

@ -38,12 +38,14 @@ class ProjectBase extends Model
'reference' => null, 'reference' => null,
'branch' => null, 'branch' => null,
'ssh_private_key' => null, 'ssh_private_key' => null,
'ssh_public_key' => null,
'type' => null, 'type' => null,
'access_information' => null, 'access_information' => null,
'last_commit' => null, 'last_commit' => null,
'build_config' => null, 'build_config' => null,
'ssh_public_key' => null,
'allow_public_status' => null, 'allow_public_status' => null,
'archived' => null,
'group_id' => null,
); );
/** /**
@ -56,14 +58,17 @@ class ProjectBase extends Model
'reference' => 'getReference', 'reference' => 'getReference',
'branch' => 'getBranch', 'branch' => 'getBranch',
'ssh_private_key' => 'getSshPrivateKey', 'ssh_private_key' => 'getSshPrivateKey',
'ssh_public_key' => 'getSshPublicKey',
'type' => 'getType', 'type' => 'getType',
'access_information' => 'getAccessInformation', 'access_information' => 'getAccessInformation',
'last_commit' => 'getLastCommit', 'last_commit' => 'getLastCommit',
'build_config' => 'getBuildConfig', 'build_config' => 'getBuildConfig',
'ssh_public_key' => 'getSshPublicKey',
'allow_public_status' => 'getAllowPublicStatus', 'allow_public_status' => 'getAllowPublicStatus',
'archived' => 'getArchived',
'group_id' => 'getGroupId',
// Foreign key getters: // Foreign key getters:
'Group' => 'getGroup',
); );
/** /**
@ -76,14 +81,17 @@ class ProjectBase extends Model
'reference' => 'setReference', 'reference' => 'setReference',
'branch' => 'setBranch', 'branch' => 'setBranch',
'ssh_private_key' => 'setSshPrivateKey', 'ssh_private_key' => 'setSshPrivateKey',
'ssh_public_key' => 'setSshPublicKey',
'type' => 'setType', 'type' => 'setType',
'access_information' => 'setAccessInformation', 'access_information' => 'setAccessInformation',
'last_commit' => 'setLastCommit', 'last_commit' => 'setLastCommit',
'build_config' => 'setBuildConfig', 'build_config' => 'setBuildConfig',
'ssh_public_key' => 'setSshPublicKey',
'allow_public_status' => 'setAllowPublicStatus', 'allow_public_status' => 'setAllowPublicStatus',
'archived' => 'setArchived',
'group_id' => 'setGroupId',
// Foreign key setters: // Foreign key setters:
'Group' => 'setGroup',
); );
/** /**
@ -117,11 +125,6 @@ class ProjectBase extends Model
'nullable' => true, 'nullable' => true,
'default' => null, 'default' => null,
), ),
'ssh_public_key' => array(
'type' => 'text',
'nullable' => true,
'default' => null,
),
'type' => array( 'type' => array(
'type' => 'varchar', 'type' => 'varchar',
'length' => 50, 'length' => 50,
@ -144,10 +147,25 @@ class ProjectBase extends Model
'nullable' => true, 'nullable' => true,
'default' => null, 'default' => null,
), ),
'ssh_public_key' => array(
'type' => 'text',
'nullable' => true,
'default' => null,
),
'allow_public_status' => array( 'allow_public_status' => array(
'type' => 'int', 'type' => 'int',
'length' => 11, 'length' => 11,
), ),
'archived' => array(
'type' => 'tinyint',
'length' => 1,
'default' => null,
),
'group_id' => array(
'type' => 'int',
'length' => 11,
'default' => 1,
),
); );
/** /**
@ -156,12 +174,20 @@ class ProjectBase extends Model
public $indexes = array( public $indexes = array(
'PRIMARY' => array('unique' => true, 'columns' => 'id'), 'PRIMARY' => array('unique' => true, 'columns' => 'id'),
'idx_project_title' => array('columns' => 'title'), 'idx_project_title' => array('columns' => 'title'),
'group_id' => array('columns' => 'group_id'),
); );
/** /**
* @var array * @var array
*/ */
public $foreignKeys = array( public $foreignKeys = array(
'project_ibfk_1' => array(
'local_col' => 'group_id',
'update' => 'CASCADE',
'delete' => '',
'table' => 'project_group',
'col' => 'id'
),
); );
/** /**
@ -224,18 +250,6 @@ class ProjectBase extends Model
return $rtn; return $rtn;
} }
/**
* Get the value of SshPublicKey / ssh_public_key.
*
* @return string
*/
public function getSshPublicKey()
{
$rtn = $this->data['ssh_public_key'];
return $rtn;
}
/** /**
* Get the value of Type / type. * Get the value of Type / type.
* *
@ -284,6 +298,18 @@ class ProjectBase extends Model
return $rtn; return $rtn;
} }
/**
* Get the value of SshPublicKey / ssh_public_key.
*
* @return string
*/
public function getSshPublicKey()
{
$rtn = $this->data['ssh_public_key'];
return $rtn;
}
/** /**
* Get the value of AllowPublicStatus / allow_public_status. * Get the value of AllowPublicStatus / allow_public_status.
* *
@ -296,6 +322,30 @@ class ProjectBase extends Model
return $rtn; return $rtn;
} }
/**
* Get the value of Archived / archived.
*
* @return int
*/
public function getArchived()
{
$rtn = $this->data['archived'];
return $rtn;
}
/**
* Get the value of GroupId / group_id.
*
* @return int
*/
public function getGroupId()
{
$rtn = $this->data['group_id'];
return $rtn;
}
/** /**
* Set the value of Id / id. * Set the value of Id / id.
* *
@ -394,24 +444,6 @@ class ProjectBase extends Model
$this->_setModified('ssh_private_key'); $this->_setModified('ssh_private_key');
} }
/**
* Set the value of SshPublicKey / ssh_public_key.
*
* @param $value string
*/
public function setSshPublicKey($value)
{
$this->_validateString('SshPublicKey', $value);
if ($this->data['ssh_public_key'] === $value) {
return;
}
$this->data['ssh_public_key'] = $value;
$this->_setModified('ssh_public_key');
}
/** /**
* Set the value of Type / type. * Set the value of Type / type.
* *
@ -486,6 +518,24 @@ class ProjectBase extends Model
$this->_setModified('build_config'); $this->_setModified('build_config');
} }
/**
* Set the value of SshPublicKey / ssh_public_key.
*
* @param $value string
*/
public function setSshPublicKey($value)
{
$this->_validateString('SshPublicKey', $value);
if ($this->data['ssh_public_key'] === $value) {
return;
}
$this->data['ssh_public_key'] = $value;
$this->_setModified('ssh_public_key');
}
/** /**
* Set the value of AllowPublicStatus / allow_public_status. * Set the value of AllowPublicStatus / allow_public_status.
* *
@ -506,6 +556,103 @@ class ProjectBase extends Model
$this->_setModified('allow_public_status'); $this->_setModified('allow_public_status');
} }
/**
* Set the value of Archived / archived.
*
* Must not be null.
* @param $value int
*/
public function setArchived($value)
{
$this->_validateNotNull('Archived', $value);
$this->_validateInt('Archived', $value);
if ($this->data['archived'] === $value) {
return;
}
$this->data['archived'] = $value;
$this->_setModified('archived');
}
/**
* Set the value of GroupId / group_id.
*
* Must not be null.
* @param $value int
*/
public function setGroupId($value)
{
$this->_validateNotNull('GroupId', $value);
$this->_validateInt('GroupId', $value);
if ($this->data['group_id'] === $value) {
return;
}
$this->data['group_id'] = $value;
$this->_setModified('group_id');
}
/**
* Get the ProjectGroup model for this Project by Id.
*
* @uses \PHPCI\Store\ProjectGroupStore::getById()
* @uses \PHPCI\Model\ProjectGroup
* @return \PHPCI\Model\ProjectGroup
*/
public function getGroup()
{
$key = $this->getGroupId();
if (empty($key)) {
return null;
}
$cacheKey = 'Cache.ProjectGroup.' . $key;
$rtn = $this->cache->get($cacheKey, null);
if (empty($rtn)) {
$rtn = Factory::getStore('ProjectGroup', 'PHPCI')->getById($key);
$this->cache->set($cacheKey, $rtn);
}
return $rtn;
}
/**
* Set Group - Accepts an ID, an array representing a ProjectGroup or a ProjectGroup model.
*
* @param $value mixed
*/
public function setGroup($value)
{
// Is this an instance of ProjectGroup?
if ($value instanceof \PHPCI\Model\ProjectGroup) {
return $this->setGroupObject($value);
}
// Is this an array representing a ProjectGroup item?
if (is_array($value) && !empty($value['id'])) {
return $this->setGroupId($value['id']);
}
// Is this a scalar value representing the ID of this foreign key?
return $this->setGroupId($value);
}
/**
* Set Group - Accepts a ProjectGroup model.
*
* @param $value \PHPCI\Model\ProjectGroup
*/
public function setGroupObject(\PHPCI\Model\ProjectGroup $value)
{
return $this->setGroupId($value->getId());
}
/** /**
* Get Build models by ProjectId for this Project. * Get Build models by ProjectId for this Project.
* *

View file

@ -0,0 +1,168 @@
<?php
/**
* ProjectGroup base model for table: project_group
*/
namespace PHPCI\Model\Base;
use PHPCI\Model;
use b8\Store\Factory;
/**
* ProjectGroup Base Model
*/
class ProjectGroupBase extends Model
{
/**
* @var array
*/
public static $sleepable = array();
/**
* @var string
*/
protected $tableName = 'project_group';
/**
* @var string
*/
protected $modelName = 'ProjectGroup';
/**
* @var array
*/
protected $data = array(
'id' => null,
'title' => null,
);
/**
* @var array
*/
protected $getters = array(
// Direct property getters:
'id' => 'getId',
'title' => 'getTitle',
// Foreign key getters:
);
/**
* @var array
*/
protected $setters = array(
// Direct property setters:
'id' => 'setId',
'title' => 'setTitle',
// Foreign key setters:
);
/**
* @var array
*/
public $columns = array(
'id' => array(
'type' => 'int',
'length' => 11,
'primary_key' => true,
'auto_increment' => true,
'default' => null,
),
'title' => array(
'type' => 'varchar',
'length' => 100,
'default' => null,
),
);
/**
* @var array
*/
public $indexes = array(
'PRIMARY' => array('unique' => true, 'columns' => 'id'),
);
/**
* @var array
*/
public $foreignKeys = array(
);
/**
* Get the value of Id / id.
*
* @return int
*/
public function getId()
{
$rtn = $this->data['id'];
return $rtn;
}
/**
* Get the value of Title / title.
*
* @return string
*/
public function getTitle()
{
$rtn = $this->data['title'];
return $rtn;
}
/**
* Set the value of Id / id.
*
* Must not be null.
* @param $value int
*/
public function setId($value)
{
$this->_validateNotNull('Id', $value);
$this->_validateInt('Id', $value);
if ($this->data['id'] === $value) {
return;
}
$this->data['id'] = $value;
$this->_setModified('id');
}
/**
* Set the value of Title / title.
*
* Must not be null.
* @param $value string
*/
public function setTitle($value)
{
$this->_validateNotNull('Title', $value);
$this->_validateString('Title', $value);
if ($this->data['title'] === $value) {
return;
}
$this->data['title'] = $value;
$this->_setModified('title');
}
/**
* Get Project models by GroupId for this ProjectGroup.
*
* @uses \PHPCI\Store\ProjectStore::getByGroupId()
* @uses \PHPCI\Model\Project
* @return \PHPCI\Model\Project[]
*/
public function getGroupProjects()
{
return Factory::getStore('Project', 'PHPCI')->getByGroupId($this->getId());
}
}

View file

@ -106,6 +106,8 @@ class UserBase extends Model
public $indexes = array( public $indexes = array(
'PRIMARY' => array('unique' => true, 'columns' => 'id'), 'PRIMARY' => array('unique' => true, 'columns' => 'id'),
'idx_email' => array('unique' => true, 'columns' => 'email'), 'idx_email' => array('unique' => true, 'columns' => 'email'),
'email' => array('unique' => true, 'columns' => 'email'),
'name' => array('columns' => 'name'),
); );
/** /**

View file

@ -28,7 +28,7 @@ class Build extends BuildBase
const STATUS_SUCCESS = 2; const STATUS_SUCCESS = 2;
const STATUS_FAILED = 3; const STATUS_FAILED = 3;
public $currentBuildPath = null; public $currentBuildPath;
/** /**
* Get link to commit from another source (i.e. Github) * Get link to commit from another source (i.e. Github)
@ -99,16 +99,21 @@ class Build extends BuildBase
{ {
$build_config = null; $build_config = null;
// Try phpci.yml first:
if (is_file($buildPath . '/phpci.yml')) {
$build_config = file_get_contents($buildPath . '/phpci.yml');
}
// Try getting the project build config from the database: // Try getting the project build config from the database:
if (empty($build_config)) { if (empty($build_config)) {
$build_config = $this->getProject()->getBuildConfig(); $build_config = $this->getProject()->getBuildConfig();
} }
// Try .phpci.yml
if (is_file($buildPath . '/.phpci.yml')) {
$build_config = file_get_contents($buildPath . '/.phpci.yml');
}
// Try phpci.yml first:
if (empty($build_config) && is_file($buildPath . '/phpci.yml')) {
$build_config = file_get_contents($buildPath . '/phpci.yml');
}
// Fall back to zero config plugins: // Fall back to zero config plugins:
if (empty($build_config)) { if (empty($build_config)) {
$build_config = $this->getZeroConfigPlugins($builder); $build_config = $this->getZeroConfigPlugins($builder);
@ -193,4 +198,104 @@ class Build extends BuildBase
return $rtn; return $rtn;
} }
/**
* Returns the commit message for this build.
* @return string
*/
public function getCommitMessage()
{
$rtn = htmlspecialchars($this->data['commit_message']);
return $rtn;
}
/**
* Allows specific build types (e.g. Github) to report violations back to their respective services.
* @param Builder $builder
* @param $plugin
* @param $message
* @param int $severity
* @param null $file
* @param null $lineStart
* @param null $lineEnd
* @return BuildError
*/
public function reportError(
Builder $builder,
$plugin,
$message,
$severity = BuildError::SEVERITY_NORMAL,
$file = null,
$lineStart = null,
$lineEnd = null
) {
unset($builder);
$error = new BuildError();
$error->setBuild($this);
$error->setCreatedDate(new \DateTime());
$error->setPlugin($plugin);
$error->setMessage($message);
$error->setSeverity($severity);
$error->setFile($file);
$error->setLineStart($lineStart);
$error->setLineEnd($lineEnd);
return Factory::getStore('BuildError')->save($error);
}
/**
* Return the path to run this build into.
*
* @return string|null
*/
public function getBuildPath()
{
if (!$this->getId()) {
return null;
}
if (empty($this->currentBuildPath)) {
$buildDirectory = $this->getId() . '_' . substr(md5(microtime(true)), 0, 5);
$this->currentBuildPath = PHPCI_BUILD_ROOT_DIR . $buildDirectory . DIRECTORY_SEPARATOR;
}
return $this->currentBuildPath;
}
/**
* Removes the build directory.
*/
public function removeBuildDirectory()
{
$buildPath = $this->getBuildPath();
if (!$buildPath || !is_dir($buildPath)) {
return;
}
exec(sprintf(IS_WIN ? 'rmdir /S /Q "%s"' : 'rm -Rf "%s"', $buildPath));
}
/**
* Get the number of seconds a build has been running for.
* @return int
*/
public function getDuration()
{
$start = $this->getStarted();
if (empty($start)) {
return 0;
}
$end = $this->getFinished();
if (empty($end)) {
$end = new \DateTime();
}
return $end->getTimestamp() - $start->getTimestamp();
}
} }

View file

@ -10,6 +10,8 @@
namespace PHPCI\Model\Build; namespace PHPCI\Model\Build;
use PHPCI\Builder; use PHPCI\Builder;
use PHPCI\Helper\Diff;
use PHPCI\Helper\Github;
use PHPCI\Model\Build\RemoteGitBuild; use PHPCI\Model\Build\RemoteGitBuild;
/** /**
@ -43,39 +45,52 @@ class GithubBuild extends RemoteGitBuild
{ {
$token = \b8\Config::getInstance()->get('phpci.github.token'); $token = \b8\Config::getInstance()->get('phpci.github.token');
if (empty($token)) { if (empty($token) || empty($this->data['id'])) {
return; return;
} }
$project = $this->getProject(); $project = $this->getProject();
if (empty($project)) {
return;
}
$url = 'https://api.github.com/repos/'.$project->getReference().'/statuses/'.$this->getCommitId(); $url = 'https://api.github.com/repos/'.$project->getReference().'/statuses/'.$this->getCommitId();
$http = new \b8\HttpClient(); $http = new \b8\HttpClient();
switch($this->getStatus()) switch ($this->getStatus()) {
{
case 0: case 0:
case 1: case 1:
$status = 'pending'; $status = 'pending';
$description = 'PHPCI build running.';
break; break;
case 2: case 2:
$status = 'success'; $status = 'success';
$description = 'PHPCI build passed.';
break; break;
case 3: case 3:
$status = 'failure'; $status = 'failure';
$description = 'PHPCI build failed.';
break; break;
default: default:
$status = 'error'; $status = 'error';
$description = 'PHPCI build failed to complete.';
break; break;
} }
$phpciUrl = \b8\Config::getInstance()->get('phpci.url'); $phpciUrl = \b8\Config::getInstance()->get('phpci.url');
$params = array( 'state' => $status,
'target_url' => $phpciUrl . '/build/view/' . $this->getId()); $params = array(
'state' => $status,
'target_url' => $phpciUrl . '/build/view/' . $this->getId(),
'description' => $description,
'context' => 'PHPCI',
);
$headers = array( $headers = array(
'Authorization: token ' . $token, 'Authorization: token ' . $token,
'Content-Type: application/x-www-form-urlencoded' 'Content-Type: application/x-www-form-urlencoded'
); );
$http->setHeaders($headers); $http->setHeaders($headers);
$http->request('POST', $url, json_encode($params)); $http->request('POST', $url, json_encode($params));
@ -101,12 +116,16 @@ class GithubBuild extends RemoteGitBuild
*/ */
public function getCommitMessage() public function getCommitMessage()
{ {
$rtn = $this->data['commit_message']; $rtn = parent::getCommitMessage($this->data['commit_message']);
$reference = $this->getProject()->getReference(); $project = $this->getProject();
$commitLink = '<a target="_blank" href="https://github.com/' . $reference . '/issues/$1">#$1</a>';
$rtn = preg_replace('/\#([0-9]+)/', $commitLink, $rtn); if (!is_null($project)) {
$rtn = preg_replace('/\@([a-zA-Z0-9_]+)/', '<a target="_blank" href="https://github.com/$1">@$1</a>', $rtn); $reference = $project->getReference();
$commitLink = '<a target="_blank" href="https://github.com/' . $reference . '/issues/$1">#$1</a>';
$rtn = preg_replace('/\#([0-9]+)/', $commitLink, $rtn);
$rtn = preg_replace('/\@([a-zA-Z0-9_]+)/', '<a target="_blank" href="https://github.com/$1">@$1</a>', $rtn);
}
return $rtn; return $rtn;
} }
@ -123,7 +142,7 @@ class GithubBuild extends RemoteGitBuild
if ($this->getExtra('build_type') == 'pull_request') { if ($this->getExtra('build_type') == 'pull_request') {
$matches = array(); $matches = array();
preg_match('/\/([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches); preg_match('/[\/:]([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches);
$reference = $matches[1]; $reference = $matches[1];
$branch = $this->getExtra('remote_branch'); $branch = $this->getExtra('remote_branch');
@ -132,7 +151,7 @@ class GithubBuild extends RemoteGitBuild
$link = 'https://github.com/' . $reference . '/'; $link = 'https://github.com/' . $reference . '/';
$link .= 'blob/' . $branch . '/'; $link .= 'blob/' . $branch . '/';
$link .= '{FILE}'; $link .= '{FILE}';
$link .= '#L{LINE}'; $link .= '#L{LINE}-L{LINE_END}';
return $link; return $link;
} }
@ -167,4 +186,69 @@ class GithubBuild extends RemoteGitBuild
return $success; return $success;
} }
/**
* @inheritDoc
*/
public function reportError(
Builder $builder,
$plugin,
$message,
$severity = BuildError::SEVERITY_NORMAL,
$file = null,
$lineStart = null,
$lineEnd = null
) {
$diffLineNumber = $this->getDiffLineNumber($builder, $file, $lineStart);
if (!is_null($diffLineNumber)) {
$helper = new Github();
$repo = $this->getProject()->getReference();
$prNumber = $this->getExtra('pull_request_number');
$commit = $this->getCommitId();
if (!empty($prNumber)) {
$helper->createPullRequestComment($repo, $prNumber, $commit, $file, $diffLineNumber, $message);
} else {
$helper->createCommitComment($repo, $commit, $file, $diffLineNumber, $message);
}
}
return parent::reportError($builder, $plugin, $message, $severity, $file, $lineStart, $lineEnd);
}
/**
* Uses git diff to figure out what the diff line position is, based on the error line number.
* @param Builder $builder
* @param $file
* @param $line
* @return int|null
*/
protected function getDiffLineNumber(Builder $builder, $file, $line)
{
$line = (integer)$line;
$builder->logExecOutput(false);
$prNumber = $this->getExtra('pull_request_number');
$path = $builder->buildPath;
if (!empty($prNumber)) {
$builder->executeCommand('cd %s && git diff origin/%s "%s"', $path, $this->getBranch(), $file);
} else {
$commitId = $this->getCommitId();
$compare = $commitId == 'Manual' ? 'HEAD' : $commitId;
$builder->executeCommand('cd %s && git diff %s^^ "%s"', $path, $compare, $file);
}
$builder->logExecOutput(true);
$diff = $builder->getLastOutput();
$helper = new Diff();
$lines = $helper->getLinePositions($diff);
return isset($lines[$line]) ? $lines[$line] : null;
}
} }

View file

@ -47,7 +47,7 @@ class GitlabBuild extends RemoteGitBuild
'http://%s/%s/blob/%s/{FILE}#L{LINE}', 'http://%s/%s/blob/%s/{FILE}#L{LINE}',
$this->getProject()->getAccessInformation("domain"), $this->getProject()->getAccessInformation("domain"),
$this->getProject()->getReference(), $this->getProject()->getReference(),
$this->getBranch() $this->getCommitId()
); );
} }

View file

@ -35,12 +35,14 @@ class LocalBuild extends Build
return $this->handleConfig($builder, $buildPath) !== false; return $this->handleConfig($builder, $buildPath) !== false;
} }
$buildSettings = $this->handleConfig($builder, $reference); $configHandled = $this->handleConfig($builder, $reference);
if ($buildSettings === false) { if ($configHandled === false) {
return false; return false;
} }
$buildSettings = $builder->getConfig('build_settings');
if (isset($buildSettings['prefer_symlink']) && $buildSettings['prefer_symlink'] === true) { if (isset($buildSettings['prefer_symlink']) && $buildSettings['prefer_symlink'] === true) {
return $this->handleSymlink($builder, $reference, $buildPath); return $this->handleSymlink($builder, $reference, $buildPath);
} else { } else {

View file

@ -13,36 +13,87 @@ use PHPCI\Model\Build;
use PHPCI\Builder; use PHPCI\Builder;
/** /**
* Mercurial Build Model * Mercurial Build Model
* @author Pavel Gopanenko <pavelgopanenko@gmail.com> * @author Pavel Gopanenko <pavelgopanenko@gmail.com>
* @package PHPCI * @package PHPCI
* @subpackage Core * @subpackage Core
*/ */
class MercurialBuild extends Build class MercurialBuild extends Build
{ {
/** /**
* Get the URL to be used to clone this remote repository. * Get the URL to be used to clone this remote repository.
*/ */
protected function getCloneUrl() protected function getCloneUrl()
{ {
return $this->getProject()->getReference(); return $this->getProject()->getReference();
} }
/** /**
* Create a working copy by cloning, copying, or similar. * Create a working copy by cloning, copying, or similar.
*/ */
public function createWorkingCopy(Builder $builder, $buildPath) public function createWorkingCopy(Builder $builder, $buildPath)
{ {
$this->cloneByHttp($builder, $buildPath); $key = trim($this->getProject()->getSshPublicKey());
if (!empty($key) && strpos($this->getProject()->getReference(), 'ssh') > -1) {
$success = $this->cloneBySsh($builder, $buildPath);
} else {
$success = $this->cloneByHttp($builder, $buildPath);
}
if (!$success) {
$builder->logFailure('Failed to clone remote git repository.');
return false;
}
return $this->handleConfig($builder, $buildPath); return $this->handleConfig($builder, $buildPath);
} }
/** /**
* Use an mercurial clone. * Use a HTTP-based Mercurial clone.
*/ */
protected function cloneByHttp(Builder $builder, $cloneTo) protected function cloneByHttp(Builder $builder, $cloneTo)
{ {
return $builder->executeCommand('hg clone %s "%s" -r %s', $this->getCloneUrl(), $cloneTo, $this->getBranch()); return $builder->executeCommand('hg clone %s "%s" -r %s', $this->getCloneUrl(), $cloneTo, $this->getBranch());
} }
/**
* Use an SSH-based Mercurial clone.
*/
protected function cloneBySsh(Builder $builder, $cloneTo)
{
$keyFile = $this->writeSshKey();
// Do the git clone:
$cmd = 'hg clone --ssh "ssh -i '.$keyFile.'" %s "%s"';
$success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo);
if ($success) {
$success = $this->postCloneSetup($builder, $cloneTo);
}
// Remove the key file:
unlink($keyFile);
return $success;
}
/**
* Handle post-clone tasks (switching branch, etc.)
* @param Builder $builder
* @param $cloneTo
* @return bool
*/
protected function postCloneSetup(Builder $builder, $cloneTo)
{
$success = true;
$commit = $this->getCommitId();
// Allow switching to a specific branch:
if (!empty($commit) && $commit != 'Manual') {
$cmd = 'cd "%s" && hg checkout %s';
$success = $builder->executeCommand($cmd, $cloneTo, $this->getBranch());
}
return $success;
}
} }

View file

@ -54,7 +54,7 @@ class RemoteGitBuild extends Build
*/ */
protected function cloneByHttp(Builder $builder, $cloneTo) protected function cloneByHttp(Builder $builder, $cloneTo)
{ {
$cmd = 'git clone '; $cmd = 'git clone --recursive ';
$depth = $builder->getConfig('clone_depth'); $depth = $builder->getConfig('clone_depth');
@ -84,7 +84,7 @@ class RemoteGitBuild extends Build
} }
// Do the git clone: // Do the git clone:
$cmd = 'git clone '; $cmd = 'git clone --recursive ';
$depth = $builder->getConfig('clone_depth'); $depth = $builder->getConfig('clone_depth');
@ -124,16 +124,16 @@ class RemoteGitBuild extends Build
$success = true; $success = true;
$commit = $this->getCommitId(); $commit = $this->getCommitId();
$chdir = IS_WIN ? 'cd /d "%s"' : 'cd "%s"';
if (!empty($commit) && $commit != 'Manual') { if (!empty($commit) && $commit != 'Manual') {
$cmd = 'cd "%s"'; $cmd = $chdir . ' && git checkout %s --quiet';
$success = $builder->executeCommand($cmd, $cloneTo, $commit);
}
if (IS_WIN) { // Always update the commit hash with the actual HEAD hash
$cmd = 'cd /d "%s"'; if ($builder->executeCommand($chdir . ' && git rev-parse HEAD', $cloneTo)) {
} $this->setCommitId(trim($builder->getLastOutput()));
$cmd .= ' && git checkout %s --quiet';
$success = $builder->executeCommand($cmd, $cloneTo, $this->getCommitId());
} }
return $success; return $success;

View file

@ -0,0 +1,182 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Model\Build;
use PHPCI\Model\Build;
use PHPCI\Builder;
/**
* Remote Subversion Build Model
* @author Nadir Dzhilkibaev <imam.sharif@gmail.com>
* @package PHPCI
* @subpackage Core
*/
class SubversionBuild extends Build
{
protected $svnCommand = 'svn export -q --non-interactive ';
/**
* Get the URL to be used to clone this remote repository.
*/
protected function getCloneUrl()
{
$url = $this->getProject()->getReference();
if (substr($url, -1) != '/') {
$url .= '/';
}
$branch = $this->getBranch();
if (empty($branch) || $branch == 'trunk') {
$url .= 'trunk';
} else {
$url .= 'branches/' . $branch;
}
return $url;
}
/**
* @param Builder $builder
*
* @return void
*/
protected function extendSvnCommandFromConfig(Builder $builder)
{
$cmd = $this->svnCommand;
$svn = $builder->getConfig('svn');
if ($svn) {
foreach ($svn as $key => $value) {
$cmd .= " --$key $value ";
}
}
$depth = $builder->getConfig('clone_depth');
if (!is_null($depth)) {
$cmd .= ' --depth ' . intval($depth) . ' ';
}
$this->svnCommand = $cmd;
}
/**
* Create a working copy by cloning, copying, or similar.
*/
public function createWorkingCopy(Builder $builder, $buildPath)
{
$this->handleConfig($builder, $buildPath);
$this->extendSvnCommandFromConfig($builder);
$key = trim($this->getProject()->getSshPrivateKey());
if (!empty($key)) {
$success = $this->cloneBySsh($builder, $buildPath);
} else {
$success = $this->cloneByHttp($builder, $buildPath);
}
if (!$success) {
$builder->logFailure('Failed to export remote subversion repository.');
return false;
}
return $this->handleConfig($builder, $buildPath);
}
/**
* Use an HTTP-based svn export.
*/
protected function cloneByHttp(Builder $builder, $cloneTo)
{
$cmd = $this->svnCommand;
if ($this->getCommitId() != 'Manual') {
$cmd .= ' -r %s %s "%s"';
$success = $builder->executeCommand($cmd, $this->getCommitId(), $this->getCloneUrl(), $cloneTo);
} else {
$cmd .= ' %s "%s"';
$success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo);
}
return $success;
}
/**
* Use an SSH-based svn export.
*/
protected function cloneBySsh(Builder $builder, $cloneTo)
{
$cmd = $this->svnCommand . ' %s "%s"';
if (!IS_WIN) {
$keyFile = $this->writeSshKey($cloneTo);
$sshWrapper = $this->writeSshWrapper($cloneTo, $keyFile);
$cmd = 'export SVN_SSH="' . $sshWrapper . '" && ' . $cmd;
}
$success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo);
if (!IS_WIN) {
// Remove the key file and svn wrapper:
unlink($keyFile);
unlink($sshWrapper);
}
return $success;
}
/**
* Create an SSH key file on disk for this build.
* @param $cloneTo
* @return string
*/
protected function writeSshKey($cloneTo)
{
$keyPath = dirname($cloneTo . '/temp');
$keyFile = $keyPath . '.key';
// Write the contents of this project's svn key to the file:
file_put_contents($keyFile, $this->getProject()->getSshPrivateKey());
chmod($keyFile, 0600);
// Return the filename:
return $keyFile;
}
/**
* Create an SSH wrapper script for Svn to use, to disable host key checking, etc.
* @param $cloneTo
* @param $keyFile
* @return string
*/
protected function writeSshWrapper($cloneTo, $keyFile)
{
$path = dirname($cloneTo . '/temp');
$wrapperFile = $path . '.sh';
$sshFlags = '-o CheckHostIP=no -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o PasswordAuthentication=no';
// Write out the wrapper script for this build:
$script = <<<OUT
#!/bin/sh
ssh {$sshFlags} -o IdentityFile={$keyFile} $*
OUT;
file_put_contents($wrapperFile, $script);
shell_exec('chmod +x "' . $wrapperFile . '"');
return $wrapperFile;
}
}

View file

@ -0,0 +1,63 @@
<?php
/**
* BuildError model for table: build_error
*/
namespace PHPCI\Model;
use PHPCI\Model\Base\BuildErrorBase;
/**
* BuildError Model
* @uses PHPCI\Model\Base\BuildErrorBase
*/
class BuildError extends BuildErrorBase
{
const SEVERITY_CRITICAL = 0;
const SEVERITY_HIGH = 1;
const SEVERITY_NORMAL = 2;
const SEVERITY_LOW = 3;
/**
* Get the language string key for this error's severity level.
* @return string
*/
public function getSeverityString()
{
switch ($this->getSeverity()) {
case self::SEVERITY_CRITICAL:
return 'critical';
case self::SEVERITY_HIGH:
return 'high';
case self::SEVERITY_NORMAL:
return 'normal';
case self::SEVERITY_LOW:
return 'low';
}
}
/**
* Get the class to apply to HTML elements representing this error.
* @return string
*/
public function getSeverityClass()
{
switch ($this->getSeverity()) {
case self::SEVERITY_CRITICAL:
return 'danger';
case self::SEVERITY_HIGH:
return 'warning';
case self::SEVERITY_NORMAL:
return 'info';
case self::SEVERITY_LOW:
return 'default';
}
}
}

View file

@ -50,6 +50,29 @@ class Project extends ProjectBase
return null; return null;
} }
/**
* Return the previous build from a specific branch, for this project.
* @param string $branch
* @return mixed|null
*/
public function getPreviousBuild($branch = 'master')
{
$criteria = array('branch' => $branch, 'project_id' => $this->getId());
$order = array('id' => 'DESC');
$builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 1, array(), $order);
if (is_array($builds['items']) && count($builds['items'])) {
$previous = array_shift($builds['items']);
if (isset($previous) && $previous instanceof Build) {
return $previous;
}
}
return null;
}
/** /**
* Store this project's access_information data * Store this project's access_information data
* @param string|array $value * @param string|array $value
@ -73,7 +96,7 @@ class Project extends ProjectBase
$info = $this->data['access_information']; $info = $this->data['access_information'];
// Handle old-format (serialized) access information first: // Handle old-format (serialized) access information first:
if (!empty($info) && substr($info, 0, 1) != '{') { if (!empty($info) && !in_array(substr($info, 0, 1), array('{', '['))) {
$data = unserialize($info); $data = unserialize($info);
} else { } else {
$data = json_decode($info, true); $data = json_decode($info, true);

View file

@ -0,0 +1,18 @@
<?php
/**
* ProjectGroup model for table: project_group
*/
namespace PHPCI\Model;
use PHPCI\Model\Base\ProjectGroupBase;
/**
* ProjectGroup Model
* @uses PHPCI\Model\Base\ProjectGroupBase
*/
class ProjectGroup extends ProjectGroupBase
{
// This class has been left blank so that you can modify it - changes in this file will not be overwritten.
}

View file

@ -9,8 +9,6 @@
namespace PHPCI; namespace PHPCI;
use PHPCI\Model\Build;
/** /**
* PHPCI Plugin Interface - Used by all build plugins. * PHPCI Plugin Interface - Used by all build plugins.
* @author Dan Cryer <dan@block8.co.uk> * @author Dan Cryer <dan@block8.co.uk>

View file

@ -10,6 +10,7 @@
namespace PHPCI\Plugin; namespace PHPCI\Plugin;
use PHPCI\Builder; use PHPCI\Builder;
use PHPCI\Helper\Lang;
use PHPCI\Model\Build; use PHPCI\Model\Build;
/** /**

View file

@ -12,6 +12,7 @@ namespace PHPCI\Plugin;
use PHPCI\Builder; use PHPCI\Builder;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\Build; use PHPCI\Model\Build;
use PHPCI\Model\BuildError;
/** /**
* Behat BDD Plugin * Behat BDD Plugin
@ -66,12 +67,72 @@ class Behat implements \PHPCI\Plugin
if (!$behat) { if (!$behat) {
$this->phpci->logFailure(Lang::get('could_not_find', 'behat')); $this->phpci->logFailure(Lang::get('could_not_find', 'behat'));
return false; return false;
} }
$success = $this->phpci->executeCommand($behat . ' %s', $this->features); $success = $this->phpci->executeCommand($behat . ' %s', $this->features);
chdir($curdir); chdir($curdir);
list($errorCount, $data) = $this->parseBehatOutput();
$this->build->storeMeta('behat-warnings', $errorCount);
$this->build->storeMeta('behat-data', $data);
return $success; return $success;
} }
/**
* Parse the behat output and return details on failures
*
* @return array
*/
public function parseBehatOutput()
{
$output = $this->phpci->getLastOutput();
$parts = explode('---', $output);
if (count($parts) <= 1) {
return array(0, array());
}
$lines = explode(PHP_EOL, $parts[1]);
$storeFailures = false;
$data = array();
foreach ($lines as $line) {
$line = trim($line);
if ($line == 'Failed scenarios:') {
$storeFailures = true;
continue;
}
if (strpos($line, ':') === false) {
$storeFailures = false;
}
if ($storeFailures) {
$lineParts = explode(':', $line);
$data[] = array(
'file' => $lineParts[0],
'line' => $lineParts[1]
);
$this->build->reportError(
$this->phpci,
'behat',
'Behat scenario failed.',
BuildError::SEVERITY_HIGH,
$lineParts[0],
$lineParts[1]
);
}
}
$errorCount = count($data);
return array($errorCount, $data);
}
} }

View file

@ -38,19 +38,19 @@ class Campfire implements \PHPCI\Plugin
*/ */
public function __construct(Builder $phpci, Build $build, array $options = array()) public function __construct(Builder $phpci, Build $build, array $options = array())
{ {
$this->phpci = $phpci; $this->phpci = $phpci;
$this->build = $build; $this->build = $build;
$this->message = $options['message'];
$this->message = $options['message'];
$this->userAgent = "PHPCI/1.0 (+http://www.phptesting.org/)"; $this->userAgent = "PHPCI/1.0 (+http://www.phptesting.org/)";
$this->cookie = "phpcicookie"; $this->cookie = "phpcicookie";
$buildSettings = $phpci->getConfig('build_settings'); $buildSettings = $phpci->getConfig('build_settings');
if (isset($buildSettings['campfire'])) { if (isset($buildSettings['campfire'])) {
$campfire = $buildSettings['campfire']; $campfire = $buildSettings['campfire'];
$this->url = $campfire['url']; $this->url = $campfire['url'];
$this->authToken = $campfire['authToken']; $this->authToken = $campfire['authToken'];
$this->roomId = $campfire['roomId']; $this->roomId = $campfire['roomId'];
} else { } else {
throw new \Exception(Lang::get('no_campfire_settings')); throw new \Exception(Lang::get('no_campfire_settings'));
} }
@ -63,7 +63,7 @@ class Campfire implements \PHPCI\Plugin
*/ */
public function execute() public function execute()
{ {
$url = PHPCI_URL."build/view/".$this->build->getId(); $url = PHPCI_URL . "build/view/" . $this->build->getId();
$message = str_replace("%buildurl%", $url, $this->message); $message = str_replace("%buildurl%", $url, $this->message);
$this->joinRoom($this->roomId); $this->joinRoom($this->roomId);
$status = $this->speak($message, $this->roomId); $status = $this->speak($message, $this->roomId);
@ -101,6 +101,7 @@ class Campfire implements \PHPCI\Plugin
public function speak($message, $roomId, $isPaste = false) public function speak($message, $roomId, $isPaste = false)
{ {
$page = '/room/'.$roomId.'/speak.json'; $page = '/room/'.$roomId.'/speak.json';
if ($isPaste) { if ($isPaste) {
$type = 'PasteMessage'; $type = 'PasteMessage';
} else { } else {
@ -143,12 +144,13 @@ class Campfire implements \PHPCI\Plugin
// We tend to get one space with an otherwise blank response // We tend to get one space with an otherwise blank response
$output = trim($output); $output = trim($output);
if (strlen($output)) { if (strlen($output)) {
/* Responses are JSON. Decode it to a data structure */ /* Responses are JSON. Decode it to a data structure */
return json_decode($output); return json_decode($output);
} }
// Simple 200 OK response (such as for joining a room) // Simple 200 OK response (such as for joining a room)
// TODO: check for other result codes here
return true; return true;
} }
} }

View file

@ -39,9 +39,9 @@ class CleanBuild implements \PHPCI\Plugin
*/ */
public function __construct(Builder $phpci, Build $build, array $options = array()) public function __construct(Builder $phpci, Build $build, array $options = array())
{ {
$this->phpci = $phpci; $this->phpci = $phpci;
$this->build = $build; $this->build = $build;
$this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : array(); $this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : array();
} }
/** /**

View file

@ -12,31 +12,66 @@ namespace PHPCI\Plugin;
use PHPCI\Builder; use PHPCI\Builder;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\Build; use PHPCI\Model\Build;
use PHPCI\Plugin\Util\TestResultParsers\Codeception as Parser;
use Psr\Log\LogLevel;
/** /**
* Codeception Plugin - Enables full acceptance, unit, and functional testing. * Codeception Plugin - Enables full acceptance, unit, and functional testing.
* @author Don Gilbert <don@dongilbert.net> * @author Don Gilbert <don@dongilbert.net>
* @author Igor Timoshenko <contact@igortimoshenko.com>
* @author Adam Cooper <adam@networkpie.co.uk>
* @package PHPCI * @package PHPCI
* @subpackage Plugins * @subpackage Plugins
*/ */
class Codeception implements \PHPCI\Plugin class Codeception implements \PHPCI\Plugin, \PHPCI\ZeroConfigPlugin
{ {
/** /** @var string */
* @var string
*/
protected $args = ''; protected $args = '';
/** /** @var Builder */
* @var Builder
*/
protected $phpci; protected $phpci;
/** @var Build */
protected $build; protected $build;
/** /**
* @var string|string[] $xmlConfigFile The path (or array of paths) of an xml config for PHPUnit * @var string $ymlConfigFile The path of a yml config for Codeception
*/ */
protected $xmlConfigFile; protected $ymlConfigFile;
/**
* @var string $path The path to the codeception tests folder.
*/
protected $path;
/**
* @param $stage
* @param Builder $builder
* @param Build $build
* @return bool
*/
public static function canExecute($stage, Builder $builder, Build $build)
{
return $stage == 'test' && !is_null(self::findConfigFile($builder->buildPath));
}
/**
* Try and find the codeception YML config file.
* @param $buildPath
* @return null|string
*/
public static function findConfigFile($buildPath)
{
if (file_exists($buildPath . 'codeception.yml')) {
return 'codeception.yml';
}
if (file_exists($buildPath . 'codeception.dist.yml')) {
return 'codeception.dist.yml';
}
return null;
}
/** /**
* Set up the plugin, configure options, etc. * Set up the plugin, configure options, etc.
@ -48,71 +83,81 @@ class Codeception implements \PHPCI\Plugin
{ {
$this->phpci = $phpci; $this->phpci = $phpci;
$this->build = $build; $this->build = $build;
$this->path = 'tests' . DIRECTORY_SEPARATOR . '_output' . DIRECTORY_SEPARATOR;
if (isset($options['config'])) { if (empty($options['config'])) {
$this->xmlConfigFile = $options['config']; $this->ymlConfigFile = self::findConfigFile($this->phpci->buildPath);
} else {
$this->ymlConfigFile = $options['config'];
} }
if (isset($options['args'])) { if (isset($options['args'])) {
$this->args = (string) $options['args']; $this->args = (string) $options['args'];
} }
if (isset($options['path'])) {
$this->path = $options['path'];
}
} }
/** /**
* Runs Codeception tests, optionally using specified config file(s). * Runs Codeception tests
*/ */
public function execute() public function execute()
{ {
$success = true; if (empty($this->ymlConfigFile)) {
throw new \Exception("No configuration file found");
// Run any config files first. This can be either a single value or an array.
if ($this->xmlConfigFile !== null) {
$success &= $this->runConfigFile($this->xmlConfigFile);
} }
return $success; // Run any config files first. This can be either a single value or an array.
return $this->runConfigFile($this->ymlConfigFile);
} }
/** /**
* Run tests from a Codeception config file. * Run tests from a Codeception config file.
* @param $configPath * @param $configPath
* @return bool|mixed * @return bool|mixed
* @throws \Exception
*/ */
protected function runConfigFile($configPath) protected function runConfigFile($configPath)
{ {
if (is_array($configPath)) { $this->phpci->logExecOutput(false);
return $this->recurseArg($configPath, array($this, "runConfigFile"));
} else {
$codecept = $this->phpci->findBinary('codecept'); $codecept = $this->phpci->findBinary('codecept');
if (!$codecept) { if (!$codecept) {
$this->phpci->logFailure(Lang::get('could_not_find', 'codecept')); $this->phpci->logFailure(Lang::get('could_not_find', 'codecept'));
return false;
}
$cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" '. $this->args; return false;
if (IS_WIN) {
$cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" '. $this->args;
}
$configPath = $this->phpci->buildPath . $configPath;
$success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath, $configPath);
return $success;
} }
}
/** $cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args;
* @param $array
* @param $callable if (IS_WIN) {
* @return bool|mixed $cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args;
*/
protected function recurseArg($array, $callable)
{
$success = true;
foreach ($array as $subItem) {
$success &= call_user_func($callable, $subItem);
} }
$configPath = $this->phpci->buildPath . $configPath;
$success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath, $configPath);
$this->phpci->log(
'Codeception XML path: '. $this->phpci->buildPath . $this->path . 'report.xml',
Loglevel::DEBUG
);
$xml = file_get_contents($this->phpci->buildPath . $this->path . 'report.xml', false);
$parser = new Parser($this->phpci, $xml);
$output = $parser->parse();
$meta = array(
'tests' => $parser->getTotalTests(),
'timetaken' => $parser->getTotalTimeTaken(),
'failures' => $parser->getTotalFailures()
);
$this->build->storeMeta('codeception-meta', $meta);
$this->build->storeMeta('codeception-data', $output);
$this->build->storeMeta('codeception-errors', $parser->getTotalFailures());
$this->phpci->logExecOutput(true);
return $success; return $success;
} }
} }

View file

@ -27,6 +27,7 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
protected $preferDist; protected $preferDist;
protected $phpci; protected $phpci;
protected $build; protected $build;
protected $nodev;
/** /**
* Check if this plugin can be executed. * Check if this plugin can be executed.
@ -37,7 +38,7 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
*/ */
public static function canExecute($stage, Builder $builder, Build $build) public static function canExecute($stage, Builder $builder, Build $build)
{ {
$path = $builder->buildPath . '/composer.json'; $path = $builder->buildPath . DIRECTORY_SEPARATOR . 'composer.json';
if (file_exists($path) && $stage == 'setup') { if (file_exists($path) && $stage == 'setup') {
return true; return true;
@ -54,15 +55,17 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
*/ */
public function __construct(Builder $phpci, Build $build, array $options = array()) public function __construct(Builder $phpci, Build $build, array $options = array())
{ {
$path = $phpci->buildPath; $path = $phpci->buildPath;
$this->phpci = $phpci; $this->phpci = $phpci;
$this->build = $build; $this->build = $build;
$this->directory = $path; $this->directory = $path;
$this->action = 'install'; $this->action = 'install';
$this->preferDist = false; $this->preferDist = false;
$this->preferSource = false;
$this->nodev = false;
if (array_key_exists('directory', $options)) { if (array_key_exists('directory', $options)) {
$this->directory = $path . '/' . $options['directory']; $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory'];
} }
if (array_key_exists('action', $options)) { if (array_key_exists('action', $options)) {
@ -72,6 +75,15 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
if (array_key_exists('prefer_dist', $options)) { if (array_key_exists('prefer_dist', $options)) {
$this->preferDist = (bool)$options['prefer_dist']; $this->preferDist = (bool)$options['prefer_dist'];
} }
if (array_key_exists('prefer_source', $options)) {
$this->preferDist = false;
$this->preferSource = (bool)$options['prefer_source'];
}
if (array_key_exists('no_dev', $options)) {
$this->nodev = (bool)$options['no_dev'];
}
} }
/** /**
@ -81,11 +93,6 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
{ {
$composerLocation = $this->phpci->findBinary(array('composer', 'composer.phar')); $composerLocation = $this->phpci->findBinary(array('composer', 'composer.phar'));
if (!$composerLocation) {
$this->phpci->logFailure(Lang::get('could_not_find', 'composer'));
return false;
}
$cmd = ''; $cmd = '';
if (IS_WIN) { if (IS_WIN) {
@ -96,10 +103,17 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
if ($this->preferDist) { if ($this->preferDist) {
$this->phpci->log('Using --prefer-dist flag'); $this->phpci->log('Using --prefer-dist flag');
$cmd .= '--prefer-dist'; $cmd .= ' --prefer-dist';
} else { }
if ($this->preferSource) {
$this->phpci->log('Using --prefer-source flag'); $this->phpci->log('Using --prefer-source flag');
$cmd .= '--prefer-source'; $cmd .= ' --prefer-source';
}
if ($this->nodev) {
$this->phpci->log('Using --no-dev flag');
$cmd .= ' --no-dev';
} }
$cmd .= ' --working-dir="%s" %s'; $cmd .= ' --working-dir="%s" %s';

View file

@ -35,12 +35,12 @@ class CopyBuild implements \PHPCI\Plugin
*/ */
public function __construct(Builder $phpci, Build $build, array $options = array()) public function __construct(Builder $phpci, Build $build, array $options = array())
{ {
$path = $phpci->buildPath; $path = $phpci->buildPath;
$this->phpci = $phpci; $this->phpci = $phpci;
$this->build = $build; $this->build = $build;
$this->directory = isset($options['directory']) ? $options['directory'] : $path; $this->directory = isset($options['directory']) ? $options['directory'] : $path;
$this->wipe = isset($options['wipe']) ? (bool)$options['wipe'] : false; $this->wipe = isset($options['wipe']) ? (bool)$options['wipe'] : false;
$this->ignore = isset($options['respect_ignore']) ? (bool)$options['respect_ignore'] : false; $this->ignore = isset($options['respect_ignore']) ? (bool)$options['respect_ignore'] : false;
} }
/** /**
@ -74,7 +74,7 @@ class CopyBuild implements \PHPCI\Plugin
*/ */
protected function wipeExistingDirectory() protected function wipeExistingDirectory()
{ {
if ($this->wipe == true && $this->directory != '/' && is_dir($this->directory)) { if ($this->wipe === true && $this->directory != '/' && is_dir($this->directory)) {
$cmd = 'rm -Rf "%s*"'; $cmd = 'rm -Rf "%s*"';
$success = $this->phpci->executeCommand($cmd, $this->directory); $success = $this->phpci->executeCommand($cmd, $this->directory);

73
PHPCI/Plugin/Deployer.php Normal file
View file

@ -0,0 +1,73 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Plugin;
use b8\HttpClient;
use PHPCI\Builder;
use PHPCI\Model\Build;
/**
* Integrates PHPCI with Deployer: https://github.com/rebelinblue/deployer
* @author Dan Cryer <dan@block8.co.uk>
* @package PHPCI
* @subpackage Plugins
*/
class Deployer implements \PHPCI\Plugin
{
protected $webhookUrl;
protected $reason;
protected $updateOnly;
/**
* Set up the plugin, configure options, etc.
* @param Builder $phpci
* @param Build $build
* @param array $options
*/
public function __construct(Builder $phpci, Build $build, array $options = array())
{
$this->phpci = $phpci;
$this->build = $build;
$this->reason = 'PHPCI Build #%BUILD% - %COMMIT_MESSAGE%';
if (isset($options['webhook_url'])) {
$this->webhookUrl = $options['webhook_url'];
}
if (isset($options['reason'])) {
$this->reason = $options['reason'];
}
$this->updateOnly = isset($options['update_only']) ? (bool) $options['update_only'] : true;
}
/**
* Copies files from the root of the build directory into the target folder
*/
public function execute()
{
if (empty($this->webhookUrl)) {
$this->phpci->logFailure('You must specify a webhook URL.');
return false;
}
$http = new HttpClient();
$response = $http->post($this->webhookUrl, array(
'reason' => $this->phpci->interpolate($this->reason),
'source' => 'PHPCI',
'url' => $this->phpci->interpolate('%BUILD_URI%'),
'branch' => $this->phpci->interpolate('%BRANCH%'),
'update_only' => $this->updateOnly
));
return $response['success'];
}
}

View file

@ -9,10 +9,13 @@
namespace PHPCI\Plugin; namespace PHPCI\Plugin;
use Exception;
use b8\View; use b8\View;
use PHPCI\Builder; use PHPCI\Builder;
use PHPCI\Helper\Lang; use PHPCI\Helper\Lang;
use PHPCI\Model\Build; use PHPCI\Model\Build;
use PHPCI\Helper\Email as EmailHelper;
use Psr\Log\LogLevel;
/** /**
* Email Plugin - Provides simple email capability to PHPCI. * Email Plugin - Provides simple email capability to PHPCI.
@ -27,21 +30,16 @@ class Email implements \PHPCI\Plugin
*/ */
protected $phpci; protected $phpci;
/**
* @var \PHPCI\Model\Build
*/
protected $build;
/** /**
* @var array * @var array
*/ */
protected $options; protected $options;
/**
* @var \Swift_Mailer
*/
protected $mailer;
/**
* @var string
*/
protected $fromAddress;
/** /**
* Set up the plugin, configure options, etc. * Set up the plugin, configure options, etc.
* @param Builder $phpci * @param Builder $phpci
@ -52,25 +50,16 @@ class Email implements \PHPCI\Plugin
public function __construct( public function __construct(
Builder $phpci, Builder $phpci,
Build $build, Build $build,
\Swift_Mailer $mailer,
array $options = array() array $options = array()
) { ) {
$this->phpci = $phpci; $this->phpci = $phpci;
$this->build = $build; $this->build = $build;
$this->options = $options; $this->options = $options;
$phpCiSettings = $phpci->getSystemConfig('phpci');
$this->fromAddress = isset($phpCiSettings['email_settings']['from_address'])
? $phpCiSettings['email_settings']['from_address']
: "notifications-ci@phptesting.org";
$this->mailer = $mailer;
} }
/** /**
* Connects to MySQL and runs a specified set of queries. * Send a notification mail.
*/ */
public function execute() public function execute()
{ {
$addresses = $this->getEmailAddresses(); $addresses = $this->getEmailAddresses();
@ -81,79 +70,86 @@ class Email implements \PHPCI\Plugin
return false; return false;
} }
$subjectTemplate = "PHPCI - %s - %s"; $buildStatus = $this->build->isSuccessful() ? "Passing Build" : "Failing Build";
$projectName = $this->phpci->getBuildProjectTitle(); $projectName = $this->build->getProject()->getTitle();
$logText = $this->build->getLog();
if ($this->build->isSuccessful()) { try {
$sendFailures = $this->sendSeparateEmails( $view = $this->getMailTemplate();
$addresses, } catch (Exception $e) {
sprintf($subjectTemplate, $projectName, Lang::get('passing_build')), $this->phpci->log(
sprintf(Lang::get('log_output')."<br><pre>%s</pre>", $logText) sprintf('Unknown mail template "%s", falling back to default.', $this->options['template']),
); LogLevel::WARNING
} else {
$view = new View('Email/failed');
$view->build = $this->build;
$view->project = $this->build->getProject();
$emailHtml = $view->render();
$sendFailures = $this->sendSeparateEmails(
$addresses,
sprintf($subjectTemplate, $projectName, Lang::get('failing_build')),
$emailHtml
); );
$view = $this->getDefaultMailTemplate();
} }
$view->build = $this->build;
$view->project = $this->build->getProject();
$layout = new View('Email/layout');
$layout->build = $this->build;
$layout->project = $this->build->getProject();
$layout->content = $view->render();
$body = $layout->render();
$sendFailures = $this->sendSeparateEmails(
$addresses,
sprintf("PHPCI - %s - %s", $projectName, $buildStatus),
$body
);
// This is a success if we've not failed to send anything. // This is a success if we've not failed to send anything.
$this->phpci->log(sprintf("%d emails sent", (count($addresses) - $sendFailures)));
$this->phpci->log(sprintf("%d emails failed to send", $sendFailures));
$this->phpci->log(Lang::get('n_emails_sent', (count($addresses) - count($sendFailures)))); return ($sendFailures === 0);
$this->phpci->log(Lang::get('n_emails_failed', count($sendFailures)));
return (count($sendFailures) == 0);
} }
/** /**
* @param string[]|string $toAddresses Array or single address to send to * @param string $toAddress Single address to send to
* @param string[] $ccList * @param string[] $ccList
* @param string $subject Email subject * @param string $subject Email subject
* @param string $body Email body * @param string $body Email body
* @return array Array of failed addresses * @return array Array of failed addresses
*/ */
public function sendEmail($toAddresses, $ccList, $subject, $body) protected function sendEmail($toAddress, $ccList, $subject, $body)
{ {
$message = \Swift_Message::newInstance($subject) $email = new EmailHelper();
->setFrom($this->fromAddress)
->setTo($toAddresses) $email->setEmailTo($toAddress, $toAddress);
->setBody($body) $email->setSubject($subject);
->setContentType("text/html"); $email->setBody($body);
$email->setHtml(true);
if (is_array($ccList) && count($ccList)) { if (is_array($ccList) && count($ccList)) {
$message->setCc($ccList); foreach ($ccList as $address) {
$email->addCc($address, $address);
}
} }
$failedAddresses = array(); return $email->send();
$this->mailer->send($message, $failedAddresses);
return $failedAddresses;
} }
/** /**
* Send out build status emails. * Send an email to a list of specified subjects.
*
* @param array $toAddresses * @param array $toAddresses
* @param $subject * List of destination addresses for message.
* @param $body * @param string $subject
* @return array * Mail subject
* @param string $body
* Mail body
*
* @return int number of failed messages
*/ */
public function sendSeparateEmails(array $toAddresses, $subject, $body) public function sendSeparateEmails(array $toAddresses, $subject, $body)
{ {
$failures = array(); $failures = 0;
$ccList = $this->getCcAddresses(); $ccList = $this->getCcAddresses();
foreach ($toAddresses as $address) { foreach ($toAddresses as $address) {
$newFailures = $this->sendEmail($address, $ccList, $subject, $body); if (!$this->sendEmail($address, $ccList, $subject, $body)) {
foreach ($newFailures as $failure) { $failures++;
$failures[] = $failure;
} }
} }
return $failures; return $failures;
@ -178,15 +174,16 @@ class Email implements \PHPCI\Plugin
} }
} }
if (isset($this->options['default_mailto_address'])) { if (empty($addresses) && isset($this->options['default_mailto_address'])) {
$addresses[] = $this->options['default_mailto_address']; $addresses[] = $this->options['default_mailto_address'];
return $addresses;
} }
return $addresses;
return array_unique($addresses);
} }
/** /**
* Get the list of email addresses to CC. * Get the list of email addresses to CC.
*
* @return array * @return array
*/ */
protected function getCcAddresses() protected function getCcAddresses()
@ -201,4 +198,30 @@ class Email implements \PHPCI\Plugin
return $ccAddresses; return $ccAddresses;
} }
/**
* Get the mail template used to sent the mail.
*
* @return View
*/
protected function getMailTemplate()
{
if (isset($this->options['template'])) {
return new View('Email/' . $this->options['template']);
}
return $this->getDefaultMailTemplate();
}
/**
* Get the default mail template.
*
* @return View
*/
protected function getDefaultMailTemplate()
{
$template = $this->build->isSuccessful() ? 'short' : 'long';
return new View('Email/' . $template);
}
} }

View file

@ -0,0 +1,73 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Plugin;
use PHPCI\Builder;
use PHPCI\Model\Build;
use Mremi\Flowdock\Api\Push\Push;
use Mremi\Flowdock\Api\Push\TeamInboxMessage;
/**
* Flowdock Plugin
* @author Petr Cervenka <petr@nanosolutions.io>
* @package PHPCI
* @subpackage Plugins
*/
class FlowdockNotify implements \PHPCI\Plugin
{
private $api_key;
private $email;
const MESSAGE_DEFAULT = 'Build %BUILD% has finished for commit <a href="%COMMIT_URI%">%SHORT_COMMIT%</a>
(%COMMIT_EMAIL%)> on branch <a href="%BRANCH_URI%">%BRANCH%</a>';
/**
* Set up the plugin, configure options, etc.
* @param Builder $phpci
* @param Build $build
* @param array $options
* @throws \Exception
*/
public function __construct(Builder $phpci, Build $build, array $options = array())
{
$this->phpci = $phpci;
$this->build = $build;
if (!is_array($options) || !isset($options['api_key'])) {
throw new \Exception('Please define the api_key for Flowdock Notify plugin!');
}
$this->api_key = trim($options['api_key']);
$this->message = isset($options['message']) ? $options['message'] : self::MESSAGE_DEFAULT;
$this->email = isset($options['email']) ? $options['email'] : 'PHPCI';
}
/**
* Run the Flowdock plugin.
* @return bool
* @throws \Exception
*/
public function execute()
{
$message = $this->phpci->interpolate($this->message);
$successfulBuild = $this->build->isSuccessful() ? 'Success' : 'Failed';
$push = new Push($this->api_key);
$flowMessage = TeamInboxMessage::create()
->setSource("PHPCI")
->setFromAddress($this->email)
->setFromName($this->build->getProject()->getTitle())
->setSubject($successfulBuild)
->setTags(['#ci'])
->setLink($this->build->getBranchLink())
->setContent($message);
if (!$push->sendTeamInboxMessage($flowMessage, array('connect_timeout' => 5000, 'timeout' => 5000))) {
throw new \Exception(sprintf('Flowdock Failed: %s', $flowMessage->getResponseErrors()));
}
return true;
}
}

View file

@ -52,7 +52,7 @@ class Grunt implements \PHPCI\Plugin
// Handle options: // Handle options:
if (isset($options['directory'])) { if (isset($options['directory'])) {
$this->directory = $path . '/' . $options['directory']; $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory'];
} }
if (isset($options['task'])) { if (isset($options['task'])) {

View file

@ -52,7 +52,7 @@ class Gulp implements \PHPCI\Plugin
// Handle options: // Handle options:
if (isset($options['directory'])) { if (isset($options['directory'])) {
$this->directory = $path . '/' . $options['directory']; $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory'];
} }
if (isset($options['task'])) { if (isset($options['task'])) {

View file

@ -21,11 +21,9 @@ use PHPCI\Model\Build;
*/ */
class HipchatNotify implements \PHPCI\Plugin class HipchatNotify implements \PHPCI\Plugin
{ {
private $authToken; protected $authToken;
private $userAgent; protected $color;
private $cookie; protected $notify;
private $color;
private $notify;
/** /**
* Set up the plugin, configure options, etc. * Set up the plugin, configure options, etc.

View file

@ -77,19 +77,56 @@ class Irc implements \PHPCI\Plugin
} }
$sock = fsockopen($this->server, $this->port); $sock = fsockopen($this->server, $this->port);
fputs($sock, 'USER ' . $this->nick . ' phptesting.org ' . $this->nick . ' :' . $this->nick . "\r\n"); stream_set_timeout($sock, 1);
fputs($sock, 'NICK ' . $this->nick . "\r\n");
fputs($sock, 'JOIN ' . $this->room . "\r\n");
fputs($sock, 'PRIVMSG ' . $this->room . ' :' . $msg . "\r\n");
while (fgets($sock)) { $connectCommands = array(
// We don't need to do anything, 'USER ' . $this->nick . ' 0 * :' . $this->nick,
// but the IRC server doesn't appear to post the message 'NICK ' . $this->nick,
// unless we wait for responses. );
} $this->executeIrcCommands($sock, $connectCommands);
$this->executeIrcCommand($sock, 'JOIN ' . $this->room);
$this->executeIrcCommand($sock, 'PRIVMSG ' . $this->room . ' :' . $msg);
fclose($sock); fclose($sock);
return true; return true;
} }
/**
* @param resource $socket
* @param array $commands
* @return bool
*/
private function executeIrcCommands($socket, array $commands)
{
foreach ($commands as $command) {
fputs($socket, $command . "\n");
}
$pingBack = false;
// almost all servers expect pingback!
while ($response = fgets($socket)) {
$matches = array();
if (preg_match('/^PING \\:([A-Z0-9]+)/', $response, $matches)) {
$pingBack = $matches[1];
}
}
if ($pingBack) {
$command = 'PONG :' . $pingBack . "\n";
fputs($socket, $command);
}
}
/**
*
* @param resource $socket
* @param string $command
* @return bool
*/
private function executeIrcCommand($socket, $command)
{
return $this->executeIrcCommands($socket, array($command));
}
} }

Some files were not shown because too many files have changed in this diff Show more